Skip to content

Commit 2af7c85

Browse files
authored
Feature/incorporate more feedback (#559)
* make github link support more robust * apply formatting changes --------- Co-authored-by: Logende <Logende@users.noreply.github.com>
1 parent e3e1540 commit 2af7c85

6 files changed

Lines changed: 216 additions & 138 deletions

File tree

meta_configurator/src/components/dialogs/csvimport/ImportCsvDialog.vue

Lines changed: 140 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {type Ref, ref, watch} from 'vue';
44
import Dialog from 'primevue/dialog';
55
import Button from 'primevue/button';
66
import Divider from 'primevue/divider';
7+
import Panel from 'primevue/panel';
78
import Dropdown from 'primevue/dropdown';
89
import InputText from 'primevue/inputtext';
910
import RadioButton from 'primevue/radiobutton';
@@ -239,151 +240,160 @@ defineExpose({show: openDialog, close: hideDialog});
239240
class="text-green-500 ml-2" />
240241
</div>
241242

242-
<div v-if="currentUserDataString.length > 0">
243-
<div class="flex align-items-center vertical-center">
244-
<label for="delimiter" class="mr-2"><b>Delimiter in the CSV document:</b></label>
245-
<Dropdown
246-
id="delimiter"
247-
v-model="delimiter"
248-
:options="delimiterOptions"
249-
:option-label="option => option.label" />
250-
</div>
251-
252-
<div class="flex align-items-center vertical-center">
253-
<label for="decimalSeparator" class="mr-2"
254-
><b>Decimal Separator in the CSV document:</b></label
255-
>
256-
<Dropdown
257-
id="decimalSeparator"
258-
v-model="decimalSeparator"
259-
class="small-input"
260-
:options="decimalSeparatorOptions"
261-
:option-label="option => option.label" />
262-
</div>
263-
264-
<p v-if="errorMessage.length > 0" style="color: red; white-space: pre-line">
265-
{{ errorMessage }}
266-
</p>
267-
</div>
268-
269-
<div
270-
v-if="currentUserCsv.length > 0"
271-
class="flex flex-wrap justify-content-center gap-3 bigger-dialog-content">
272-
<Divider />
273-
274-
<div class="flex flex-wrap gap-4">
275-
<div class="flex items-center">
276-
<RadioButton
277-
v-model="isExpandWithLookupTables"
278-
inputId="independentTable"
279-
name="independentTable"
280-
:value="false" />
281-
<label for="independentTable" class="ml-2">Independent Table</label>
282-
</div>
283-
<div class="flex items-center">
284-
<RadioButton
285-
v-model="isExpandWithLookupTables"
286-
inputId="expandWithLookupTable"
287-
name="expandWithLookupTable"
288-
:value="true"
289-
:disabled="possiblePreviousTables.length == 0" />
290-
<label for="expand" class="ml-2">Expand with Lookup Table</label>
291-
</div>
292-
</div>
293-
<div v-if="!isExpandWithLookupTables">
294-
<span>Import this CSV as a standalone table.</span>
295-
296-
<div class="flex align-items-center vertical-center">
297-
<label for="delimiter" class="mr-2">
298-
<b>Path for the resulting array in the document:</b>
299-
</label>
300-
<InputText v-model="pathBeforeRowIndex" class="fixed-width" />
301-
</div>
302-
</div>
303-
<div v-else>
304-
<span
305-
>Use this CSV to expand an existing table by matching foreign keys with primary keys
306-
from the lookup table.</span
307-
>
308-
309-
<div class="flex align-items-center vertical-center">
310-
<label class="mr-2">
311-
<b>Primary key in new CSV:</b>
312-
</label>
313-
<Dropdown
314-
id="tableToExpand"
315-
v-model="primaryKeyProp"
316-
class="fixed-width"
317-
:options="possiblePrimaryKeyProps"
318-
:option-label="option => option.label" />
319-
</div>
243+
<Panel
244+
v-if="currentUserDataString.length > 0"
245+
header="Import Options"
246+
toggleable
247+
:collapsed="true">
248+
<div>
320249
<div class="flex align-items-center vertical-center">
321-
<label class="mr-2">
322-
<b>Table to expand:</b>
323-
</label>
250+
<label for="delimiter" class="mr-2"><b>Delimiter in the CSV document:</b></label>
324251
<Dropdown
325-
id="tableToExpand"
326-
v-model="tableToExpand"
327-
class="fixed-width"
328-
:options="possiblePreviousTables"
252+
id="delimiter"
253+
v-model="delimiter"
254+
:options="delimiterOptions"
329255
:option-label="option => option.label" />
330256
</div>
257+
331258
<div class="flex align-items-center vertical-center">
332-
<label class="mr-2">
333-
<b>Foreign key in existing data:</b>
334-
</label>
259+
<label for="decimalSeparator" class="mr-2"
260+
><b>Decimal Separator in the CSV document:</b></label
261+
>
335262
<Dropdown
336-
id="foreignKeyName"
337-
v-model="foreignKey"
338-
class="fixed-width"
339-
:options="possibleForeignKeyProps"
263+
id="decimalSeparator"
264+
v-model="decimalSeparator"
265+
class="small-input"
266+
:options="decimalSeparatorOptions"
340267
:option-label="option => option.label" />
341268
</div>
269+
270+
<p v-if="errorMessage.length > 0" style="color: red; white-space: pre-line">
271+
{{ errorMessage }}
272+
</p>
342273
</div>
343274

344-
<Divider />
275+
<div
276+
v-if="currentUserCsv.length > 0"
277+
class="flex flex-wrap justify-content-center gap-3 bigger-dialog-content">
278+
<Divider />
279+
280+
<div class="flex flex-wrap gap-4">
281+
<div class="flex items-center">
282+
<RadioButton
283+
v-model="isExpandWithLookupTables"
284+
inputId="independentTable"
285+
name="independentTable"
286+
:value="false" />
287+
<label for="independentTable" class="ml-2">Independent Table</label>
288+
</div>
289+
<div class="flex items-center">
290+
<RadioButton
291+
v-model="isExpandWithLookupTables"
292+
inputId="expandWithLookupTable"
293+
name="expandWithLookupTable"
294+
:value="true"
295+
:disabled="possiblePreviousTables.length == 0" />
296+
<label for="expand" class="ml-2">Expand with Lookup Table</label>
297+
</div>
298+
</div>
299+
<div v-if="!isExpandWithLookupTables">
300+
<span>Import this CSV as a standalone table.</span>
301+
302+
<div class="flex align-items-center vertical-center">
303+
<label for="delimiter" class="mr-2">
304+
<b>Path for the resulting array in the document:</b>
305+
</label>
306+
<InputText v-model="pathBeforeRowIndex" class="fixed-width" />
307+
</div>
308+
</div>
309+
<div v-else>
310+
<span
311+
>Use this CSV to expand an existing table by matching foreign keys with primary keys
312+
from the lookup table.</span
313+
>
314+
315+
<div class="flex align-items-center vertical-center">
316+
<label class="mr-2">
317+
<b>Primary key in new CSV:</b>
318+
</label>
319+
<Dropdown
320+
id="tableToExpand"
321+
v-model="primaryKeyProp"
322+
class="fixed-width"
323+
:options="possiblePrimaryKeyProps"
324+
:option-label="option => option.label" />
325+
</div>
326+
<div class="flex align-items-center vertical-center">
327+
<label class="mr-2">
328+
<b>Table to expand:</b>
329+
</label>
330+
<Dropdown
331+
id="tableToExpand"
332+
v-model="tableToExpand"
333+
class="fixed-width"
334+
:options="possiblePreviousTables"
335+
:option-label="option => option.label" />
336+
</div>
337+
<div class="flex align-items-center vertical-center">
338+
<label class="mr-2">
339+
<b>Foreign key in existing data:</b>
340+
</label>
341+
<Dropdown
342+
id="foreignKeyName"
343+
v-model="foreignKey"
344+
class="fixed-width"
345+
:options="possibleForeignKeyProps"
346+
:option-label="option => option.label" />
347+
</div>
348+
</div>
345349

346-
<div class="flex align-items-center vertical-center">
347-
<label for="delimiter" class="mr-2"><b>Infer and generate schema for the data:</b></label>
348-
<InputSwitch id="delimiter" v-model="isInferSchema" class="small-input" />
349-
</div>
350+
<Divider />
350351

351-
<p>
352-
CSV file has {{ currentUserCsv.length }} rows and
353-
{{ currentColumnMapping.length }} columns (attributes).
354-
</p>
355-
<p>Define the mapping from the CSV to the JSON document for each attribute.</p>
356-
357-
<table>
358-
<thead>
359-
<tr>
360-
<th>CSV Column</th>
361-
<th>JSON Property Identifier</th>
362-
<!--th v-if="isInferSchema">Property Schema Title</--th-->
363-
</tr>
364-
</thead>
365-
<tbody>
366-
<tr v-for="column in currentColumnMapping">
367-
<td>{{ column.name }}</td>
368-
<td>
369-
<span class="text-xs">/{{ column.pathBeforeRowIndex }}/ROW_INDEX/</span>
370-
<span class="text-xs" v-if="isExpandWithLookupTables">{{ foreignKey.value }}/</span>
371-
<InputText v-model="column.pathAfterRowIndex" class="fixed-width" />
372-
</td>
373-
<!--td v-if="isInferSchema">
352+
<div class="flex align-items-center vertical-center">
353+
<label for="delimiter" class="mr-2"
354+
><b>Infer and generate schema for the data:</b></label
355+
>
356+
<InputSwitch id="delimiter" v-model="isInferSchema" class="small-input" />
357+
</div>
358+
359+
<p>
360+
CSV file has {{ currentUserCsv.length }} rows and
361+
{{ currentColumnMapping.length }} columns (attributes).
362+
</p>
363+
<p>Define the mapping from the CSV to the JSON document for each attribute.</p>
364+
365+
<table>
366+
<thead>
367+
<tr>
368+
<th>CSV Column</th>
369+
<th>JSON Property Identifier</th>
370+
<!--th v-if="isInferSchema">Property Schema Title</--th-->
371+
</tr>
372+
</thead>
373+
<tbody>
374+
<tr v-for="column in currentColumnMapping">
375+
<td>{{ column.name }}</td>
376+
<td>
377+
<span class="text-xs">/{{ column.pathBeforeRowIndex }}/ROW_INDEX/</span>
378+
<span class="text-xs" v-if="isExpandWithLookupTables"
379+
>{{ foreignKey.value }}/</span
380+
>
381+
<InputText v-model="column.pathAfterRowIndex" class="fixed-width" />
382+
</td>
383+
<!--td v-if="isInferSchema">
374384
<InputText v-model="column.titleInSchema" class="fixed-width" />
375385
</td-->
376-
</tr>
377-
</tbody>
378-
</table>
379-
380-
<Divider />
386+
</tr>
387+
</tbody>
388+
</table>
389+
</div>
390+
</Panel>
381391

382-
<Button
383-
@click="submitImport"
384-
class="p-button-raised p-button-rounded"
385-
label="Import"></Button>
386-
</div>
392+
<Button
393+
v-if="currentUserCsv.length > 0"
394+
@click="submitImport"
395+
class="p-button-raised p-button-rounded"
396+
label="Import"></Button>
387397
</div>
388398
</Dialog>
389399
</template>

meta_configurator/src/components/panels/gui-editor/GuiEditorPanelJsonSchema.vue

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,15 @@ import PropertiesPanel from '@/components/panels/gui-editor/PropertiesPanel.vue'
55
import type {Path} from '@/utility/path';
66
import {computed} from 'vue';
77
import {JsonSchemaWrapper} from '@/schema/jsonSchemaWrapper';
8-
import {getDataForMode, getSessionForMode} from '@/data/useDataLink';
8+
import {
9+
getDataForMode,
10+
getSchemaForMode,
11+
getSessionForMode,
12+
useCurrentSchema,
13+
} from '@/data/useDataLink';
914
import type {SessionMode} from '@/store/sessionMode';
15+
import {dataPathToSchemaPath} from '@/utility/pathUtils';
16+
import _ from 'lodash';
1017
1118
const props = defineProps<{
1219
sessionMode: SessionMode;
@@ -20,6 +27,48 @@ function updatePath(newPath: Path) {
2027
}
2128
2229
function updateData(path: Path, newValue: any) {
30+
if (path.length > 0) {
31+
const dataAtPath = data.dataAt(path);
32+
33+
if (dataAtPath == null) {
34+
// if the element is new, we add it to the parent object in a sorted way
35+
const parentPath = path.slice(0, path.length - 1);
36+
const parentSchemaPath = dataPathToSchemaPath(parentPath);
37+
const parentSchemaProps = getSchemaForMode(props.sessionMode).effectiveSchemaAtPath(
38+
parentSchemaPath
39+
).schema.properties;
40+
const parentData = structuredClone(data.dataAt(parentPath));
41+
if (!_.isEmpty(parentSchemaProps) && !_.isEmpty(parentData)) {
42+
// only proceed with property sorting when parent schema and data are not empty
43+
// warning: this function only works for normal properties, not for composition, conditionals and other advanced features
44+
// for those advanced features, the new property will be added at the end of the object
45+
// TODO: implement sorting for advanced features. This will be more complicated and will require a lot of testing
46+
const schemaKeys = Object.keys(parentSchemaProps);
47+
const dataKeys = Object.keys(parentData);
48+
const newElementKey = path[path.length - 1];
49+
parentData[newElementKey] = newValue; // Add the new property
50+
51+
// sort the document properties based on the order of schema properties
52+
const sortedProperties: {[key: string]: any} = {};
53+
schemaKeys.forEach(key => {
54+
if (dataKeys.includes(key) || key === newElementKey) {
55+
sortedProperties[key] = parentData[key];
56+
}
57+
});
58+
// after adding properties from the schema in proper order, add the rest of the properties
59+
dataKeys.forEach(key => {
60+
if (!schemaKeys.includes(key)) {
61+
sortedProperties[key] = parentData[key];
62+
}
63+
});
64+
65+
// if properties were sorted, update the parent object instead of the new property
66+
path = parentPath;
67+
newValue = sortedProperties;
68+
}
69+
}
70+
}
71+
2372
data.setDataAt(path, newValue);
2473
}
2574

meta_configurator/src/components/panels/schema-diagram/VueFlowPanel.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,11 @@ function updateGraph(forceRebuild: boolean = false) {
174174
// only data updated or nodes removed
175175
const nodesToRemove = updateNodeData(activeNodes.value, vueFlowGraph.nodes);
176176
activeNodes.value = activeNodes.value.filter(node => !nodesToRemove.includes(node.id));
177+
177178
// we still update edges, because they might have changed
179+
// todo: check if the edges are updated and only then trigger layouting
178180
activeEdges.value = vueFlowGraph.edges;
181+
graphNeedsLayouting = true;
179182
}
180183
181184
// if not on root level but current path is set: show only subgraph
@@ -188,8 +191,10 @@ function updateGraph(forceRebuild: boolean = false) {
188191
layoutGraph(graphDirection.value, false);
189192
}
190193
191-
if (!graphNeedsLayouting && schemaSession.currentSelectedElement.value) {
192-
fitViewForElementByPath(schemaSession.currentSelectedElement.value);
194+
if (schemaSession.currentSelectedElement.value) {
195+
nextTick(() => {
196+
fitViewForElementByPath(schemaSession.currentSelectedElement.value);
197+
});
193198
}
194199
}
195200

0 commit comments

Comments
 (0)