From 118b694b15f2547c8237bb99ad72ebe2ea6f2818 Mon Sep 17 00:00:00 2001 From: Sebastian Eicke <sebastian.eicke@HARTING.com> Date: Mon, 9 Dec 2024 14:02:29 +0100 Subject: [PATCH] Add administrative information component (#111) * Add administrative information component * adds missing check for keys * Adds missing checks (optional chaining) * Harmonize v-list-items * Add expansion panel * Fix margin --------- Co-authored-by: Aaron Zielstorff <aaron.zi@web.de> --- .../AppNavigation/AASListDetails.vue | 20 +- .../src/components/SubmodelElementView.vue | 18 +- .../AdministrativeInformationElement.vue | 248 ++++++++++++++++++ 3 files changed, 282 insertions(+), 4 deletions(-) create mode 100644 aas-web-ui/src/components/UIComponents/AdministrativeInformationElement.vue diff --git a/aas-web-ui/src/components/AppNavigation/AASListDetails.vue b/aas-web-ui/src/components/AppNavigation/AASListDetails.vue index ffd0a8e..0ca4f5a 100644 --- a/aas-web-ui/src/components/AppNavigation/AASListDetails.vue +++ b/aas-web-ui/src/components/AppNavigation/AASListDetails.vue @@ -40,7 +40,7 @@ <AssetInformation v-if="assetInformation && Object.keys(assetInformation).length > 0" :asset-object="assetInformation"></AssetInformation> - <v-divider v-if="assetInformation"></v-divider> + <v-divider v-if="assetInformation" thickness="2"></v-divider> <!-- AAS Details --> <v-list v-if="detailsObject" lines="one" nav class="bg-detailsCard"> <!-- AAS Identification --> @@ -50,10 +50,22 @@ :model-type="'AAS'" :id-type="'Identification (ID)'" :name-type="'idShort'"></IdentificationElement> + <!-- AAS Administrative Information--> <v-divider - v-if="detailsObject.displayName && detailsObject.displayName.length > 0" + v-if=" + detailsObject.administration && + (detailsObject.administration.revision != '' || + detailsObject.administration.version != '') + " class="mt-2"></v-divider> - <!-- SubmodelELement DisplayName --> + <AdministrativeInformationElement + v-if="detailsObject.administration" + :administrative-information-object="detailsObject.administration" + :administrative-information-title="'Administrative Information'" + :small="false" + :background-color="'detailsCard'"></AdministrativeInformationElement> + <v-divider v-if="detailsObject.displayName && detailsObject.displayName.length > 0"></v-divider> + <!-- AAS DisplayName --> <DisplayNameElement v-if="detailsObject.displayName && detailsObject.displayName.length > 0" :display-name-object="detailsObject.displayName" @@ -77,6 +89,7 @@ <script lang="ts"> import { defineComponent } from 'vue'; + import AdministrativeInformationElement from '@/components/UIComponents/AdministrativeInformationElement.vue'; import AssetInformation from '@/components/UIComponents/AssetInformation.vue'; import DescriptionElement from '@/components/UIComponents/DescriptionElement.vue'; import DisplayNameElement from '@/components/UIComponents/DisplayNameElement.vue'; @@ -89,6 +102,7 @@ name: 'AASListDetails', components: { IdentificationElement, + AdministrativeInformationElement, DisplayNameElement, DescriptionElement, AssetInformation, diff --git a/aas-web-ui/src/components/SubmodelElementView.vue b/aas-web-ui/src/components/SubmodelElementView.vue index 028fc93..f497ccf 100644 --- a/aas-web-ui/src/components/SubmodelElementView.vue +++ b/aas-web-ui/src/components/SubmodelElementView.vue @@ -13,9 +13,23 @@ :model-type="submodelElementData.modelType" :id-type="'Identification (ID)'" :name-type="'idShort'"></IdentificationElement> + <!-- Submodel Administrative Information--> <v-divider - v-if="submodelElementData.displayName && submodelElementData.displayName.length > 0" + v-if=" + submodelElementData.administration && + (submodelElementData.administration.revision != '' || + submodelElementData.administration.version != '') + " class="mt-2"></v-divider> + <AdministrativeInformationElement + v-if="submodelElementData.administration" + :administrative-information-object="submodelElementData.administration" + :administrative-information-title="'Administrative Information'" + :small="false"></AdministrativeInformationElement> + <v-divider + v-if=" + submodelElementData.displayName && submodelElementData.displayName.length > 0 + "></v-divider> <!-- SubmodelELement DisplayName --> <DisplayNameElement v-if="submodelElementData.displayName && submodelElementData.displayName.length > 0" @@ -154,6 +168,7 @@ import Submodel from '@/components/SubmodelElements/Submodel.vue'; import SubmodelElementCollection from '@/components/SubmodelElements/SubmodelElementCollection.vue'; import SubmodelElementList from '@/components/SubmodelElements/SubmodelElementList.vue'; + import AdministrativeInformationElement from '@/components/UIComponents/AdministrativeInformationElement.vue'; import ConceptDescription from '@/components/UIComponents/ConceptDescription.vue'; import DescriptionElement from '@/components/UIComponents/DescriptionElement.vue'; import DisplayNameElement from '@/components/UIComponents/DisplayNameElement.vue'; @@ -169,6 +184,7 @@ name: 'SubmodelElementView', components: { IdentificationElement, + AdministrativeInformationElement, DisplayNameElement, DescriptionElement, SemanticID, diff --git a/aas-web-ui/src/components/UIComponents/AdministrativeInformationElement.vue b/aas-web-ui/src/components/UIComponents/AdministrativeInformationElement.vue new file mode 100644 index 0000000..1eead98 --- /dev/null +++ b/aas-web-ui/src/components/UIComponents/AdministrativeInformationElement.vue @@ -0,0 +1,248 @@ +<template> + <v-container fluid class="pa-0"> + <v-expansion-panels> + <v-expansion-panel elevation="0" tile static :color="backgroundColor"> + <v-expansion-panel-title class="px-2"> + <span :class="small ? 'text-caption' : 'text-subtitle-2 '"> + {{ administrativeInformationTitle }} + </span> + </v-expansion-panel-title> + <v-expansion-panel-text :class="'bg-' + backgroundColor"> + <v-divider + v-if=" + Array.isArray(administrativeInformationObject?.creator?.keys) && + administrativeInformationObject?.creator?.keys.length > 0 + " + class="mb-1" + opacity="0.05"></v-divider> + <v-list nav class="pa-0"> + <!-- Creator --> + <v-list-item + v-if=" + Array.isArray(administrativeInformationObject?.creator?.keys) && + administrativeInformationObject?.creator?.keys.length > 0 + " + class="ma-0"> + <v-tooltip activator="parent" open-delay="600" transition="slide-x-transition"> + <div + v-for="(creator, i) in administrativeInformationObject.creator.keys" + :key="i" + class="text-caption"> + <span v-if="creator?.type" class="font-weight-bold">{{ + '(' + creator.type + ') ' + }}</span + >{{ creator.value }} + </div> + </v-tooltip> + <template #title> + <span class="text-subtitle-2"> + {{ + administrativeInformationObject.creator.keys.length === 1 + ? 'Creator:' + : 'Creators:' + }} + </span> + </template> + <v-list-item-subtitle + v-for="(creator, i) in administrativeInformationObject.creator.keys" + :key="i"> + <div v-if="creator?.type" class="pt-2"> + <v-chip label size="x-small" border class="mr-2">{{ creator.type }}</v-chip> + <span>{{ creator.value }}</span> + </div> + </v-list-item-subtitle> + </v-list-item> + <v-divider + v-if=" + Array.isArray(administrativeInformationObject?.creator?.keys) && + administrativeInformationObject?.creator?.keys.length > 0 && + (administrativeInformationObject?.version || administrativeInformationObject?.revision) + " + class="mt-2" + opacity="0.05"></v-divider> + <!-- Version and Revision --> + <v-list-item + v-if="administrativeInformationObject?.version || administrativeInformationObject?.revision" + class="ma-0"> + <v-list-item-title> + <template v-if="administrativeInformationObject?.version"> + <span class="text-subtitle-2 mt-2 mr-2">{{ 'Version:' }}</span + ><v-chip label size="x-small" border class="mr-5">{{ + administrativeInformationObject.version + }}</v-chip> + </template> + <template v-if="administrativeInformationObject?.revision"> + <span class="text-subtitle-2 mt-2 mr-2">{{ 'Revision:' }}</span + ><v-chip label size="x-small" border class="mr-5">{{ + administrativeInformationObject.revision + }}</v-chip> + </template> + </v-list-item-title> + </v-list-item> + </v-list> + <v-divider + v-if=" + ((Array.isArray(administrativeInformationObject?.creator?.keys) && + administrativeInformationObject?.creator?.keys.length > 0) || + administrativeInformationObject?.version || + administrativeInformationObject?.revision) && + administrativeInformationObject?.templateId + " + opacity="0.05"></v-divider> + <v-list nav class="pa-0"> + <v-hover v-slot="{ isHovering, props }"> + <v-list-item v-if="administrativeInformationObject?.templateId" class="ma-0"> + <template #title> + <span class="text-subtitle-2"> + {{ 'Template ID:' }} + </span> + </template> + <template #subtitle> + <div + v-if="administrativeInformationObject.templateId" + v-bind="props" + :class="isHovering ? 'cursor-pointer' : ''" + @click=" + copyToClipboard(administrativeInformationObject.templateId, 'Template ID') + "> + <v-icon v-if="isHovering" color="subtitleText" size="x-small" class="mr-1">{{ + copyIcon + }}</v-icon> + <span>{{ administrativeInformationObject.templateId }}</span> + </div> + </template> + </v-list-item> + </v-hover> + </v-list> + <v-divider + v-if=" + ((Array.isArray(administrativeInformationObject?.creator?.keys) && + administrativeInformationObject?.creator?.keys.length > 0) || + administrativeInformationObject?.version || + administrativeInformationObject?.revision || + administrativeInformationObject?.templateId) && + Array.isArray(administrativeInformationObject?.embeddedDataSpecifications) && + administrativeInformationObject?.embeddedDataSpecifications.length > 0 + " + opacity="0.05"></v-divider> + <!-- Embedded Data Specifications --> + <v-list + v-if=" + Array.isArray(administrativeInformationObject?.embeddedDataSpecifications) && + administrativeInformationObject?.embeddedDataSpecifications.length > 0 + " + nav + class="pa-0"> + <v-card + v-for="( + embeddedDataSpecification, i + ) in administrativeInformationObject.embeddedDataSpecifications" + :key="i" + color="elevatedCard" + class="mt-2"> + <v-list nav class="bg-elevatedCard pt-0"> + <!-- hasDataSpecification --> + <SemanticID + v-if=" + Array.isArray(embeddedDataSpecification?.dataSpecification?.keys) && + embeddedDataSpecification?.dataSpecification?.keys.length > 0 + " + :semantic-id-object="embeddedDataSpecification.dataSpecification" + :semantic-title="'Data Specification'" + class="mb-2"></SemanticID> + <v-divider v-if="embeddedDataSpecification?.dataSpecificationContent"></v-divider> + <!-- dataSpecificationContent --> + <DataSpecificationContent + v-if="embeddedDataSpecification?.dataSpecificationContent" + :data-specification-object=" + embeddedDataSpecification?.dataSpecificationContent + "></DataSpecificationContent> + </v-list> + </v-card> + </v-list> + </v-expansion-panel-text> + </v-expansion-panel> + </v-expansion-panels> + </v-container> +</template> + +<script lang="ts"> + import { defineComponent } from 'vue'; + import { useNavigationStore } from '@/store/NavigationStore'; + import DataSpecificationContent from './DataSpecificationContent.vue'; + import SemanticID from './SemanticID.vue'; + + export default defineComponent({ + name: 'AdministrativeInformationElement', + components: { + SemanticID, + DataSpecificationContent, + }, + // props: ['administrativeInformationObject', 'administrativeInformationTitle', 'small', 'backgroundColor'], + props: { + administrativeInformationObject: { + type: Object, + default: () => ({}), + }, + administrativeInformationTitle: { + type: String, + default: '', + }, + small: { + type: Boolean, + default: true, + }, + backgroundColor: { + type: String, + default: '', + }, + }, + + setup() { + const navigationStore = useNavigationStore(); + + return { + navigationStore, // NavigationStore Object + }; + }, + + data() { + return { + copyIcon: 'mdi-clipboard-file-outline', + }; + }, + + methods: { + // Function to copy the id to the clipboard + copyToClipboard(value: string, valueName: string) { + if (!value || !value) return; + // console.log('Copy ID to Clipboard: ', this.identificationObject.id); + // set the icon to checkmark + this.copyIcon = 'mdi-check'; + // copy the path to the clipboard + navigator.clipboard.writeText(value); + // set the clipboard tooltip to false after 1.5 seconds + setTimeout(() => { + this.copyIcon = 'mdi-clipboard-file-outline'; + }, 2000); + // open Snackbar to inform the user that the path was copied to the clipboard + this.navigationStore.dispatchSnackbar({ + status: true, + timeout: 2000, + color: 'success', + btnColor: 'buttonText', + text: valueName + ' copied to Clipboard.', + }); + }, + }, + }); +</script> + +<style lang="css" scoped> + .v-expansion-panel-text :deep(.v-expansion-panel-text__wrapper) { + padding-left: 8px !important; + padding-right: 8px !important; + padding-top: 0px !important; + padding-bottom: 12px !important; + } +</style>