Skip to content

Commit

Permalink
Preserve query parameter for switching between AAS Viewer/Editor and …
Browse files Browse the repository at this point in the history
…Submodel Viewer (#199)

* Preserve query parameter for switching between AAS Viewer/Editor, Submodel Viewer, About page, ...
Fixes #166

* Using anchor tag `to` for declarative navigation

* Undo changes in AppNavigation/MainMenu

* Adds state for url query

* Adds route guard to preserver query parameter

* Implements fetchSme() and fetchAndDispatchSme() in SMRepositoryClient

* Usage of fetchAndDispathSme() in App.vue

* Usage of fetchAndDispatchSme() in route guard

* Strip idShortPath of path URL query in case of switching to SubmodelViewer

* Updates router guard
  • Loading branch information
seicke authored Jan 9, 2025
1 parent 71ef6bb commit dc8ad40
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 51 deletions.
40 changes: 3 additions & 37 deletions aas-web-ui/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,12 @@
import { RouteRecordNameGeneric, useRoute, useRouter } from 'vue-router';
import { useDisplay } from 'vuetify';
import { useAASRepositoryClient } from '@/composables/Client/AASRepositoryClient';
import { useRequestHandling } from '@/composables/RequestHandling';
import { useAASStore } from '@/store/AASDataStore';
import { useEnvStore } from '@/store/EnvironmentStore';
import { useNavigationStore } from '@/store/NavigationStore';
import { formatDate } from '@/utils/DateUtils';
import { useSMRepositoryClient } from './composables/Client/SMRepositoryClient';
// Stores
const navigationStore = useNavigationStore();
const aasStore = useAASStore();
const envStore = useEnvStore();
// Vue Router
Expand All @@ -35,7 +32,7 @@
// Composables
const { fetchAndDispatchAas } = useAASRepositoryClient();
const { getRequest } = useRequestHandling();
const { fetchAndDispatchSme } = useSMRepositoryClient();
// Vuetify
const { mobile } = useDisplay();
Expand Down Expand Up @@ -100,7 +97,7 @@
}
if (aasEndpoint && submodelElementPath) {
handleSubmodelElement(submodelElementPath);
await fetchAndDispatchSme(submodelElementPath);
}
});
Expand Down Expand Up @@ -148,35 +145,4 @@
router.push({ name: 'AASViewer', query });
}
}
// Handle the selected submodel element
async function handleSubmodelElement(submodelElementPath: string) {
const path = submodelElementPath;
const context = 'retrieving SubmodelElement';
const disableMessage = true;
const response = await getRequest(path, context, disableMessage);
if (response.success) {
const data = response.data;
data.timestamp = formatDate(new Date());
data.path = submodelElementPath;
data.isActive = true;
aasStore.dispatchNode(data);
} else {
handleRequestFailure(response);
}
}
// Handle request failure and show appropriate message
function handleRequestFailure(response: any) {
if (Object.keys(response.data).length === 0) {
navigationStore.dispatchSnackbar({
status: true,
timeout: 60000,
color: 'error',
btnColor: 'buttonText',
text: 'No valid SubmodelElement under the given Path',
});
aasStore.dispatchNode({});
}
}
</script>
49 changes: 49 additions & 0 deletions aas-web-ui/src/composables/Client/SMRepositoryClient.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { computed } from 'vue';
import { useSMRegistryClient } from '@/composables/Client/SMRegistryClient';
import { useRequestHandling } from '@/composables/RequestHandling';
import { useAASStore } from '@/store/AASDataStore';
import { useNavigationStore } from '@/store/NavigationStore';
import { formatDate } from '@/utils/DateUtils';
import { extractEndpointHref } from '@/utils/DescriptorUtils';

