@raclettejs/workbench 0.1.17 → 0.1.19

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,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.19] - 12.12.2025 <a href="https://gitlab.com/raclettejs/workbench/-/compare/v0.1.18...v0.1.19" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
11
+
12
+ ### Added
13
+
14
+ - Compositions, interactionLinks and tags can now be imported and exported
15
+
16
+ ## [0.1.18] - 05.12.2025 <a href="https://gitlab.com/raclettejs/workbench/-/compare/v0.1.17...v0.1.18" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
17
+
18
+ ### Removed
19
+
20
+ - Removed redundand authTokenExpiryDays config
21
+
22
+ ### Added
23
+
24
+ - Interaction Links can now have a trigger in the appbar
25
+ - possibility to restore items
26
+ - possibility to add prepend and append actions to base table row items
27
+ - added project configuration interface
28
+
10
29
  ## [0.1.17] - 06.11.2025 <a href="https://gitlab.com/raclettejs/workbench/-/compare/v0.1.15...v0.1.17" target="_blank" rel="noopener"><b>Overview of all changes</b></a>
11
30
 
12
31
  ### Added
@@ -2,7 +2,6 @@ export default [
2
2
  // composition administration
3
3
  {
4
4
  _id: "compositionList",
5
- description: "Ein Test",
6
5
  pathname: {
7
6
  default: "composition-list",
8
7
  },
@@ -21,7 +20,6 @@ export default [
21
20
  },
22
21
  {
23
22
  _id: "compositionCreate",
24
- description: "Ein Test",
25
23
  pathname: {
26
24
  default: "composition-create",
27
25
  },
@@ -41,7 +39,6 @@ export default [
41
39
  },
42
40
  {
43
41
  _id: "compositionEdit",
44
- description: "Ein Test",
45
42
  pathname: {
46
43
  default: "composition-edit",
47
44
  },
@@ -63,7 +60,6 @@ export default [
63
60
  // interactionLinks administration
64
61
  {
65
62
  _id: "interactionLinkList",
66
- description: "Ein Test",
67
63
  pathname: {
68
64
  default: "interactionLink-list",
69
65
  },
@@ -82,7 +78,6 @@ export default [
82
78
  },
83
79
  {
84
80
  _id: "interactionLinkCreate",
85
- description: "Ein Test",
86
81
  pathname: {
87
82
  default: "interactionLink-create",
88
83
  },
@@ -101,7 +96,6 @@ export default [
101
96
  },
102
97
  {
103
98
  _id: "interactionLinkEdit",
104
- description: "Ein Test",
105
99
  pathname: {
106
100
  default: "interactionLink-edit",
107
101
  },
@@ -123,7 +117,6 @@ export default [
123
117
  // users administration
124
118
  {
125
119
  _id: "userList",
126
- description: "Ein Test",
127
120
  pathname: {
128
121
  default: "user-list",
129
122
  },
@@ -142,7 +135,6 @@ export default [
142
135
  },
143
136
  {
144
137
  _id: "userCreate",
145
- description: "Ein Test",
146
138
  pathname: {
147
139
  default: "user-create",
148
140
  },
@@ -161,7 +153,6 @@ export default [
161
153
  },
162
154
  {
163
155
  _id: "userEdit",
164
- description: "Ein Test",
165
156
  pathname: {
166
157
  default: "user-edit",
167
158
  },
@@ -182,7 +173,6 @@ export default [
182
173
  // tags administration
183
174
  {
184
175
  _id: "tagList",
185
- description: "Ein Test",
186
176
  pathname: {
187
177
  default: "tag-list",
188
178
  },
@@ -201,7 +191,6 @@ export default [
201
191
  },
202
192
  {
203
193
  _id: "tagCreate",
204
- description: "Ein Test",
205
194
  pathname: {
206
195
  default: "tag-create",
207
196
  },
@@ -220,7 +209,6 @@ export default [
220
209
  },
221
210
  {
222
211
  _id: "tagEdit",
223
- description: "Ein Test",
224
212
  pathname: {
225
213
  default: "tag-edit",
226
214
  },
@@ -242,7 +230,6 @@ export default [
242
230
  // plugins configuration
243
231
  {
244
232
  _id: "pluginList",
245
- description: "Ein Test",
246
233
  pathname: {
247
234
  default: "plugin-list",
248
235
  },
@@ -261,7 +248,6 @@ export default [
261
248
  },
262
249
  {
263
250
  _id: "pluginDetail",
264
- description: "Ein Test",
265
251
  pathname: {
266
252
  default: "plugin-detail",
267
253
  },
@@ -279,4 +265,22 @@ export default [
279
265
  ],
280
266
  queryParameters: ["id"],
281
267
  },
268
+ {
269
+ _id: "projectDetail",
270
+ pathname: {
271
+ default: "project-detail",
272
+ },
273
+ widgetsLayout: [
274
+ [
275
+ {
276
+ column: 12,
277
+ widget: {
278
+ uuid: "ProjectDetail",
279
+ name: "ProjectDetail",
280
+ pluginKey: "pacifico__project",
281
+ },
282
+ },
283
+ ],
284
+ ],
285
+ },
282
286
  ] //satisfies Composition[]
@@ -45,8 +45,8 @@ export default [
45
45
  settings: {
46
46
  icon: "mdi-link-box-variant",
47
47
  title: {
48
- default: "interactionLink",
49
- de: "interactionLink",
48
+ default: "Interaction Links",
49
+ de: "Interaktionslinks",
50
50
  },
51
51
  sortOrder: 1,
52
52
  navigationBarType: "navItem",
@@ -78,8 +78,8 @@ export default [
78
78
  settings: {
79
79
  icon: "mdi-account-details",
80
80
  title: {
81
- default: "users",
82
- de: "users",
81
+ default: "Users",
82
+ de: "Nutzer",
83
83
  },
84
84
  sortOrder: 2,
85
85
  navigationBarType: "navItem",
@@ -111,10 +111,10 @@ export default [
111
111
  settings: {
112
112
  icon: "mdi-tag",
113
113
  title: {
114
- default: "tags",
115
- de: "tags",
114
+ default: "Tags",
115
+ de: "Tags",
116
116
  },
117
- sortOrder: 2,
117
+ sortOrder: 3,
118
118
  navigationBarType: "navItem",
119
119
  },
120
120
  },
@@ -145,10 +145,10 @@ export default [
145
145
  settings: {
146
146
  icon: "mdi-puzzle",
147
147
  title: {
148
- default: "plugins",
149
- de: "plugins",
148
+ default: "Plugins",
149
+ de: "Plugins",
150
150
  },
151
- sortOrder: 2,
151
+ sortOrder: 4,
152
152
  navigationBarType: "navItem",
153
153
  },
154
154
  },
@@ -161,4 +161,24 @@ export default [
161
161
  triggers: [],
162
162
  queryParameters: ["pluginKey"],
163
163
  },
164
+ // project configuration
165
+ {
166
+ _id: "projectDetailInteractionLink",
167
+ composition: compositions[14]._id,
168
+ slotType: "page",
169
+ triggers: [
170
+ {
171
+ type: "page-navigation",
172
+ settings: {
173
+ icon: "mdi-application-cog",
174
+ title: {
175
+ default: "Project",
176
+ de: "Projekt",
177
+ },
178
+ sortOrder: 0,
179
+ navigationBarType: "footerItem",
180
+ },
181
+ },
182
+ ],
183
+ },
164
184
  ] // satisfies Partial<InteractionLink>[]
package/i18n/de-DE.json CHANGED
@@ -42,6 +42,7 @@
42
42
  "searchPlaceholder": "{dataName} durchsuchen",
43
43
  "createDataButton": "{dataName} erstellen",
44
44
  "deleteDataTitle": "{dataName} löschen",
45
+ "restoreDataTitle": "{dataName} widerherstellen",
45
46
  "deleteDataConfirmMessage": "Bist du sicher, dass du {article} {dataName} <strong>{value}</strong> löschen willst?",
46
47
  "deleteDataConfirmButton": "Löschen",
47
48
  "deleteDataConfirmCancel": "Abbrechen"
@@ -246,6 +247,31 @@
246
247
  "invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
247
248
  "invalidUrl": "Bitte geben Sie eine gültige URL ein"
248
249
  }
250
+ },
251
+ "project": {
252
+ "formtitle": "Projekt bearbeiten",
253
+ "dataName": "Projekt",
254
+ "dividers": {
255
+ "general": "Allgemein",
256
+ "config": "Konfiguration"
257
+ },
258
+ "title": {
259
+ "title": "Projekt name",
260
+ "description": "Der Name des Projekts"
261
+ },
262
+ "tags": {
263
+ "title": "Tags",
264
+ "description": "Dieses Feld dient nur der internen Zuordnung der Komposition und wird nicht im Produkt angezeigt."
265
+ },
266
+ "description": {
267
+ "title": "Projekt Beschreibung",
268
+ "description": "Die Beschreibung des Portal/Platform Inhalts"
269
+ },
270
+ "config": "Projekt Konfiguration",
271
+ "railPageNavigation": {
272
+ "title": "Ausklappbare Navigation",
273
+ "description": "Wenn aktiviert wird die Seitennavigation automatisch ein- und aufgeklappt"
274
+ }
249
275
  }
250
276
  }
251
- }
277
+ }
package/i18n/en-EU.json CHANGED
@@ -42,6 +42,7 @@
42
42
  "searchPlaceholder": "Search {dataName}",
43
43
  "createDataButton": "Create {dataName}",
44
44
  "deleteDataTitle": "Delete {dataName}",
45
+ "restoreDataTitle": "Restore {dataName}",
45
46
  "deleteDataConfirmMessage": "Are you sure you want to delete {article} {dataName} <strong>{value}</strong>?",
46
47
  "deleteDataConfirmButton": "Delete",
47
48
  "deleteDataConfirmCancel": "Cancel"
@@ -246,6 +247,31 @@
246
247
  "invalidEmail": "Please enter a valid email address",
247
248
  "invalidUrl": "Please enter a valid URL"
248
249
  }
250
+ },
251
+ "project": {
252
+ "formtitle": "Edit Project",
253
+ "dataName": "Project",
254
+ "dividers": {
255
+ "general": "General",
256
+ "config": "Configuration"
257
+ },
258
+ "title": {
259
+ "title": "Project Name",
260
+ "description": "The name of the project"
261
+ },
262
+ "tags": {
263
+ "title": "Tags",
264
+ "description": "This field is only used for internal categorization of the composition and is not displayed in the product."
265
+ },
266
+ "description": {
267
+ "title": "Project Description",
268
+ "description": "The description of the portal/platform content"
269
+ },
270
+ "config": "Project Configuration",
271
+ "railPageNavigation": {
272
+ "title": "Collapsible Navigation",
273
+ "description": "When enabled, the page navigation is automatically collapsed and expanded"
274
+ }
249
275
  }
250
276
  }
251
- }
277
+ }
package/i18n/sk-SK.json CHANGED
@@ -42,6 +42,7 @@
42
42
  "searchPlaceholder": "Hľadať {dataName}",
43
43
  "createDataButton": "Vytvoriť {dataName}",
44
44
  "deleteDataTitle": "Odstrániť {dataName}",
45
+ "restoreDataTitle": "Obnoviť {dataName}",
45
46
  "deleteDataConfirmMessage": "Naozaj chcete odstrániť {article} {dataName} <strong>{value}</strong>?",
46
47
  "deleteDataConfirmButton": "Odstrániť",
47
48
  "deleteDataConfirmCancel": "Zrušiť"
@@ -241,6 +242,31 @@
241
242
  "invalidEmail": "Zadajte platnú emailovú adresu",
242
243
  "invalidUrl": "Zadajte platnú URL adresu"
243
244
  }
245
+ },
246
+ "project": {
247
+ "formtitle": "Upraviť projekt",
248
+ "dataName": "Projekt",
249
+ "dividers": {
250
+ "general": "Všeobecné",
251
+ "config": "Konfigurácia"
252
+ },
253
+ "title": {
254
+ "title": "Názov projektu",
255
+ "description": "Názov projektu"
256
+ },
257
+ "description": {
258
+ "title": "Popis projektu",
259
+ "description": "Popis obsahu portálu/platformy"
260
+ },
261
+ "config": "Projekt Konfigurácia",
262
+ "railPageNavigation": {
263
+ "title": "Sklopná navigácia",
264
+ "description": "Ak je povolená, stránková navigácia sa automaticky sklopí a rozbalí"
265
+ },
266
+ "tags": {
267
+ "title": "Značky",
268
+ "description": "Toto pole slúži len na internú kategorizáciu kompozície a nezobrazuje sa v produkte."
269
+ }
244
270
  }
245
271
  }
246
272
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raclettejs/workbench",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
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",
@@ -31,13 +31,13 @@
31
31
  ".": "./index.ts"
32
32
  },
33
33
  "dependencies": {
34
- "@raclettejs/core": "0.1.17",
34
+ "@raclettejs/core": "0.1.19",
35
35
  "three": "0.180.0",
36
36
  "vanta": "0.5.24"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@eslint/js": "9.35.0",
40
- "@raclettejs/types": "0.1.17",
40
+ "@raclettejs/types": "0.1.19",
41
41
  "@sinclair/typebox": "0.34.41",
42
42
  "@types/ramda": "0.31.1",
43
43
  "@vueuse/core": "13.9.0",
@@ -1,56 +1,151 @@
1
1
  <template>
2
- <BaseDataTable
3
- :items="parsedCompositions"
4
- :loading="compositionsLoading || tagsLoading"
5
- :headers="headers"
6
- create-interaction-link-id="compositionCreateInteractionLink"
7
- edit-interaction-link-id="compositionEditInteractionLink"
8
- :data-name="$t('workbench.compositionList.dataName')"
9
- :data-article="$t('workbench.compositionList.dataArticle')"
10
- @delete="deleteComposition"
11
- :itemsDeleted="showDeleted"
12
- >
13
- <template #actions>
14
- <v-tooltip
15
- :text="showDeleted ? $t('core.hideDeleted') : $t('core.showDeleted')"
16
- >
17
- <template v-slot:activator="{ props }">
18
- <v-btn-toggle
19
- density="compact"
20
- v-bind="props"
21
- v-model="showDeleted"
22
- mandatory
23
- :color="showDeleted ? 'error' : 'success'"
24
- >
25
- <v-btn :value="true">
26
- <v-icon>mdi-table-eye-off</v-icon>
27
- </v-btn>
28
- <v-btn :value="false"><v-icon>mdi-table-eye</v-icon></v-btn>
29
- </v-btn-toggle>
30
- </template>
31
- </v-tooltip>
32
- </template>
33
- <!-- Custom status column -->
34
- <template #item.status="{ item }">
35
- <v-icon
36
- :icon="item.statusIcon"
37
- :color="item.statusColor"
38
- size="x-large"
39
- />
40
- </template>
41
- </BaseDataTable>
2
+ <div>
3
+ <BaseDataTable
4
+ :items="parsedCompositions"
5
+ :loading="compositionsLoading || tagsLoading"
6
+ :headers="headers"
7
+ create-interaction-link-id="compositionCreateInteractionLink"
8
+ edit-interaction-link-id="compositionEditInteractionLink"
9
+ :data-name="$t('workbench.compositionList.dataName')"
10
+ :data-article="$t('workbench.compositionList.dataArticle')"
11
+ @delete="deleteComposition"
12
+ :itemsDeleted="showDeleted"
13
+ >
14
+ <template #actions>
15
+ <v-tooltip
16
+ :text="showDeleted ? $t('core.hideDeleted') : $t('core.showDeleted')"
17
+ >
18
+ <template v-slot:activator="{ props }">
19
+ <v-btn-toggle
20
+ density="compact"
21
+ v-bind="props"
22
+ v-model="showDeleted"
23
+ mandatory
24
+ :color="showDeleted ? 'error' : 'success'"
25
+ >
26
+ <v-btn :value="true">
27
+ <v-icon>mdi-table-eye-off</v-icon>
28
+ </v-btn>
29
+ <v-btn :value="false"><v-icon>mdi-table-eye</v-icon></v-btn>
30
+ </v-btn-toggle>
31
+ </template>
32
+ </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
+ </template>
54
+ <!-- Custom status column -->
55
+ <template #item.status="{ item }">
56
+ <v-icon
57
+ :icon="item.statusIcon"
58
+ :color="item.statusColor"
59
+ size="x-large"
60
+ />
61
+ </template>
62
+ <template #prepend-row-actions="{ item }">
63
+ <v-icon
64
+ v-if="showDeleted"
65
+ color="green"
66
+ class="mr-2"
67
+ @click.stop.prevent="restore(item._id)"
68
+ :title="
69
+ $t('workbench.baseDataTable.restoreDataTitle', {
70
+ dataName: item.title,
71
+ })
72
+ "
73
+ icon="mdi-delete-restore"
74
+ />
75
+ </template>
76
+ </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
+ <!-- Import Selection Dialog -->
88
+ <SelectionDialog
89
+ v-model="showImportDialog"
90
+ :items="importableItems"
91
+ display-attribute="title"
92
+ :title="$t('core.selectItemsToImport')"
93
+ :confirm-text="$t('core.import')"
94
+ @confirm="handleImportConfirm"
95
+ />
96
+
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
+ <!-- Import Summary Dialog -->
108
+ <SummaryDialog
109
+ v-model="showImportSummary"
110
+ display-attribute="title"
111
+ :items="importedItems"
112
+ :title="$t('core.importSummary')"
113
+ :message="$t('core.importSuccessMessage')"
114
+ type="success"
115
+ :show-confirm="true"
116
+ :confirm-text="$t('core.saveImported')"
117
+ @confirm="finalizeImport"
118
+ />
119
+ </div>
42
120
  </template>
43
121
 
44
122
  <script setup lang="ts">
45
123
  import { usePluginApi } from "@raclettejs/core/orchestrator/composables"
46
- import { computed, watch, ref } from "vue"
124
+ import { computed, watch, ref, useTemplateRef } from "vue"
47
125
  import type { Composition } from "@raclettejs/core"
48
126
  import { formatDistance } from "date-fns"
49
127
  import BaseDataTable from "@app/components/BaseDataTable.vue"
50
128
  import { useI18n } from "vue-i18n"
51
- const showDeleted = ref(false)
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"
52
133
 
53
134
  const { t } = useI18n()
135
+ const showDeleted = ref(false)
136
+ // Dialog states
137
+ const showExportDialog = ref(false)
138
+ const showImportDialog = ref(false)
139
+ const showExportSummary = ref(false)
140
+ const showImportSummary = ref(false)
141
+
142
+ // Data for dialogs
143
+ const exportableItems = ref<any[]>([])
144
+ const importableItems = ref<any[]>([])
145
+ const exportedItems = ref<any[]>([])
146
+ const importedItems = ref<any[]>([])
147
+ const pendingImportData = ref<any>(null)
148
+ const uploadRef = useTemplateRef("uploadRef")
54
149
 
55
150
  const headers = computed<
56
151
  { title: string; key: keyof Composition | "actions" }[]
@@ -85,13 +180,12 @@ const props = defineProps<{
85
180
  uuid: string
86
181
  }>()
87
182
 
88
- const { $data, $socket } = usePluginApi("raclette__core")
183
+ const { $data, $socket, $user } = usePluginApi("raclette__core")
89
184
  const {
90
185
  data: compositions,
91
186
  execute,
92
187
  isLoading: compositionsLoading,
93
188
  } = $data.composition.getAll()
94
-
95
189
  const { data: tags, isLoading: tagsLoading } = $data.tag.getAll({
96
190
  options: { immediate: true },
97
191
  })
@@ -101,6 +195,12 @@ const { data: users } = $data.user.getAll({
101
195
  const { execute: executeDelete } = await $data.composition.delete({
102
196
  options: { cb: () => execute({ isDeleted: showDeleted.value }) },
103
197
  })
198
+ const { execute: executeBulkCreate } = await $data.composition.createBulk({
199
+ options: { notify: false, cb: () => execute() },
200
+ })
201
+ const { execute: executeRestore } = await $data.composition.updateRestore({
202
+ options: { cb: () => execute({ isDeleted: showDeleted.value }) },
203
+ })
104
204
  const { execute: executeHardDelete } = await $data.composition.deleteHard({
105
205
  options: { cb: () => execute({ isDeleted: showDeleted.value }) },
106
206
  })
@@ -146,7 +246,67 @@ const getStatusColor = (status: string) => {
146
246
  return "#B66218"
147
247
  }
148
248
  }
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
+ }
149
267
 
268
+ // Import functions
269
+ const handleFileUpload = (content: string, file: File) => {
270
+ try {
271
+ const parsedData = JSON.parse(content)
272
+ const itemsArray = Object.values(parsedData)
273
+
274
+ importableItems.value = itemsArray
275
+ pendingImportData.value = parsedData
276
+ showImportDialog.value = true
277
+ } catch (error) {
278
+ console.error("Failed to parse JSON:", error)
279
+ }
280
+ }
281
+
282
+ const handleImportConfirm = (selectedIds: string[]) => {
283
+ const selectedItems = selectedIds.map((id) => pendingImportData.value[id])
284
+ importedItems.value = selectedItems
285
+ showImportSummary.value = true
286
+ }
287
+
288
+ const finalizeImport = async () => {
289
+ const mapping = {
290
+ owner: $user.value._id,
291
+ lastEditor: $user.value._id,
292
+ createdBy: $user.value._id,
293
+ }
294
+
295
+ const dataToImport = importedItems.value.reduce((acc, item) => {
296
+ const mappedItem = mapJsonValues(item, mapping)
297
+ acc[item._id] = mappedItem
298
+ return acc
299
+ }, {} as any)
300
+
301
+ await executeBulkCreate(Object.values(dataToImport))
302
+ // TODO: Add your actual import logic here
303
+ // e.g., call an API endpoint to save the imported compositions
304
+
305
+ // Reset states
306
+ pendingImportData.value = null
307
+ importableItems.value = []
308
+ importedItems.value = []
309
+ }
150
310
  const deleteComposition = async (composition: Composition) => {
151
311
  if (showDeleted.value) {
152
312
  await executeHardDelete({ _id: composition._id })
@@ -154,7 +314,9 @@ const deleteComposition = async (composition: Composition) => {
154
314
  await executeDelete({ _id: composition._id })
155
315
  }
156
316
  }
157
-
317
+ const restore = (_id: string) => {
318
+ executeRestore({ _id })
319
+ }
158
320
  $socket.on("compositionCreated", () =>
159
321
  execute({ isDeleted: showDeleted.value }),
160
322
  )