Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates app.vue and mixins to Vue 3 Composition API #153

Merged
merged 6 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
265 changes: 127 additions & 138 deletions aas-web-ui/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,161 +13,150 @@
</v-app>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { useRoute, useRouter } from 'vue-router';
<script lang="ts" setup>
import { onMounted } from 'vue';
import { RouteRecordNameGeneric, useRoute, useRouter } from 'vue-router';
import { useDisplay } from 'vuetify';
import AppNavigation from '@/components/AppNavigation/AppNavigation.vue';
import RequestHandling from '@/mixins/RequestHandling';
import SubmodelElementHandling from '@/mixins/SubmodelElementHandling';
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';

export default defineComponent({
name: 'App',
components: {
RequestHandling, // Mixin to handle the requests to the AAS
SubmodelElementHandling, // Mixin to handle the SubmodelElements
// Stores
const navigationStore = useNavigationStore();
const aasStore = useAASStore();
const envStore = useEnvStore();

AppNavigation,
},
mixins: [RequestHandling, SubmodelElementHandling],
// Vue Router
const route = useRoute();
const router = useRouter();

setup() {
const navigationStore = useNavigationStore();
const aasStore = useAASStore();
const route = useRoute();
const router = useRouter();
const envStore = useEnvStore();
// composables
const { fetchAndDispatchAas } = useAASRepositoryClient();
const { getRequest } = useRequestHandling();

return {
navigationStore, // NavigationStore Object
aasStore, // AASStore Object
route, // Route Object
router, // Router Object
envStore, // EnvironmentStore Object
};
},
// Vuetify
const { mobile } = useDisplay();
const { platform } = useDisplay();

async mounted() {
let mobile = this.$vuetify.display.mobile;
onMounted(async () => {
// Check if the platform is a mobile device
let showMobileVersion = false;
if (
mobile.value ||
// include IPad as mobile device
if (this.$vuetify.display.platform.mac && this.$vuetify.display.platform.touch) {
mobile = true;
}
if (this.$vuetify.display.platform.ios) {
mobile = true;
}
if (this.$vuetify.display.platform.android) {
mobile = true;
}
// console.log('Mobile: ', mobile);
this.navigationStore.dispatchIsMobile(mobile);
this.navigationStore.dispatchPlatform(this.$vuetify.display.platform);
(platform.value.mac && platform.value.touch) ||
// IOS and Android are mobile platforms
platform.value.ios ||
platform.value.android
) {
showMobileVersion = true;
}

// check if the aas and path Queries are set in the URL and include them in the URL when switching to the mobile view
const searchParams = new URL(window.location.href).searchParams;
const aasEndpoint = searchParams.get('aas');
const submodelElementPath = searchParams.get('path');
// Dispatch the mobile status to the store
navigationStore.dispatchIsMobile(showMobileVersion);
navigationStore.dispatchPlatform(platform.value);

// Ensure available aasEndpoint query parameter
if (
this.envStore.singleAas &&
(aasEndpoint === null || aasEndpoint === undefined || aasEndpoint.trim() === '')
) {
if (this.envStore.getSingleAasRedirect) {
window.location.replace(this.envStore.getSingleAasRedirect);
return;
} else if (this.route.name !== '404NotFound404') {
this.router.push({ name: 'NotFound404' });
return;
}
}
// Extract the aas and path Queries from the URL
const searchParams = new URL(window.location.href).searchParams;
const aasEndpoint = searchParams.get('aas');
const submodelElementPath = searchParams.get('path');

// check which platform is used and change the fitting view
if (mobile) {
if (this.route.name === 'MainWindow') {
if (aasEndpoint && submodelElementPath) {
this.router.push({ name: 'AASList', query: { aas: aasEndpoint, path: submodelElementPath } });
} else if (aasEndpoint && !submodelElementPath) {
this.router.push({ name: 'AASList', query: { aas: aasEndpoint } });
} else {
this.router.push({ name: 'AASList' });
}
} else if (this.route.name === 'ComponentVisualization') {
if (!aasEndpoint && !submodelElementPath) {
this.router.push({ name: 'AASList' });
} else if (aasEndpoint && !submodelElementPath) {
this.router.push({ name: 'SubmodelList', query: { aas: aasEndpoint } });
}
}
} else {
// change to MainWindow when the platform is not android or ios
if (this.route.name === 'AASList' || this.route.name === 'SubmodelList') {
if (aasEndpoint && submodelElementPath)
this.router.push({
name: 'MainWindow',
query: { aas: aasEndpoint, path: submodelElementPath },
});
else if (aasEndpoint && !submodelElementPath)
this.router.push({ name: 'MainWindow', query: { aas: aasEndpoint } });
else this.router.push({ name: 'MainWindow' });
} else if (this.route.name === 'ComponentVisualization') {
if (aasEndpoint && !submodelElementPath)
this.router.push({ name: 'MainWindow', query: { aas: aasEndpoint } });
else if (!aasEndpoint && !submodelElementPath) this.router.push({ name: 'MainWindow' });
} else {
if (aasEndpoint && submodelElementPath)
this.router.push({ query: { aas: aasEndpoint, path: submodelElementPath } });
else if (aasEndpoint && !submodelElementPath) this.router.push({ query: { aas: aasEndpoint } });
}
// Check if single AAS mode is on and no aas query is set to either redirect or show 404
if (envStore.singleAas && (aasEndpoint === null || aasEndpoint === undefined || aasEndpoint.trim() === '')) {
if (envStore.getSingleAasRedirect) {
window.location.replace(envStore.getSingleAasRedirect);
return;
} else if (route.name !== '404NotFound404') {
router.push({ name: 'NotFound404' });
return;
}
}

if (aasEndpoint) {
// console.log('AAS Query is set: ', aasEndpoint);
await this.fetchAndDispatchAas(aasEndpoint);
}
// Check which platform is used and change the fitting view
if (showMobileVersion) {
handleMobileView(aasEndpoint, submodelElementPath);
} else {
handleDesktopView(aasEndpoint, submodelElementPath);
}

if (aasEndpoint && submodelElementPath) {
// console.log('AAS and Path Queries are set: ', submodelElementPath);
// Request the selected SubmodelElement
let path = submodelElementPath;
let context = 'retrieving SubmodelElement';
let disableMessage = true;
this.getRequest(path, context, disableMessage).then((response: any) => {
if (response.success) {
// execute if the Request was successful
response.data.timestamp = this.formatDate(new Date()); // add timestamp to the SubmodelElement Data
response.data.path = submodelElementPath; // add the path to the SubmodelElement Data
response.data.isActive = true; // add the isActive Property to the SubmodelElement Data
// console.log('SubmodelElement Data: ', response.data)
// dispatch the SubmodelElementPath set by the URL to the store
this.aasStore.dispatchNode(response.data); // set the updatedNode in the AASStore
} else {
// execute if the Request failed
if (response?.data && Object.keys(response?.data).length === 0) {
// don't copy the static SubmodelElement Data if no Node is selected or Node is invalid
this.navigationStore.dispatchSnackbar({
status: true,
timeout: 60000,
color: 'error',
btnColor: 'buttonText',
text: 'No valid SubmodelElement under the given Path',
}); // Show Error Snackbar
return;
}
this.aasStore.dispatchNode({});
}
});
}
},
if (aasEndpoint) {
await fetchAndDispatchAas(aasEndpoint);
}

if (aasEndpoint && submodelElementPath) {
handleSubmodelElement(submodelElementPath);
}
});
</script>

<style>
@import '../node_modules/@fontsource/roboto/index.css';
// Handle mobile view routing logic
function handleMobileView(aasEndpoint: string | null, submodelElementPath: string | null) {
const currentRouteName = route.name;
const routesToAASList: Array<RouteRecordNameGeneric> = ['MainWindow', 'AASViewer', 'AASList'];
if (currentRouteName && routesToAASList.includes(currentRouteName)) {
// Redirect to 'AASList' with existing query parameters
router.push({ name: 'AASList', query: route.query });
} else if (currentRouteName === 'SubmodelList' && aasEndpoint) {
// Redirect to 'SubmodelList' with 'aas' parameter
router.push({ name: 'SubmodelList', query: { aas: aasEndpoint } });
} else if (currentRouteName === 'ComponentVisualization' && aasEndpoint && submodelElementPath) {
// Redirect to 'ComponentVisualization' with 'aas' and 'path' parameters
router.push({ name: 'ComponentVisualization', query: { aas: aasEndpoint, path: submodelElementPath } });
} else {
// Redirect to 'AASList' without query parameters
router.push({ name: 'AASList' });
}
}

// Handle desktop view routing logic
function handleDesktopView(aasEndpoint: string | null, submodelElementPath: string | null) {
const currentRouteName = route.name;
const routesToMainWindow: Array<RouteRecordNameGeneric> = ['AASList', 'SubmodelList', 'ComponentVisualization'];
const query: any = {};
if (aasEndpoint) query.aas = aasEndpoint;
if (submodelElementPath) query.path = submodelElementPath;
if (currentRouteName && routesToMainWindow.includes(currentRouteName)) {
// Redirect to 'MainWindow' with appropriate query parameters
router.push({ name: 'MainWindow', query });
} else if (currentRouteName === 'AASViewer') {
// Stay on 'AASViewer' but update query parameters
router.push({ name: 'AASViewer', query });
} else {
// Default to 'MainWindow' with query parameters
router.push({ name: 'MainWindow', query });
}
}

html {
overflow-y: auto;
// 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({});
}
}
</style>
</script>
41 changes: 41 additions & 0 deletions aas-web-ui/src/composables/ChartHandling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useConceptDescriptionHandling } from '@/composables/ConceptDescriptionHandling';

export function useChartHandling() {
const { unitSuffix } = useConceptDescriptionHandling();

function prepareYValueTooltip(chartData: any, yVariables: any) {
return chartData.map((_series: any, index: number) => {
// Use optional chaining and nullish coalescing to simplify the retrieval of the unit
let unit = '';
if (yVariables[index]) {
unit = unitSuffix(yVariables[index]);
}
return {
formatter: (value: any) => `${value} ${unit}`,
};
});
}

function prepareLegend(yVariables: any) {
return {
formatter: (seriesName: any, opts: any) => {
let unit = '';
const index = opts.seriesIndex;

// check if the yVariable exists
if (yVariables.length > index) {
// check if the yVariable has an unit (embeddedDataSpecification) -> take the first one (TODO: make this more generic in the future)
if (yVariables[index]) {
unit = '[' + unitSuffix(yVariables[index]) + ']';
}
}
return seriesName + ' ' + unit;
},
};
}

return {
prepareYValueTooltip,
prepareLegend,
};
}
Loading
Loading