@raclettejs/workbench 0.1.32 → 0.1.33-canary.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/CHANGELOG.md CHANGED
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ - set eslint version to latest
11
+
10
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>
11
13
 
12
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>
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.32",
3
+ "version": "0.1.33-canary.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.32",
36
+ "@raclettejs/core": "^0.1.33-canary.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.32",
46
+ "@raclettejs/types": "^0.1.33-canary.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
- :itemsDeleted="showDeleted"
13
+ :items-deleted="showDeleted"
14
+ :on-file-loaded="handleFileUpload"
13
15
  >
14
- <template #actions>
16
+ <template #toolbar-leading>
15
17
  <v-tooltip
16
18
  :text="showDeleted ? $t('core.hideDeleted') : $t('core.showDeleted')"
17
19
  >
18
- <template v-slot:activator="{ props }">
20
+ <template #activator="{ props: activatorProps }">
19
21
  <v-btn-toggle
20
22
  density="compact"
21
- v-bind="props"
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
- <SelectionDialog
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
- <SummaryDialog
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, useTemplateRef } from "vue"
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 "@app/components/BaseDataTable.vue"
89
+ import BaseDataTable from "@raclettejs/core/orchestrator/components/dataTable/BaseDataTable.vue"
128
90
  import { useI18n } from "vue-i18n"