export function useSMRepositoryClient() {
const { getRequest } = useRequestHandling();
const { fetchSmDescriptorById } = useSMRegistryClient();

const navigationStore = useNavigationStore();
const aasStore = useAASStore();

const submodelRepoUrl = computed(() => navigationStore.getSubmodelRepoURL);

Expand Down Expand Up @@ -82,6 +85,50 @@ export function useSMRepositoryClient() {
return failResponse;
}

// Fetch SME from (SM Repo) Endpoint
async function fetchSme(submodelElementPath: string): Promise<any> {
// console.log('fetchSme()', submodelElementPath);
const failResponse = {} as any;

if (submodelElementPath.trim() === '') return failResponse;

const smRepoPath = submodelElementPath;
const smRepoContext = 'retrieving SubmodelElement';
const disableMessage = true;
try {
const smRepoResponse = await getRequest(smRepoPath, smRepoContext, disableMessage);
if (smRepoResponse?.success && smRepoResponse?.data && Object.keys(smRepoResponse?.data).length > 0) {
const sme = smRepoResponse.data;

return sme;
}
} catch {
navigationStore.dispatchSnackbar({
status: true,
timeout: 60000,
color: 'error',
btnColor: 'buttonText',
text: 'No valid SubmodelElement under the given Path',
});
return failResponse;
}

return failResponse;
}

// Fetch and Dispatch AAS from (AAS Repo) Endpoint
async function fetchAndDispatchSme(submodelElementPath: string) {
// console.log('fetchAndDispatchSme()', submodelElementPath);
if (submodelElementPath.trim() === '') return;

const sme = await fetchSme(submodelElementPath);
sme.timestamp = formatDate(new Date());
sme.path = submodelElementPath;
sme.isActive = true;

aasStore.dispatchNode(sme);
}

function smNotFound(response: any, submodelId: string, path: string, text: string): any {
// Check if response contains a "messages" array with a "403" or "401" code
const messages = response.data?.messages || [];
Expand Down Expand Up @@ -132,6 +179,8 @@ export function useSMRepositoryClient() {
fetchSmList,
fetchSmById,
fetchSm,
fetchSme,
fetchAndDispatchSme,
smNotFound,
};
}
5 changes: 3 additions & 2 deletions aas-web-ui/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ const app = createApp(App);
const pinia = createPinia();

async function loadPlugins() {
const router = await createAppRouter();
app.use(pinia);

const router = await createAppRouter();
app.use(router);
app.use(pinia);

app.use(VueApexCharts);

const envStore = useEnvStore(); // Get the store instance
Expand Down
5 changes: 0 additions & 5 deletions aas-web-ui/src/pages/AASEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,9 @@
<script lang="ts" setup>
import { computed, onBeforeUnmount, onMounted } from 'vue';
import { useTheme } from 'vuetify';
import { useAASStore } from '@/store/AASDataStore';
const theme = useTheme();
const aasStore = useAASStore();
const primaryColor = computed(() => theme.current.value.colors.primary);
onMounted(() => {
Expand All @@ -60,8 +57,6 @@
document.removeEventListener('mousemove', function () {});
document.removeEventListener('mouseup', function () {});
}
// clear the current AAS
aasStore.dispatchSelectedAAS({});
});
// creates a div element (Resize Bar) on each Divider between Windows to allow the user to resize the windows
Expand Down
5 changes: 0 additions & 5 deletions aas-web-ui/src/pages/AASViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,9 @@
<script lang="ts" setup>
import { computed, onBeforeUnmount, onMounted } from 'vue';
import { useTheme } from 'vuetify';
import { useAASStore } from '@/store/AASDataStore';
const theme = useTheme();
const aasStore = useAASStore();
const primaryColor = computed(() => theme.current.value.colors.primary);
onMounted(() => {
Expand All @@ -60,8 +57,6 @@
document.removeEventListener('mousemove', function () {});
document.removeEventListener('mouseup', function () {});
}
// clear the current AAS
aasStore.dispatchSelectedAAS({});
});
// creates a div element (Resize Bar) on each Divider between Windows to allow the user to resize the windows
Expand Down
71 changes: 69 additions & 2 deletions aas-web-ui/src/router.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createRouter, createWebHistory } from 'vue-router';
import { createRouter, createWebHistory, Router } from 'vue-router';
import AASList from '@/components/AppNavigation/AASList.vue';
import ComponentVisualization from '@/components/ComponentVisualization.vue';
import SubmodelList from '@/components/SubmodelList.vue';
Expand All @@ -9,6 +9,9 @@ import Dashboard from '@/pages/Dashboard.vue';
import DashboardGroup from '@/pages/DashboardGroup.vue';
import Page404 from '@/pages/Page404.vue';
import SubmodelViewer from '@/pages/SubmodelViewer.vue';
import { useNavigationStore } from '@/store/NavigationStore';
import { useAASRepositoryClient } from './composables/Client/AASRepositoryClient';
import { useSMRepositoryClient } from './composables/Client/SMRepositoryClient';

