Skip to content

Commit 325cccf

Browse files
committed
Adds JSON insert to editor
1 parent b146475 commit 325cccf

File tree

4 files changed

+264
-2
lines changed

4 files changed

+264
-2
lines changed

aas-web-ui/src/components.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ declare module 'vue' {
5858
ImagePreview: typeof import('./components/Plugins/ImagePreview.vue')['default']
5959
InvalidElement: typeof import('./components/SubmodelElements/InvalidElement.vue')['default']
6060
JSONArrayProperty: typeof import('./components/Plugins/SubmodelElements/JSONArrayProperty.vue')['default']
61+
JsonInsert: typeof import('./components/EditorComponents/JsonInsert.vue')['default']
6162
JSONPreview: typeof import('./components/Plugins/JSONPreview.vue')['default']
6263
LastSync: typeof import('./components/UIComponents/LastSync.vue')['default']
6364
LineChart: typeof import('./components/Widgets/LineChart.vue')['default']
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
<template>
2+
<v-dialog v-model="jsonInsertDialog" width="860" persistent>
3+
<v-card>
4+
<v-card-title>Insert {{ type }} from JSON</v-card-title>
5+
<v-divider></v-divider>
6+
<v-card-text class="bg-card pa-3">
7+
<v-card>
8+
<v-textarea
9+
v-model="jsonInput"
10+
:error-messages="jsonInputErrors"
11+
variant="outlined"
12+
rows="10"
13+
hide-details
14+
@update:model-value="clearErrorMessages" />
15+
</v-card>
16+
</v-card-text>
17+
<v-divider></v-divider>
18+
<v-card-actions>
19+
<v-spacer></v-spacer>
20+
<v-btn @click="closeDialog">Cancel</v-btn>
21+
<v-btn color="primary" @click="insertJson">Save</v-btn>
22+
</v-card-actions>
23+
</v-card>
24+
</v-dialog>
25+
</template>
26+
27+
<script lang="ts" setup>
28+
import type { JsonValue } from '@aas-core-works/aas-core3.0-typescript/jsonization';
29+
import { jsonization, types as aasTypes } from '@aas-core-works/aas-core3.0-typescript';
30+
import { computed, ref, watch } from 'vue';
31+
import { useRouter } from 'vue-router';
32+
import { useAASRepositoryClient } from '@/composables/Client/AASRepositoryClient';
33+
import { useSMRepositoryClient } from '@/composables/Client/SMRepositoryClient';
34+
import { useAASStore } from '@/store/AASDataStore';
35+
import { useNavigationStore } from '@/store/NavigationStore';
36+
import { extractEndpointHref } from '@/utils/AAS/DescriptorUtils';
37+
import { base64Decode, base64Encode } from '@/utils/EncodeDecodeUtils';
38+
39+
const props = defineProps<{
40+
modelValue: boolean;
41+
type: 'Submodel' | 'SubmodelElement';
42+
parentElement?: any;
43+
}>();
44+
45+
const emit = defineEmits<{
46+
(event: 'update:modelValue', value: boolean): void;
47+
}>();
48+
49+
// Vue Router
50+
const router = useRouter();
51+
52+
// Stores
53+
const aasStore = useAASStore();
54+
const navigationStore = useNavigationStore();
55+
56+
// Composables
57+
const { postSubmodel, postSubmodelElement } = useSMRepositoryClient();
58+
const { putAas } = useAASRepositoryClient();
59+
60+
// Data
61+
const jsonInsertDialog = ref(false);
62+
const jsonInput = ref<string | null>(null);
63+
const jsonInputErrors = ref<string[]>([]);
64+
65+
// Computed Properties
66+
const selectedAAS = computed(() => aasStore.getSelectedAAS); // Get the selected AAS from Store
67+
const submodelRepoUrl = computed(() => navigationStore.getSubmodelRepoURL);
68+
69+
watch(
70+
() => props.modelValue,
71+
(value) => {
72+
jsonInsertDialog.value = value;
73+
}
74+
);
75+
76+
watch(
77+
() => jsonInsertDialog.value,
78+
(value) => {
79+
emit('update:modelValue', value);
80+
}
81+
);
82+
83+
function insertJson(): void {
84+
if (!jsonInput.value || !isValidJson(jsonInput.value)) {
85+
jsonInputErrors.value = ['Invalid JSON input'];
86+
return;
87+
}
88+
89+
// Parse JSON to Submodel/SubmodelElement
90+
if (props.type === 'Submodel') {
91+
insertSubmodel(JSON.parse(jsonInput.value));
92+
} else {
93+
insertSubmodelElement(JSON.parse(jsonInput.value));
94+
}
95+
}
96+
97+
async function insertSubmodel(json: JsonValue): Promise<void> {
98+
// Parse JSON to Submodel
99+
const instanceOrError = jsonization.submodelFromJsonable(json);
100+
if (instanceOrError.error !== null) {
101+
console.error('Error parsing Submodel: ', instanceOrError.error);
102+
return;
103+
}
104+
const submodel = instanceOrError.mustValue();
105+
106+
// Create Submodel
107+
await postSubmodel(submodel);
108+
// Add Submodel Reference to AAS
109+
await addSubmodelReferenceToAas(submodel);
110+
// Fetch and dispatch Submodel
111+
const path = submodelRepoUrl.value + '/' + base64Encode(submodel.id);
112+
const aasEndpoint = extractEndpointHref(selectedAAS.value, 'AAS-3.0');
113+
router.push({ query: { aas: aasEndpoint, path: path } });
114+
115+
closeDialog();
116+
navigationStore.dispatchTriggerTreeviewReload();
117+
}
118+
119+
async function insertSubmodelElement(json: JsonValue): Promise<void> {
120+
const instanceOrError = jsonization.submodelElementFromJsonable(json);
121+
if (instanceOrError.error !== null) {
122+
console.error('Error parsing SubmodelElement: ', instanceOrError.error);
123+
return;
124+
}
125+
const submodelElement = instanceOrError.mustValue();
126+
127+
if (props.parentElement.modelType === 'Submodel') {
128+
// Create the property on the parent Submodel
129+
await postSubmodelElement(submodelElement, props.parentElement.id);
130+
131+
const aasEndpoint = extractEndpointHref(selectedAAS.value, 'AAS-3.0');
132+
133+
// Navigate to the new property
134+
router.push({
135+
query: {
136+
aas: aasEndpoint,
137+
path: props.parentElement.path + '/submodel-elements/' + submodelElement.idShort,
138+
},
139+
});
140+
} else {
141+
// Extract the submodel ID and the idShortPath from the parentElement path
142+
const splitted = props.parentElement.path.split('/submodel-elements/');
143+
const submodelId = base64Decode(splitted[0].split('/submodels/')[1]);
144+
const idShortPath = splitted[1];
145+
146+
// Create the property on the parent element
147+
await postSubmodelElement(submodelElement, submodelId, idShortPath);
148+
149+
const aasEndpoint = extractEndpointHref(selectedAAS.value, 'AAS-3.0');
150+
151+
// Navigate to the new property
152+
if (props.parentElement.modelType === 'SubmodelElementCollection') {
153+
router.push({
154+
query: {
155+
aas: aasEndpoint,
156+
path: props.parentElement.path + '.' + submodelElement.idShort,
157+
},
158+
});
159+
}
160+
}
161+
162+
closeDialog();
163+
navigationStore.dispatchTriggerTreeviewReload();
164+
}
165+
166+
async function addSubmodelReferenceToAas(submodel: aasTypes.Submodel): Promise<void> {
167+
if (selectedAAS.value === null) return;
168+
const localAAS = { ...selectedAAS.value };
169+
const instanceOrError = jsonization.assetAdministrationShellFromJsonable(localAAS);
170+
if (instanceOrError.error !== null) {
171+
console.error('Error parsing AAS: ', instanceOrError.error);
172+
return;
173+
}
174+
const aas = instanceOrError.mustValue();
175+
// Create new SubmodelReference
176+
const submodelReference = new aasTypes.Reference(aasTypes.ReferenceTypes.ExternalReference, [
177+
new aasTypes.Key(aasTypes.KeyTypes.Submodel, submodel.id),
178+
]);
179+
// Check if Submodels are null
180+
if (aas.submodels === null || aas.submodels === undefined) {
181+
aas.submodels = [submodelReference];
182+
localAAS.submodels = [jsonization.toJsonable(submodelReference)];
183+
} else {
184+
aas.submodels.push(submodelReference);
185+
localAAS.submodels.push(jsonization.toJsonable(submodelReference));
186+
}
187+
await putAas(aas);
188+
189+
// Update AAS in Store
190+
aasStore.dispatchSelectedAAS(localAAS);
191+
}
192+
193+
function isValidJson(jsonString: string): boolean {
194+
try {
195+
JSON.parse(jsonString);
196+
return true;
197+
} catch {
198+
return false;
199+
}
200+
}
201+
202+
function closeDialog(): void {
203+
clearForm();
204+
jsonInsertDialog.value = false;
205+
}
206+
207+
function clearForm(): void {
208+
jsonInput.value = null;
209+
}
210+
211+
function clearErrorMessages(): void {
212+
jsonInputErrors.value = [];
213+
}
214+
</script>

aas-web-ui/src/components/SubmodelTree.vue

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,20 @@
9393
</v-list-item>
9494
</template>
9595
<span>Create a new Submodel</span>
96-
</v-tooltip></v-list
97-
>
96+
</v-tooltip>
97+
<!-- Open JSON insert dialog -->
98+
<v-tooltip open-delay="600" location="end">
99+
<template #activator="{ props }">
100+
<v-list-item slim v-bind="props" @click="openJsonInsertDialog('Submodel')">
101+
<template #prepend>
102+
<v-icon size="small">mdi-code-json</v-icon>
103+
</template>
104+
Submodel from JSON
105+
</v-list-item>
106+
</template>
107+
<span>Create a new Submodel from JSON</span>
108+
</v-tooltip>
109+
</v-list>
98110
</v-sheet>
99111
</v-menu>
100112
</template>
@@ -139,6 +151,7 @@
139151
@open-edit-submodel-element-dialog="openEditSubmodelElementDialogForElement"
140152
@open-add-submodel-element-dialog="openAddSubmodelElementDialog"
141153
@open-edit-dialog="openEditDialog(false, $event)"
154+
@open-json-insert-dialog="openJsonInsertDialog('SubmodelElement', $event)"
142155
@show-delete-dialog="openDeleteDialog"></Treeview>
143156
</template>
144157
<v-empty-state
@@ -217,6 +230,8 @@
217230
:sml="submodelElementToEdit"></ListForm>
218231
<!-- Dialog for creating/editing Submodel -->
219232
<SubmodelForm v-model="editDialog" :new-sm="newSubmodel" :submodel="submodelToEdit"></SubmodelForm>
233+
<!-- Dialog for inserting JSON -->
234+
<JsonInsert v-model="jsonInsertDialog" :type="jsonInsertType" :parent-element="elementToAddSME"></JsonInsert>
220235
<!-- Dialog for deleting SM/SME -->
221236
<DeleteDialog v-model="deleteDialog" :element="elementToDelete"></DeleteDialog>
222237
</template>
@@ -274,6 +289,8 @@
274289
const elementToAddSME = ref<any | undefined>(undefined); // Variable to store the Element where the new SME is added inside
275290
const submodelElementPath = ref<string | undefined>(undefined); // Variable to store the Element where the new SME is added inside
276291
const submodelElementToEdit = ref<any | undefined>(undefined); // Variable to store the Element where the new SME is added inside
292+
const jsonInsertDialog = ref(false); // Variable to store if the JSON Insert Dialog should be shown
293+
const jsonInsertType = ref<'Submodel' | 'SubmodelElement'>('Submodel'); // Variable to store the ModelType of the JSON to be inserted
277294
278295
// Computed Properties
279296
const isMobile = computed(() => navigationStore.getIsMobile); // Check if the current Device is a Mobile Device
@@ -489,6 +506,14 @@
489506
}
490507
}
491508
509+
function openJsonInsertDialog(type: 'Submodel' | 'SubmodelElement', element?: any): void {
510+
jsonInsertDialog.value = true;
511+
jsonInsertType.value = type;
512+
if (type === 'SubmodelElement' && element) {
513+
elementToAddSME.value = element;
514+
}
515+
}
516+
492517
function openDeleteDialog(element: any): void {
493518
deleteDialog.value = true;
494519
elementToDelete.value = element;

aas-web-ui/src/components/UIComponents/Treeview.vue

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,13 @@
161161
</template>
162162
<v-list-item-subtitle>Add Submodel Element</v-list-item-subtitle>
163163
</v-list-item>
164+
<!-- Open Insert SubmodelElement from JSON dialog -->
165+
<v-list-item @click="$emit('openJsonInsertDialog', item)">
166+
<template #prepend>
167+
<v-icon size="x-small">mdi-code-json</v-icon>
168+
</template>
169+
<v-list-item-subtitle>Submodel Element from JSON</v-list-item-subtitle>
170+
</v-list-item>
164171
<v-divider></v-divider>
165172
<!-- Open Submodel edit dialog -->
166173
<v-list-item @click="$emit('openEditDialog', item)">
@@ -213,6 +220,19 @@
213220
</template>
214221
<v-list-item-subtitle>Add Submodel Element</v-list-item-subtitle>
215222
</v-list-item>
223+
<!-- Open Insert SubmodelElement from JSON dialog -->
224+
<v-list-item
225+
v-if="
226+
item.modelType === 'SubmodelElementCollection' ||
227+
item.modelType === 'SubmodelElementList' ||
228+
item.modelType === 'Entity'
229+
"
230+
@click="$emit('openJsonInsertDialog', item)">
231+
<template #prepend>
232+
<v-icon size="x-small">mdi-code-json</v-icon>
233+
</template>
234+
<v-list-item-subtitle>Submodel Element from JSON</v-list-item-subtitle>
235+
</v-list-item>
216236
<v-divider
217237
v-if="
218238
item.modelType === 'SubmodelElementCollection' ||
@@ -261,6 +281,7 @@
261281
:depth="depth + 1"
262282
@open-add-submodel-element-dialog="$emit('openAddSubmodelElementDialog', $event)"
263283
@open-edit-submodel-element-dialog="$emit('openEditSubmodelElementDialog', $event)"
284+
@open-json-insert-dialog="$emit('openJsonInsertDialog', $event)"
264285
@show-delete-dialog="$emit('showDeleteDialog', $event)"></Treeview>
265286
</template>
266287
</div>
@@ -303,6 +324,7 @@
303324
(event: 'openEditDialog', item: any): void;
304325
(event: 'showDeleteDialog', item: any): void;
305326
(event: 'openAddSubmodelElementDialog', item: any): void;
327+
(event: 'openJsonInsertDialog', type: 'Submodel' | 'SubmodelElement'): void;
306328
(event: 'openEditSubmodelElementDialog', item: any): void;
307329
}>();
308330

0 commit comments

Comments
 (0)