129
- import { downloadJson, mapJsonValues } from "@app/lib/jsonTools"
130
- import FileUpload from "@app/components/FileUpload.vue"
131
- import SelectionDialog from "@app/components/SelectionDialog.vue"
132
- import SummaryDialog from "@app/components/SummaryDialog.vue"
91
+ import {
92
+ mapJsonValues,
93
+ parseAndNormalizeWorkbenchImportJson,
94
+ resolveWorkbenchImportSelection,
95
+ } from "@app/lib/jsonTools"
96
+ import ImportSelectionDialog from "@raclettejs/core/orchestrator/components/dataImport/ImportSelectionDialog.vue"
97
+ import ImportSummaryDialog from "@raclettejs/core/orchestrator/components/dataImport/ImportSummaryDialog.vue"
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 pendingImportData = ref<any>(null)
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 parsedCompositions = computed(() => {
208
- if (compositions.value && tags.value) {
209
- return Object.values(
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, file: File) => {
222
+ const handleFileUpload = (content: string | ArrayBuffer) => {
223
+ if (typeof content !== "string") {
224
+ return
225
+ }
270
226
  try {
271
- const parsedData = JSON.parse(content)
272
- const itemsArray = Object.values(parsedData)
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 parse JSON:", error)
232
+ console.error("Failed to prepare import:", error)
279
233
  }
280
234
  }
281
235
 
282
236
  const handleImportConfirm = (selectedIds: string[]) => {
283
- const selectedItems = selectedIds.map((id) => pendingImportData.value[id])
284
- importedItems.value = selectedItems
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
- pendingImportData.value = null
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
- :itemsDeleted="showDeleted"
13
+ :items-deleted="showDeleted"
14
+ :on-file-loaded="handleFileUpload"
13
15
  >
14
- <template #actions>
16
+ <template #toolbar-leading>
15
17
  <v-tooltip
16
18
  :text="showDeleted ? $t('core.hideDeleted') : $t('core.showDeleted')"
17
19
  >
18
- <template v-slot:activator="{ props }">
20
+ <template #activator="{ props: activatorProps }">
19
21
  <v-btn-toggle
20
22
  density="compact"
21
- v-bind="props"
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
- <SelectionDialog
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
- <SummaryDialog
69
+ <ImportSummaryDialog
108
70
  v-model="showImportSummary"
109
71
  :items="importedItems"
110
72
  display-attribute="description"
@@ -119,31 +81,26 @@
119
81
  </template>
120
82
 
121
83
  <script setup lang="ts">
122
- import { clone } from "ramda"
123
- import BaseDataTable from "@app/components/BaseDataTable.vue"
84
+ import BaseDataTable from "@raclettejs/core/orchestrator/components/dataTable/BaseDataTable.vue"
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, useTemplateRef } from "vue"
88
+ import { computed, ref, watch } from "vue"
128
89
  import { useI18n } from "vue-i18n"
129
- import { downloadJson, mapJsonValues } from "@app/lib/jsonTools"
130
- import FileUpload from "@app/components/FileUpload.vue"
131
- import SelectionDialog from "@app/components/SelectionDialog.vue"
132
- import SummaryDialog from "@app/components/SummaryDialog.vue"
90
+ import {
91
+ mapJsonValues,
92
+ parseAndNormalizeWorkbenchImportJson,
93
+ resolveWorkbenchImportSelection,
94
+ } from "@app/lib/jsonTools"
95
+ import ImportSelectionDialog from "@raclettejs/core/orchestrator/components/dataImport/ImportSelectionDialog.vue"
96
+ import ImportSummaryDialog from "@raclettejs/core/orchestrator/components/dataImport/ImportSummaryDialog.vue"
133
97
  const showDeleted = ref(false)
134
- // Dialog states
135
- const showExportDialog = ref(false)
136
98
  const showImportDialog = ref(false)
137
- const showExportSummary = ref(false)
138
99
  const showImportSummary = ref(false)
139
100
 
140
- // Data for dialogs
141
- const exportableItems = ref<any[]>([])
142
101
  const importableItems = ref<any[]>([])
143
- const exportedItems = ref<any[]>([])
144
102
  const importedItems = ref<any[]>([])
145
- const pendingImportData = ref<any>(null)
146
- const uploadRef = useTemplateRef("uploadRef")
103
+ const pendingImportById = ref<Record<string, any>>({})
147
104
 
148
105
  const { t } = useI18n()
149
106
 
@@ -217,32 +174,40 @@ const { execute: executeHardDelete } = await $data.interactionLink.deleteHard({
217
174
  const { execute: executeRestore } = await $data.interactionLink.updateRestore({
218
175
  options: { cb: () => execute({ isDeleted: showDeleted.value }) },
219
176
  })
220
- const parsedInteractionLinks = computed(() => {
221
- if (interactionLinks.value && tags.value) {
222
- return Object.values(
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
- })
177
+ const interactionLinkTableRows = (relativeDates: boolean) => {
178
+ if (!interactionLinks.value || !tags.value) {
179
+ return []
242
180
  }
243
181
 
244
- return []
245
- })
182
+ return Object.values(
183
+ interactionLinks.value as { [key: string]: InteractionLink },
184
+ ).map((interactionLink) => ({
185
+ ...interactionLink,
186
+ composition: compositions.value?.[interactionLink.composition]?.title,
187
+ tags: interactionLink.tags.map((tag) => tags.value?.[tag]),
188
+ lastEditor: users.value?.[interactionLink.author]?.email,
189
+ author: users.value?.[interactionLink.author]?.email,
190
+ triggers: interactionLink.triggers.length,
191
+ ...(relativeDates
192
+ ? {
193
+ createdAt: formatDistance(
194
+ new Date(interactionLink.createdAt),
195
+ new Date(),
196
+ ),
197
+ updatedAt: formatDistance(
198
+ new Date(interactionLink.updatedAt),
199
+ new Date(),
200
+ ),
201
+ }
202
+ : {}),
203
+ }))
204
+ }
205
+
206
+ const parsedInteractionLinks = computed(() => interactionLinkTableRows(true))
207
+
208
+ const exportableInteractionLinks = computed(() =>
209
+ interactionLinkTableRows(false),
210
+ )
246
211
  const restore = async (_id: string) => {
247
212
  executeRestore({ _id })
248
213
  }
@@ -267,50 +232,27 @@ const createTitleString = (link) => {
267
232
  link.description = linkTitleString + link.description
268
233
  return link
269
234
  }
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
235
 
297
236
  // Import functions
298
- const handleFileUpload = (content: string, file: File) => {
237
+ const handleFileUpload = (content: string | ArrayBuffer) => {
238
+ if (typeof content !== "string") {
239
+ return
240
+ }
299
241
  try {
300
- const parsedData = JSON.parse(content)
301
- const itemsArray = Object.values(parsedData)
302
-
303
- importableItems.value = itemsArray
304
- pendingImportData.value = parsedData
242
+ const { items, byId } = parseAndNormalizeWorkbenchImportJson(content)
243
+ importableItems.value = items
244
+ pendingImportById.value = byId
305
245
  showImportDialog.value = true
306
246
  } catch (error) {
307
- console.error("Failed to parse JSON:", error)
247
+ console.error("Failed to prepare import:", error)
308
248
  }
309
249
  }
310
250
 
311
251
  const handleImportConfirm = (selectedIds: string[]) => {
312
- const selectedItems = selectedIds.map((id) => pendingImportData.value[id])
313
- importedItems.value = selectedItems
252
+ importedItems.value = resolveWorkbenchImportSelection(
253
+ pendingImportById.value,
254
+ selectedIds,
255
+ )
314
256
  showImportSummary.value = true
315
257
  }
316
258
 
@@ -332,7 +274,7 @@ const finalizeImport = async () => {
332
274
  // e.g., call an API endpoint to save the imported compositions
333
275
 
334
276
  // Reset states
335
- pendingImportData.value = null
277
+ pendingImportById.value = {}
336
278
  importableItems.value = []
337
279
  importedItems.value = []
338
280
  }
@@ -8,20 +8,20 @@
8
8
  :show-delete-action="false"
9
9
  :show-actions-column="false"
10
10
  :row-click-handler="onRowClick"
11
+ :show-export="false"
11
12
  >
12
13
  </BaseDataTable>
13
14
  </template>
14
15
 
15
16
  <script setup lang="ts">
16
17
  import useWorkbenchPlugins from "@app/composables/useWorkbenchPlugins"
17
- import BaseDataTable from "@app/components/BaseDataTable.vue"
18
+ import BaseDataTable from "@raclettejs/core/orchestrator/components/dataTable/BaseDataTable.vue"
18
19
  import type { PluginMetadata } from "@raclettejs/core"
19
20
  import { computed } from "vue"
20
21
  import {
21
22
  usePluginApi,
22
23
  useRouteState,
23
24
  } from "@raclettejs/core/orchestrator/composables"
24
- import { DEFAULT_SLOT_NAME } from "@raclettejs/core/orchestrator/router/routeParserHelper"
25
25
 
26
26
  const { pluginMetadataFiles, hasPluginSettingsWidget } = useWorkbenchPlugins()
27
27