const routes = [
{ path: '/', name: 'AASViewer', component: AASViewer },
Expand All @@ -24,13 +27,77 @@ const routes = [
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: Page404 },
];

export async function createAppRouter() {
const routeNamesToSaveAndLoadUrlQuery = ['AASList', 'AASEditor', 'AASViewer', 'SubmodelViewer'];

export async function createAppRouter(): Promise<Router> {
const base = import.meta.env.BASE_URL;

// Stores
const navigationStore = useNavigationStore();

// Composables
const { fetchAndDispatchAas } = useAASRepositoryClient();
const { fetchAndDispatchSme } = useSMRepositoryClient();

const router = createRouter({
history: createWebHistory(base),
routes,
});

router.beforeEach(async (to, from, next) => {
// Same route
if (from.name && from.name === to.name) {
// But changed URL query
if (from.query !== to.query) {
// Just for routes to save/load Query
if (routeNamesToSaveAndLoadUrlQuery.includes(from.name as string))
// Save URL query
navigationStore.dispatchUrlQuery(to.query);
// NOTE Fetching and dispatching of AAS/SM/SME (within same route) with respect to URL query parameter is
// done in other components, like AASList, AASTreeview, VTreeView, SubmodelElementView up till now
// TODO Fetching and dispatching of AAS/SM/SME at this central point (instead of in other components)
}
}

// Switch from one route to another
if (from.name && from.name !== to.name) {
// Just for switching from a route to Save/Load Query
if (routeNamesToSaveAndLoadUrlQuery.includes(from.name as string)) {
// Save URL query
if (Object.keys(from.query).length > 0) {
const queryToDispatch = from.query;

// Strip idShortPath in case of switching to Submodel Viewer
const queryPathToDispatch = queryToDispatch?.path as string;
if (to.name === 'SubmodelViewer' && queryPathToDispatch && queryPathToDispatch.trim() !== '') {
queryToDispatch.path = queryPathToDispatch.trim().split('/submodel-elements/')[0];
}

navigationStore.dispatchUrlQuery(queryToDispatch);
}
}

// Just for switching to a route to save/load Query
if (routeNamesToSaveAndLoadUrlQuery.includes(to.name as string)) {
// Load URL query
if (!to.query || Object.keys(to.query).length === 0) {
const queryLoaded = navigationStore.getUrlQuery;
const updatedRoute = Object.assign({}, to, {
query: queryLoaded,
});

if (queryLoaded && Object.keys(queryLoaded).length > 0 && updatedRoute !== to) {
next(updatedRoute);
// Dispatch AAS/SM/SME with respect to URL query parameter
if (queryLoaded.aas) await fetchAndDispatchAas(queryLoaded.aas as string);
if (queryLoaded.path) await fetchAndDispatchSme(queryLoaded.path as string);
return;
}
}
}
}
next();
});

return router;
}
6 changes: 6 additions & 0 deletions aas-web-ui/src/store/NavigationStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface PluginType {
}

import { defineStore } from 'pinia';
import { LocationQuery } from 'vue-router';
import { useAASStore } from './AASDataStore';

export const useNavigationStore = defineStore({
Expand All @@ -56,6 +57,7 @@ export const useNavigationStore = defineStore({
plugins: [] as PluginType[],
triggerAASListReload: false as boolean,
triggerAASListScroll: false as boolean,
urlQuery: {} as LocationQuery,
}),

getters: {
Expand All @@ -74,6 +76,7 @@ export const useNavigationStore = defineStore({
getPlugins: (state) => state.plugins,
getTriggerAASListReload: (state) => state.triggerAASListReload,
getTriggerAASListScroll: (state) => state.triggerAASListScroll,
getUrlQuery: (state) => state.urlQuery,
},

actions: {
Expand Down Expand Up @@ -129,5 +132,8 @@ export const useNavigationStore = defineStore({
dispatchTriggerAASListScroll() {
this.triggerAASListScroll = !this.triggerAASListScroll;
},
dispatchUrlQuery(query: LocationQuery) {
this.urlQuery = query;
},
},
});

0 comments on commit dc8ad40

Please sign in to comment.