@raclettejs/workbench 0.1.31 → 0.1.32-nightly-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.raclette/backend/raclette.config.js +515 -0
- package/.raclette/backend/yarn.lock +3636 -0
- package/.raclette/frontend/raclette.config.js +43 -0
- package/.raclette/frontend/yarn.lock +3388 -0
- package/.raclette/virtual/backend/README.md +172 -0
- package/.raclette/virtual/backend/raclette.config.js +515 -0
- package/.raclette/virtual/backend/yarn.lock +3636 -0
- package/.raclette/virtual/frontend/README.md +511 -0
- package/.raclette/virtual/frontend/raclette.config.js +43 -0
- package/.raclette/virtual/frontend/src/core/lib/eggs/readme.md +75 -0
- package/.raclette/virtual/frontend/yarn.lock +3388 -0
- package/CHANGELOG.md +6 -10
- package/i18n/de-DE.json +1 -10
- package/i18n/en-EU.json +1 -10
- package/i18n/sk-SK.json +1 -10
- package/package.json +5 -3
- package/plugins/pacifico__compositions/frontend/widgets/CompositionListWidget.vue +63 -107
- package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkListWidget.vue +65 -122
- package/plugins/pacifico__plugins/frontend/widgets/PluginListWidget.vue +2 -2
- package/plugins/pacifico__tags/frontend/widgets/TagListWidget.vue +48 -104
- package/plugins/pacifico__users/frontend/widgets/UserListWidget.vue +22 -16
- package/services/frontend/src/app/lib/jsonTools.ts +69 -3
- package/services/frontend/src/app/components/BaseDataTable.vue +0 -303
- package/services/frontend/src/app/components/FileUpload.vue +0 -98
- package/services/frontend/src/app/components/SelectionDialog.vue +0 -182
- package/services/frontend/src/app/components/SummaryDialog.vue +0 -140
package/CHANGELOG.md
CHANGED
|
@@ -7,24 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
- set eslint version to latest
|
|
11
11
|
|
|
12
|
-
## [0.1.
|
|
12
|
+
## [0.1.32] - 2026-04-30 <a href="https://gitlab.com/raclettejs/core-dev/-/compare/v0.1.31...v0.1.32" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
|
|
13
13
|
|
|
14
|
-
## [0.1.
|
|
14
|
+
## [0.1.31] - 2026-04-27 <a href="https://gitlab.com/raclettejs/core-dev/-/compare/v0.1.30...v0.1.31" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
|
|
15
15
|
|
|
16
|
-
## [0.1.
|
|
16
|
+
## [0.1.30] - 2026-04-27 <a href="https://gitlab.com/raclettejs/core-dev/-/compare/v0.1.29...v0.1.30" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
## [0.1.29] - 2026-04-27 <a href="https://gitlab.com/raclettejs/core-dev/-/compare/v0.1.28...v0.1.29" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
|
|
19
19
|
|
|
20
|
-
- core
|
|
20
|
+
## [0.1.28] - 2026-04-27 <a href="https://gitlab.com/raclettejs/core-dev/-/compare/v0.1.27...v0.1.28" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
|
|
21
21
|
|
|
22
22
|
## [0.1.27] - 2026-04-27 <a href="https://gitlab.com/raclettejs/core-dev/-/compare/v0.1.26...v0.1.27" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
|
|
23
23
|
|
|
24
|
-
### Updated
|
|
25
|
-
|
|
26
|
-
- core release
|
|
27
|
-
|
|
28
24
|
## [0.1.26] - 2026-04-27 <a href="https://gitlab.com/raclettejs/core-dev/-/compare/v0.1.25...v0.1.26" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
|
|
29
25
|
|
|
30
26
|
### Updated
|
package/i18n/de-DE.json
CHANGED
|
@@ -38,15 +38,6 @@
|
|
|
38
38
|
"continue": "Weiter"
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
|
-
"baseDataTable": {
|
|
42
|
-
"searchPlaceholder": "{dataName} durchsuchen",
|
|
43
|
-
"createDataButton": "{dataName} erstellen",
|
|
44
|
-
"deleteDataTitle": "{dataName} löschen",
|
|
45
|
-
"restoreDataTitle": "{dataName} widerherstellen",
|
|
46
|
-
"deleteDataConfirmMessage": "Bist du sicher, dass du {article} {dataName} <strong>{value}</strong> löschen willst?",
|
|
47
|
-
"deleteDataConfirmButton": "Löschen",
|
|
48
|
-
"deleteDataConfirmCancel": "Abbrechen"
|
|
49
|
-
},
|
|
50
41
|
"compositionList": {
|
|
51
42
|
"dataName": "Komposition",
|
|
52
43
|
"dataArticle": "die",
|
|
@@ -290,4 +281,4 @@
|
|
|
290
281
|
}
|
|
291
282
|
}
|
|
292
283
|
}
|
|
293
|
-
}
|
|
284
|
+
}
|
package/i18n/en-EU.json
CHANGED
|
@@ -38,15 +38,6 @@
|
|
|
38
38
|
"continue": "Continue"
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
|
-
"baseDataTable": {
|
|
42
|
-
"searchPlaceholder": "Search {dataName}",
|
|
43
|
-
"createDataButton": "Create {dataName}",
|
|
44
|
-
"deleteDataTitle": "Delete {dataName}",
|
|
45
|
-
"restoreDataTitle": "Restore {dataName}",
|
|
46
|
-
"deleteDataConfirmMessage": "Are you sure you want to delete {article} {dataName} <strong>{value}</strong>?",
|
|
47
|
-
"deleteDataConfirmButton": "Delete",
|
|
48
|
-
"deleteDataConfirmCancel": "Cancel"
|
|
49
|
-
},
|
|
50
41
|
"compositionList": {
|
|
51
42
|
"dataName": "Composition",
|
|
52
43
|
"dataArticle": "the",
|
|
@@ -290,4 +281,4 @@
|
|
|
290
281
|
}
|
|
291
282
|
}
|
|
292
283
|
}
|
|
293
|
-
}
|
|
284
|
+
}
|
package/i18n/sk-SK.json
CHANGED
|
@@ -38,15 +38,6 @@
|
|
|
38
38
|
"continue": "Pokračovať"
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
|
-
"baseDataTable": {
|
|
42
|
-
"searchPlaceholder": "Hľadať {dataName}",
|
|
43
|
-
"createDataButton": "Vytvoriť {dataName}",
|
|
44
|
-
"deleteDataTitle": "Odstrániť {dataName}",
|
|
45
|
-
"restoreDataTitle": "Obnoviť {dataName}",
|
|
46
|
-
"deleteDataConfirmMessage": "Naozaj chcete odstrániť {article} {dataName} <strong>{value}</strong>?",
|
|
47
|
-
"deleteDataConfirmButton": "Odstrániť",
|
|
48
|
-
"deleteDataConfirmCancel": "Zrušiť"
|
|
49
|
-
},
|
|
50
41
|
"compositionList": {
|
|
51
42
|
"dataName": "Kompozícia",
|
|
52
43
|
"dataArticle": "",
|
|
@@ -285,4 +276,4 @@
|
|
|
285
276
|
}
|
|
286
277
|
}
|
|
287
278
|
}
|
|
288
|
-
}
|
|
279
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raclettejs/workbench",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32-nightly-1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "racletteJS Workbench - used to configure your application, for dev and prod",
|
|
6
6
|
"repository": "https://gitlab.com/raclettejs/workbench",
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
"update": "raclette update",
|
|
14
14
|
"restart": "raclette restart",
|
|
15
15
|
"add-package": "raclette add-package",
|
|
16
|
+
"format:check": "prettier --check .",
|
|
17
|
+
"format:fix": "prettier --write .",
|
|
16
18
|
"lint": "eslint",
|
|
17
19
|
"lint:fix": "eslint --fix"
|
|
18
20
|
},
|
|
@@ -31,7 +33,7 @@
|
|
|
31
33
|
".": "./index.ts"
|
|
32
34
|
},
|
|
33
35
|
"dependencies": {
|
|
34
|
-
"@raclettejs/core": "^0.1.
|
|
36
|
+
"@raclettejs/core": "^0.1.32-nightly-1",
|
|
35
37
|
"@vueuse/integrations": "14.2.1",
|
|
36
38
|
"fuse.js": "7.1.0",
|
|
37
39
|
"three": "0.183.2",
|
|
@@ -41,7 +43,7 @@
|
|
|
41
43
|
"@emnapi/core": "1.9.2",
|
|
42
44
|
"@emnapi/runtime": "1.9.2",
|
|
43
45
|
"@eslint/js": "9.35.0",
|
|
44
|
-
"@raclettejs/types": "^0.1.
|
|
46
|
+
"@raclettejs/types": "^0.1.32-nightly-1",
|
|
45
47
|
"@sinclair/typebox": "0.34.48",
|
|
46
48
|
"@types/ramda": "0.31.1",
|
|
47
49
|
"@vueuse/core": "14.2.1",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<div>
|
|
3
3
|
<BaseDataTable
|
|
4
4
|
:items="parsedCompositions"
|
|
5
|
+
:exportable-items="exportableCompositions"
|
|
5
6
|
:loading="compositionsLoading || tagsLoading"
|
|
6
7
|
:headers="headers"
|
|
7
8
|
create-interaction-link-id="compositionCreateInteractionLink"
|
|
@@ -9,16 +10,17 @@
|
|
|
9
10
|
:data-name="$t('workbench.compositionList.dataName')"
|
|
10
11
|
:data-article="$t('workbench.compositionList.dataArticle')"
|
|
11
12
|
@delete="deleteComposition"
|
|
12
|
-
:
|
|
13
|
+
:items-deleted="showDeleted"
|
|
14
|
+
:on-file-loaded="handleFileUpload"
|
|
13
15
|
>
|
|
14
|
-
<template #
|
|
16
|
+
<template #toolbar-leading>
|
|
15
17
|
<v-tooltip
|
|
16
18
|
:text="showDeleted ? $t('core.hideDeleted') : $t('core.showDeleted')"
|
|
17
19
|
>
|
|
18
|
-
<template
|
|
20
|
+
<template #activator="{ props: activatorProps }">
|
|
19
21
|
<v-btn-toggle
|
|
20
22
|
density="compact"
|
|
21
|
-
v-bind="
|
|
23
|
+
v-bind="activatorProps"
|
|
22
24
|
v-model="showDeleted"
|
|
23
25
|
mandatory
|
|
24
26
|
:color="showDeleted ? 'error' : 'success'"
|
|
@@ -30,29 +32,9 @@
|
|
|
30
32
|
</v-btn-toggle>
|
|
31
33
|
</template>
|
|
32
34
|
</v-tooltip>
|
|
33
|
-
<v-btn-group density="compact">
|
|
34
|
-
<v-tooltip :text="$t('core.export')">
|
|
35
|
-
<template v-slot:activator="{ props }">
|
|
36
|
-
<v-btn color="info" v-bind="props" flat @click="openExportDialog">
|
|
37
|
-
<v-icon>mdi-database-export</v-icon>
|
|
38
|
-
</v-btn>
|
|
39
|
-
</template>
|
|
40
|
-
</v-tooltip>
|
|
41
|
-
<v-tooltip :text="$t('core.import')">
|
|
42
|
-
<template v-slot:activator="{ props }">
|
|
43
|
-
<v-btn v-bind="props" color="warning">
|
|
44
|
-
<FileUpload
|
|
45
|
-
ref="uploadRef"
|
|
46
|
-
:onFileLoaded="handleFileUpload"
|
|
47
|
-
accept=".json"
|
|
48
|
-
readAs="text"
|
|
49
|
-
/></v-btn>
|
|
50
|
-
</template>
|
|
51
|
-
</v-tooltip>
|
|
52
|
-
</v-btn-group>
|
|
53
35
|
</template>
|
|
54
36
|
<!-- Custom status column -->
|
|
55
|
-
<template #item.status="{ item }">
|
|
37
|
+
<template #[`item.status`]="{ item }">
|
|
56
38
|
<v-icon
|
|
57
39
|
:icon="item.statusIcon"
|
|
58
40
|
:color="item.statusColor"
|
|
@@ -74,18 +56,8 @@
|
|
|
74
56
|
/>
|
|
75
57
|
</template>
|
|
76
58
|
</BaseDataTable>
|
|
77
|
-
<!-- Export Selection Dialog -->
|
|
78
|
-
<SelectionDialog
|
|
79
|
-
v-model="showExportDialog"
|
|
80
|
-
:items="exportableItems"
|
|
81
|
-
display-attribute="title"
|
|
82
|
-
:title="$t('core.selectItemsToExport')"
|
|
83
|
-
:confirm-text="$t('core.export')"
|
|
84
|
-
@confirm="handleExportConfirm"
|
|
85
|
-
/>
|
|
86
|
-
|
|
87
59
|
<!-- Import Selection Dialog -->
|
|
88
|
-
<
|
|
60
|
+
<ImportSelectionDialog
|
|
89
61
|
v-model="showImportDialog"
|
|
90
62
|
:items="importableItems"
|
|
91
63
|
display-attribute="title"
|
|
@@ -94,18 +66,8 @@
|
|
|
94
66
|
@confirm="handleImportConfirm"
|
|
95
67
|
/>
|
|
96
68
|
|
|
97
|
-
<!-- Export Summary Dialog -->
|
|
98
|
-
<SummaryDialog
|
|
99
|
-
v-model="showExportSummary"
|
|
100
|
-
display-attribute="title"
|
|
101
|
-
:items="exportedItems"
|
|
102
|
-
:title="$t('core.exportSummary')"
|
|
103
|
-
:message="$t('core.exportSuccessMessage')"
|
|
104
|
-
type="success"
|
|
105
|
-
/>
|
|
106
|
-
|
|
107
69
|
<!-- Import Summary Dialog -->
|
|
108
|
-
<
|
|
70
|
+
<ImportSummaryDialog
|
|
109
71
|
v-model="showImportSummary"
|
|
110
72
|
display-attribute="title"
|
|
111
73
|
:items="importedItems"
|
|
@@ -121,31 +83,27 @@
|
|
|
121
83
|
|
|
122
84
|
<script setup lang="ts">
|
|
123
85
|
import { usePluginApi } from "@raclettejs/core/orchestrator/composables"
|
|
124
|
-
import { computed, watch, ref
|
|
86
|
+
import { computed, watch, ref } from "vue"
|
|
125
87
|
import type { Composition } from "@raclettejs/core"
|
|
126
88
|
import { formatDistance } from "date-fns"
|
|
127
|
-
import BaseDataTable from "@
|
|
89
|
+
import { BaseDataTable } from "@raclettejs/core/orchestrator/components"
|
|
128
90
|
import { useI18n } from "vue-i18n"
|
|
129
|
-
import {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
91
|
+
import {
|
|
92
|
+
mapJsonValues,
|
|
93
|
+
parseAndNormalizeWorkbenchImportJson,
|
|
94
|
+
resolveWorkbenchImportSelection,
|
|
95
|
+
} from "@app/lib/jsonTools"
|
|
96
|
+
import { ImportSelectionDialog } from "@raclettejs/core/orchestrator/components"
|
|
97
|
+
import { ImportSummaryDialog } from "@raclettejs/core/orchestrator/components"
|
|
133
98
|
|
|
134
99
|
const { t } = useI18n()
|
|
135
100
|
const showDeleted = ref(false)
|
|
136
|
-
// Dialog states
|
|
137
|
-
const showExportDialog = ref(false)
|
|
138
101
|
const showImportDialog = ref(false)
|
|
139
|
-
const showExportSummary = ref(false)
|
|
140
102
|
const showImportSummary = ref(false)
|
|
141
103
|
|
|
142
|
-
// Data for dialogs
|
|
143
|
-
const exportableItems = ref<any[]>([])
|
|
144
104
|
const importableItems = ref<any[]>([])
|
|
145
|
-
const exportedItems = ref<any[]>([])
|
|
146
105
|
const importedItems = ref<any[]>([])
|
|
147
|
-
const
|
|
148
|
-
const uploadRef = useTemplateRef("uploadRef")
|
|
106
|
+
const pendingImportById = ref<Record<string, any>>({})
|
|
149
107
|
|
|
150
108
|
const headers = computed<
|
|
151
109
|
{ title: string; key: keyof Composition | "actions" }[]
|
|
@@ -204,25 +162,39 @@ const { execute: executeRestore } = await $data.composition.updateRestore({
|
|
|
204
162
|
const { execute: executeHardDelete } = await $data.composition.deleteHard({
|
|
205
163
|
options: { cb: () => execute({ isDeleted: showDeleted.value }) },
|
|
206
164
|
})
|
|
207
|
-
const
|
|
208
|
-
if (compositions.value
|
|
209
|
-
return
|
|
210
|
-
compositions.value as { [key: string]: Composition },
|
|
211
|
-
).map((composition) => ({
|
|
212
|
-
...composition,
|
|
213
|
-
tags: composition.tags.map((tag) => tags.value?.[tag]),
|
|
214
|
-
lastEditor: users.value?.[composition.author]?.email,
|
|
215
|
-
author: users.value?.[composition.author]?.email,
|
|
216
|
-
status: composition.status, // TODO: parse status
|
|
217
|
-
statusIcon: getStatusIcon(composition.status),
|
|
218
|
-
statusColor: getStatusColor(composition.status),
|
|
219
|
-
createdAt: formatDistance(new Date(composition.createdAt), new Date()),
|
|
220
|
-
updatedAt: formatDistance(new Date(composition.updatedAt), new Date()),
|
|
221
|
-
}))
|
|
165
|
+
const compositionTableRows = (relativeDates: boolean) => {
|
|
166
|
+
if (!compositions.value || !tags.value) {
|
|
167
|
+
return []
|
|
222
168
|
}
|
|
223
169
|
|
|
224
|
-
return
|
|
225
|
-
}
|
|
170
|
+
return Object.values(
|
|
171
|
+
compositions.value as { [key: string]: Composition },
|
|
172
|
+
).map((composition) => ({
|
|
173
|
+
...composition,
|
|
174
|
+
tags: composition.tags.map((tag) => tags.value?.[tag]),
|
|
175
|
+
lastEditor: users.value?.[composition.author]?.email,
|
|
176
|
+
author: users.value?.[composition.author]?.email,
|
|
177
|
+
status: composition.status, // TODO: parse status
|
|
178
|
+
statusIcon: getStatusIcon(composition.status),
|
|
179
|
+
statusColor: getStatusColor(composition.status),
|
|
180
|
+
...(relativeDates
|
|
181
|
+
? {
|
|
182
|
+
createdAt: formatDistance(
|
|
183
|
+
new Date(composition.createdAt),
|
|
184
|
+
new Date(),
|
|
185
|
+
),
|
|
186
|
+
updatedAt: formatDistance(
|
|
187
|
+
new Date(composition.updatedAt),
|
|
188
|
+
new Date(),
|
|
189
|
+
),
|
|
190
|
+
}
|
|
191
|
+
: {}),
|
|
192
|
+
}))
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const parsedCompositions = computed(() => compositionTableRows(true))
|
|
196
|
+
|
|
197
|
+
const exportableCompositions = computed(() => compositionTableRows(false))
|
|
226
198
|
|
|
227
199
|
// TODO: replace with composition status type
|
|
228
200
|
const getStatusIcon = (status: string) => {
|
|
@@ -246,42 +218,26 @@ const getStatusColor = (status: string) => {
|
|
|
246
218
|
return "#B66218"
|
|
247
219
|
}
|
|
248
220
|
}
|
|
249
|
-
// Export functions
|
|
250
|
-
const openExportDialog = () => {
|
|
251
|
-
exportableItems.value = Object.values(compositions.value || {})
|
|
252
|
-
showExportDialog.value = true
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const handleExportConfirm = (selectedIds: string[]) => {
|
|
256
|
-
const dataToExport = selectedIds.reduce((acc, id) => {
|
|
257
|
-
if (compositions.value?.[id]) {
|
|
258
|
-
acc[id] = compositions.value[id]
|
|
259
|
-
}
|
|
260
|
-
return acc
|
|
261
|
-
}, {} as any)
|
|
262
|
-
|
|
263
|
-
exportedItems.value = Object.values(dataToExport)
|
|
264
|
-
downloadJson(dataToExport, "compositions")
|
|
265
|
-
showExportSummary.value = true
|
|
266
|
-
}
|
|
267
|
-
|
|
268
221
|
// Import functions
|
|
269
|
-
const handleFileUpload = (content: string
|
|
222
|
+
const handleFileUpload = (content: string | ArrayBuffer) => {
|
|
223
|
+
if (typeof content !== "string") {
|
|
224
|
+
return
|
|
225
|
+
}
|
|
270
226
|
try {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
importableItems.value = itemsArray
|
|
275
|
-
pendingImportData.value = parsedData
|
|
227
|
+
const { items, byId } = parseAndNormalizeWorkbenchImportJson(content)
|
|
228
|
+
importableItems.value = items
|
|
229
|
+
pendingImportById.value = byId
|
|
276
230
|
showImportDialog.value = true
|
|
277
231
|
} catch (error) {
|
|
278
|
-
console.error("Failed to
|
|
232
|
+
console.error("Failed to prepare import:", error)
|
|
279
233
|
}
|
|
280
234
|
}
|
|
281
235
|
|
|
282
236
|
const handleImportConfirm = (selectedIds: string[]) => {
|
|
283
|
-
|
|
284
|
-
|
|
237
|
+
importedItems.value = resolveWorkbenchImportSelection(
|
|
238
|
+
pendingImportById.value,
|
|
239
|
+
selectedIds,
|
|
240
|
+
)
|
|
285
241
|
showImportSummary.value = true
|
|
286
242
|
}
|
|
287
243
|
|
|
@@ -303,7 +259,7 @@ const finalizeImport = async () => {
|
|
|
303
259
|
// e.g., call an API endpoint to save the imported compositions
|
|
304
260
|
|
|
305
261
|
// Reset states
|
|
306
|
-
|
|
262
|
+
pendingImportById.value = {}
|
|
307
263
|
importableItems.value = []
|
|
308
264
|
importedItems.value = []
|
|
309
265
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<div>
|
|
3
3
|
<BaseDataTable
|
|
4
4
|
:items="parsedInteractionLinks"
|
|
5
|
+
:exportable-items="exportableInteractionLinks"
|
|
5
6
|
:loading="interactionLinksLoading || tagsLoading"
|
|
6
7
|
:headers="headers"
|
|
7
8
|
create-interaction-link-id="interactionLinkCreateInteractionLink"
|
|
@@ -9,16 +10,17 @@
|
|
|
9
10
|
:data-name="$t('workbench.interactionLinkList.dataName')"
|
|
10
11
|
:data-article="$t('workbench.interactionLinkList.dataArticle')"
|
|
11
12
|
@delete="deleteInteractionLink"
|
|
12
|
-
:
|
|
13
|
+
:items-deleted="showDeleted"
|
|
14
|
+
:on-file-loaded="handleFileUpload"
|
|
13
15
|
>
|
|
14
|
-
<template #
|
|
16
|
+
<template #toolbar-leading>
|
|
15
17
|
<v-tooltip
|
|
16
18
|
:text="showDeleted ? $t('core.hideDeleted') : $t('core.showDeleted')"
|
|
17
19
|
>
|
|
18
|
-
<template
|
|
20
|
+
<template #activator="{ props: activatorProps }">
|
|
19
21
|
<v-btn-toggle
|
|
20
22
|
density="compact"
|
|
21
|
-
v-bind="
|
|
23
|
+
v-bind="activatorProps"
|
|
22
24
|
v-model="showDeleted"
|
|
23
25
|
mandatory
|
|
24
26
|
:color="showDeleted ? 'error' : 'success'"
|
|
@@ -30,29 +32,9 @@
|
|
|
30
32
|
</v-btn-toggle>
|
|
31
33
|
</template>
|
|
32
34
|
</v-tooltip>
|
|
33
|
-
<v-btn-group density="compact">
|
|
34
|
-
<v-tooltip :text="$t('core.export')">
|
|
35
|
-
<template v-slot:activator="{ props }">
|
|
36
|
-
<v-btn color="info" v-bind="props" flat @click="openExportDialog">
|
|
37
|
-
<v-icon>mdi-database-export</v-icon>
|
|
38
|
-
</v-btn>
|
|
39
|
-
</template>
|
|
40
|
-
</v-tooltip>
|
|
41
|
-
<v-tooltip :text="$t('core.import')">
|
|
42
|
-
<template v-slot:activator="{ props }">
|
|
43
|
-
<v-btn v-bind="props" color="warning">
|
|
44
|
-
<FileUpload
|
|
45
|
-
ref="uploadRef"
|
|
46
|
-
:onFileLoaded="handleFileUpload"
|
|
47
|
-
accept=".json"
|
|
48
|
-
readAs="text"
|
|
49
|
-
/></v-btn>
|
|
50
|
-
</template>
|
|
51
|
-
</v-tooltip>
|
|
52
|
-
</v-btn-group>
|
|
53
35
|
</template>
|
|
54
36
|
<!-- Custom triggers column -->
|
|
55
|
-
<template #item.triggers="{ item }">
|
|
37
|
+
<template #[`item.triggers`]="{ item }">
|
|
56
38
|
<span class="tw:flex tw:gap-2">
|
|
57
39
|
<span>{{ item.triggers }}</span>
|
|
58
40
|
<v-icon icon="mdi-link" />
|
|
@@ -73,18 +55,8 @@
|
|
|
73
55
|
/>
|
|
74
56
|
</template>
|
|
75
57
|
</BaseDataTable>
|
|
76
|
-
<!-- Export Selection Dialog -->
|
|
77
|
-
<SelectionDialog
|
|
78
|
-
v-model="showExportDialog"
|
|
79
|
-
:items="exportableItems"
|
|
80
|
-
display-attribute="description"
|
|
81
|
-
:title="$t('core.selectItemsToExport')"
|
|
82
|
-
:confirm-text="$t('core.export')"
|
|
83
|
-
@confirm="handleExportConfirm"
|
|
84
|
-
/>
|
|
85
|
-
|
|
86
58
|
<!-- Import Selection Dialog -->
|
|
87
|
-
<
|
|
59
|
+
<ImportSelectionDialog
|
|
88
60
|
v-model="showImportDialog"
|
|
89
61
|
:items="importableItems"
|
|
90
62
|
display-attribute="description"
|
|
@@ -93,18 +65,8 @@
|
|
|
93
65
|
@confirm="handleImportConfirm"
|
|
94
66
|
/>
|
|
95
67
|
|
|
96
|
-
<!-- Export Summary Dialog -->
|
|
97
|
-
<SummaryDialog
|
|
98
|
-
v-model="showExportSummary"
|
|
99
|
-
display-attribute="description"
|
|
100
|
-
:items="exportedItems"
|
|
101
|
-
:title="$t('core.exportSummary')"
|
|
102
|
-
:message="$t('core.exportSuccessMessage')"
|
|
103
|
-
type="success"
|
|
104
|
-
/>
|
|
105
|
-
|
|
106
68
|
<!-- Import Summary Dialog -->
|
|
107
|
-
<
|
|
69
|
+
<ImportSummaryDialog
|
|
108
70
|
v-model="showImportSummary"
|
|
109
71
|
:items="importedItems"
|
|
110
72
|
display-attribute="description"
|
|
@@ -119,31 +81,27 @@
|
|
|
119
81
|
</template>
|
|
120
82
|
|
|
121
83
|
<script setup lang="ts">
|
|
122
|
-
import {
|
|
123
|
-
import BaseDataTable from "@app/components/BaseDataTable.vue"
|
|
84
|
+
import { BaseDataTable } from "@raclettejs/core/orchestrator/components"
|
|
124
85
|
import { InteractionLink } from "@raclettejs/core"
|
|
125
86
|
import { usePluginApi } from "@raclettejs/core/orchestrator/composables"
|
|
126
87
|
import { formatDistance } from "date-fns"
|
|
127
|
-
import { computed, ref, watch
|
|
88
|
+
import { computed, ref, watch } from "vue"
|
|
128
89
|
import { useI18n } from "vue-i18n"
|
|
129
|
-
import {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
90
|
+
import {
|
|
91
|
+
mapJsonValues,
|
|
92
|
+
parseAndNormalizeWorkbenchImportJson,
|
|
93
|
+
resolveWorkbenchImportSelection,
|
|
94
|
+
} from "@app/lib/jsonTools"
|
|
95
|
+
import { ImportSelectionDialog } from "@raclettejs/core/orchestrator/components"
|
|
96
|
+
import { ImportSummaryDialog } from "@raclettejs/core/orchestrator/components"
|
|
97
|
+
|
|
133
98
|
const showDeleted = ref(false)
|
|
134
|
-
// Dialog states
|
|
135
|
-
const showExportDialog = ref(false)
|
|
136
99
|
const showImportDialog = ref(false)
|
|
137
|
-
const showExportSummary = ref(false)
|
|
138
100
|
const showImportSummary = ref(false)
|
|
139
101
|
|
|
140
|
-
// Data for dialogs
|
|
141
|
-
const exportableItems = ref<any[]>([])
|
|
142
102
|
const importableItems = ref<any[]>([])
|
|
143
|
-
const exportedItems = ref<any[]>([])
|
|
144
103
|
const importedItems = ref<any[]>([])
|
|
145
|
-
const
|
|
146
|
-
const uploadRef = useTemplateRef("uploadRef")
|
|
104
|
+
const pendingImportById = ref<Record<string, any>>({})
|
|
147
105
|
|
|
148
106
|
const { t } = useI18n()
|
|
149
107
|
|
|
@@ -217,32 +175,40 @@ const { execute: executeHardDelete } = await $data.interactionLink.deleteHard({
|
|
|
217
175
|
const { execute: executeRestore } = await $data.interactionLink.updateRestore({
|
|
218
176
|
options: { cb: () => execute({ isDeleted: showDeleted.value }) },
|
|
219
177
|
})
|
|
220
|
-
const
|
|
221
|
-
if (interactionLinks.value
|
|
222
|
-
return
|
|
223
|
-
interactionLinks.value as { [key: string]: InteractionLink },
|
|
224
|
-
).map((interactionLink) => {
|
|
225
|
-
return {
|
|
226
|
-
...interactionLink,
|
|
227
|
-
composition: compositions.value?.[interactionLink.composition]?.title,
|
|
228
|
-
tags: interactionLink.tags.map((tag) => tags.value?.[tag]),
|
|
229
|
-
lastEditor: users.value?.[interactionLink.author]?.email,
|
|
230
|
-
author: users.value?.[interactionLink.author]?.email,
|
|
231
|
-
triggers: interactionLink.triggers.length,
|
|
232
|
-
createdAt: formatDistance(
|
|
233
|
-
new Date(interactionLink.createdAt),
|
|
234
|
-
new Date(),
|
|
235
|
-
),
|
|
236
|
-
updatedAt: formatDistance(
|
|
237
|
-
new Date(interactionLink.updatedAt),
|
|
238
|
-
new Date(),
|
|
239
|
-
),
|
|
240
|
-
}
|
|
241
|
-
})
|
|
178
|
+
const interactionLinkTableRows = (relativeDates: boolean) => {
|
|
179
|
+
if (!interactionLinks.value || !tags.value) {
|
|
180
|
+
return []
|
|
242
181
|
}
|
|
243
182
|
|
|
244
|
-
return
|
|
245
|
-
}
|
|
183
|
+
return Object.values(
|
|
184
|
+
interactionLinks.value as { [key: string]: InteractionLink },
|
|
185
|
+
).map((interactionLink) => ({
|
|
186
|
+
...interactionLink,
|
|
187
|
+
composition: compositions.value?.[interactionLink.composition]?.title,
|
|
188
|
+
tags: interactionLink.tags.map((tag) => tags.value?.[tag]),
|
|
189
|
+
lastEditor: users.value?.[interactionLink.author]?.email,
|
|
190
|
+
author: users.value?.[interactionLink.author]?.email,
|
|
191
|
+
triggers: interactionLink.triggers.length,
|
|
192
|
+
...(relativeDates
|
|
193
|
+
? {
|
|
194
|
+
createdAt: formatDistance(
|
|
195
|
+
new Date(interactionLink.createdAt),
|
|
196
|
+
new Date(),
|
|
197
|
+
),
|
|
198
|
+
updatedAt: formatDistance(
|
|
199
|
+
new Date(interactionLink.updatedAt),
|
|
200
|
+
new Date(),
|
|
201
|
+
),
|
|
202
|
+
}
|
|
203
|
+
: {}),
|
|
204
|
+
}))
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const parsedInteractionLinks = computed(() => interactionLinkTableRows(true))
|
|
208
|
+
|
|
209
|
+
const exportableInteractionLinks = computed(() =>
|
|
210
|
+
interactionLinkTableRows(false),
|
|
211
|
+
)
|
|
246
212
|
const restore = async (_id: string) => {
|
|
247
213
|
executeRestore({ _id })
|
|
248
214
|
}
|
|
@@ -267,50 +233,27 @@ const createTitleString = (link) => {
|
|
|
267
233
|
link.description = linkTitleString + link.description
|
|
268
234
|
return link
|
|
269
235
|
}
|
|
270
|
-
const createLinkDescriptions = (linkArr) => {
|
|
271
|
-
linkArr.forEach((link) => {
|
|
272
|
-
link = createTitleString(link)
|
|
273
|
-
})
|
|
274
|
-
return linkArr
|
|
275
|
-
}
|
|
276
|
-
// Export functions
|
|
277
|
-
const openExportDialog = () => {
|
|
278
|
-
exportableItems.value = createLinkDescriptions(
|
|
279
|
-
Object.values(clone(interactionLinks.value) || {}),
|
|
280
|
-
)
|
|
281
|
-
showExportDialog.value = true
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const handleExportConfirm = (selectedIds: string[]) => {
|
|
285
|
-
const _interactionLinks = clone(interactionLinks.value)
|
|
286
|
-
const dataToExport = clone(selectedIds).reduce((acc, id) => {
|
|
287
|
-
if (_interactionLinks?.[id]) {
|
|
288
|
-
acc[id] = createTitleString(_interactionLinks[id])
|
|
289
|
-
}
|
|
290
|
-
return acc
|
|
291
|
-
}, {} as any)
|
|
292
|
-
exportedItems.value = Object.values(dataToExport)
|
|
293
|
-
downloadJson(dataToExport, "interactionLinks")
|
|
294
|
-
showExportSummary.value = true
|
|
295
|
-
}
|
|
296
236
|
|
|
297
237
|
// Import functions
|
|
298
|
-
const handleFileUpload = (content: string
|
|
238
|
+
const handleFileUpload = (content: string | ArrayBuffer) => {
|
|
239
|
+
if (typeof content !== "string") {
|
|
240
|
+
return
|
|
241
|
+
}
|
|
299
242
|
try {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
importableItems.value = itemsArray
|
|
304
|
-
pendingImportData.value = parsedData
|
|
243
|
+
const { items, byId } = parseAndNormalizeWorkbenchImportJson(content)
|
|
244
|
+
importableItems.value = items
|
|
245
|
+
pendingImportById.value = byId
|
|
305
246
|
showImportDialog.value = true
|
|
306
247
|
} catch (error) {
|
|
307
|
-
console.error("Failed to
|
|
248
|
+
console.error("Failed to prepare import:", error)
|
|
308
249
|
}
|
|
309
250
|
}
|
|
310
251
|
|
|
311
252
|
const handleImportConfirm = (selectedIds: string[]) => {
|
|
312
|
-
|
|
313
|
-
|
|
253
|
+
importedItems.value = resolveWorkbenchImportSelection(
|
|
254
|
+
pendingImportById.value,
|
|
255
|
+
selectedIds,
|
|
256
|
+
)
|
|
314
257
|
showImportSummary.value = true
|
|
315
258
|
}
|
|
316
259
|
|
|
@@ -332,7 +275,7 @@ const finalizeImport = async () => {
|
|
|
332
275
|
// e.g., call an API endpoint to save the imported compositions
|
|
333
276
|
|
|
334
277
|
// Reset states
|
|
335
|
-
|
|
278
|
+
pendingImportById.value = {}
|
|
336
279
|
importableItems.value = []
|
|
337
280
|
importedItems.value = []
|
|
338
281
|
}
|