@raclettejs/workbench 0.1.18 → 0.1.20
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 +519 -0
- package/{yarn.lock → .raclette/backend/yarn.lock} +1014 -796
- package/.raclette/frontend/raclette.config.js +47 -0
- package/.raclette/frontend/yarn.lock +3428 -0
- package/.raclette/raclette.config.js +643 -0
- package/.raclette/virtual/backend/README.md +172 -0
- package/.raclette/virtual/backend/raclette.config.js +519 -0
- package/.raclette/virtual/backend/yarn.lock +3773 -0
- package/.raclette/virtual/frontend/README.md +511 -0
- package/.raclette/virtual/frontend/raclette.config.js +47 -0
- package/.raclette/virtual/frontend/src/core/lib/eggs/readme.md +75 -0
- package/.raclette/virtual/frontend/yarn.lock +3428 -0
- package/CHANGELOG.md +11 -0
- package/i18n/de-DE.json +17 -1
- package/i18n/en-EU.json +17 -1
- package/i18n/sk-SK.json +17 -1
- package/package.json +6 -4
- package/packages.json +3 -1
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionWidgetsLayout.vue +8 -3
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetContextMenu.vue +60 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetIcon.vue +4 -7
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetList.vue +34 -117
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetListFlat.vue +20 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetListGrouped.vue +50 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetListHeader.vue +213 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetListItem.vue +56 -0
- package/plugins/pacifico__compositions/frontend/composables/useWidgetList.ts +288 -0
- package/plugins/pacifico__compositions/frontend/widgets/CompositionListWidget.vue +201 -57
- package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkListWidget.vue +219 -55
- package/plugins/pacifico__tags/frontend/widgets/TagListWidget.vue +198 -48
- package/raclette.config.js +4 -1
- package/services/frontend/src/app/components/FileUpload.vue +98 -0
- package/services/frontend/src/app/components/SelectionDialog.vue +182 -0
- package/services/frontend/src/app/components/SummaryDialog.vue +140 -0
- package/services/frontend/src/app/lib/jsonTools.ts +81 -0
|
@@ -1,70 +1,151 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<v-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<v-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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>
|
|
56
120
|
</template>
|
|
57
121
|
|
|
58
122
|
<script setup lang="ts">
|
|
59
123
|
import { usePluginApi } from "@raclettejs/core/orchestrator/composables"
|
|
60
|
-
import { computed, watch, ref } from "vue"
|
|
124
|
+
import { computed, watch, ref, useTemplateRef } from "vue"
|
|
61
125
|
import type { Composition } from "@raclettejs/core"
|
|
62
126
|
import { formatDistance } from "date-fns"
|
|
63
127
|
import BaseDataTable from "@app/components/BaseDataTable.vue"
|
|
64
128
|
import { useI18n } from "vue-i18n"
|
|
65
|
-
|
|
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"
|
|
66
133
|
|
|
67
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")
|
|
68
149
|
|
|
69
150
|
const headers = computed<
|
|
70
151
|
{ title: string; key: keyof Composition | "actions" }[]
|
|
@@ -99,7 +180,7 @@ const props = defineProps<{
|
|
|
99
180
|
uuid: string
|
|
100
181
|
}>()
|
|
101
182
|
|
|
102
|
-
const { $data, $socket } = usePluginApi("raclette__core")
|
|
183
|
+
const { $data, $socket, $user } = usePluginApi("raclette__core")
|
|
103
184
|
const {
|
|
104
185
|
data: compositions,
|
|
105
186
|
execute,
|
|
@@ -114,6 +195,9 @@ const { data: users } = $data.user.getAll({
|
|
|
114
195
|
const { execute: executeDelete } = await $data.composition.delete({
|
|
115
196
|
options: { cb: () => execute({ isDeleted: showDeleted.value }) },
|
|
116
197
|
})
|
|
198
|
+
const { execute: executeBulkCreate } = await $data.composition.createBulk({
|
|
199
|
+
options: { notify: false, cb: () => execute() },
|
|
200
|
+
})
|
|
117
201
|
const { execute: executeRestore } = await $data.composition.updateRestore({
|
|
118
202
|
options: { cb: () => execute({ isDeleted: showDeleted.value }) },
|
|
119
203
|
})
|
|
@@ -162,7 +246,67 @@ const getStatusColor = (status: string) => {
|
|
|
162
246
|
return "#B66218"
|
|
163
247
|
}
|
|
164
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
|
+
}
|
|
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
|
+
}
|
|
165
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
|
+
}
|
|
166
310
|
const deleteComposition = async (composition: Composition) => {
|
|
167
311
|
if (showDeleted.value) {
|
|
168
312
|
await executeHardDelete({ _id: composition._id })
|
|
@@ -1,67 +1,149 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<v-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<v-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
2
|
+
<div>
|
|
3
|
+
<BaseDataTable
|
|
4
|
+
:items="parsedInteractionLinks"
|
|
5
|
+
:loading="interactionLinksLoading || tagsLoading"
|
|
6
|
+
:headers="headers"
|
|
7
|
+
create-interaction-link-id="interactionLinkCreateInteractionLink"
|
|
8
|
+
edit-interaction-link-id="interactionLinkEditInteractionLink"
|
|
9
|
+
:data-name="$t('workbench.interactionLinkList.dataName')"
|
|
10
|
+
:data-article="$t('workbench.interactionLinkList.dataArticle')"
|
|
11
|
+
@delete="deleteInteractionLink"
|
|
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 triggers column -->
|
|
55
|
+
<template #item.triggers="{ item }">
|
|
56
|
+
<span class="tw:flex tw:gap-2">
|
|
57
|
+
<span>{{ item.triggers }}</span>
|
|
58
|
+
<v-icon icon="mdi-link" />
|
|
59
|
+
</span>
|
|
60
|
+
</template>
|
|
61
|
+
<template #prepend-row-actions="{ item }">
|
|
62
|
+
<v-icon
|
|
63
|
+
v-if="showDeleted"
|
|
64
|
+
color="green"
|
|
65
|
+
class="mr-2"
|
|
66
|
+
@click="restore(item._id)"
|
|
67
|
+
:title="
|
|
68
|
+
$t('workbench.baseDataTable.restoreDataTitle', {
|
|
69
|
+
dataName: item.title,
|
|
70
|
+
})
|
|
71
|
+
"
|
|
72
|
+
icon="mdi-delete-restore"
|
|
73
|
+
/>
|
|
74
|
+
</template>
|
|
75
|
+
</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
|
+
<!-- Import Selection Dialog -->
|
|
87
|
+
<SelectionDialog
|
|
88
|
+
v-model="showImportDialog"
|
|
89
|
+
:items="importableItems"
|
|
90
|
+
display-attribute="description"
|
|
91
|
+
:title="$t('core.selectItemsToImport')"
|
|
92
|
+
:confirm-text="$t('core.import')"
|
|
93
|
+
@confirm="handleImportConfirm"
|
|
94
|
+
/>
|
|
95
|
+
|
|
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
|
+
<!-- Import Summary Dialog -->
|
|
107
|
+
<SummaryDialog
|
|
108
|
+
v-model="showImportSummary"
|
|
109
|
+
:items="importedItems"
|
|
110
|
+
display-attribute="description"
|
|
111
|
+
:title="$t('core.importSummary')"
|
|
112
|
+
:message="$t('core.importSuccessMessage')"
|
|
113
|
+
type="success"
|
|
114
|
+
:show-confirm="true"
|
|
115
|
+
:confirm-text="$t('core.saveImported')"
|
|
116
|
+
@confirm="finalizeImport"
|
|
117
|
+
/>
|
|
118
|
+
</div>
|
|
55
119
|
</template>
|
|
56
120
|
|
|
57
121
|
<script setup lang="ts">
|
|
122
|
+
import { clone } from "ramda"
|
|
58
123
|
import BaseDataTable from "@app/components/BaseDataTable.vue"
|
|
59
124
|
import { InteractionLink } from "@raclettejs/core"
|
|
60
125
|
import { usePluginApi } from "@raclettejs/core/orchestrator/composables"
|
|
61
126
|
import { formatDistance } from "date-fns"
|
|
62
|
-
import { computed, ref, watch } from "vue"
|
|
127
|
+
import { computed, ref, watch, useTemplateRef } from "vue"
|
|
63
128
|
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"
|
|
64
133
|
const showDeleted = ref(false)
|
|
134
|
+
// Dialog states
|
|
135
|
+
const showExportDialog = ref(false)
|
|
136
|
+
const showImportDialog = ref(false)
|
|
137
|
+
const showExportSummary = ref(false)
|
|
138
|
+
const showImportSummary = ref(false)
|
|
139
|
+
|
|
140
|
+
// Data for dialogs
|
|
141
|
+
const exportableItems = ref<any[]>([])
|
|
142
|
+
const importableItems = ref<any[]>([])
|
|
143
|
+
const exportedItems = ref<any[]>([])
|
|
144
|
+
const importedItems = ref<any[]>([])
|
|
145
|
+
const pendingImportData = ref<any>(null)
|
|
146
|
+
const uploadRef = useTemplateRef("uploadRef")
|
|
65
147
|
|
|
66
148
|
const { t } = useI18n()
|
|
67
149
|
|
|
@@ -102,7 +184,7 @@ const props = defineProps<{
|
|
|
102
184
|
uuid: string
|
|
103
185
|
}>()
|
|
104
186
|
|
|
105
|
-
const { $data, $socket } = usePluginApi("raclette__core")
|
|
187
|
+
const { $data, $socket, $user } = usePluginApi("raclette__core")
|
|
106
188
|
|
|
107
189
|
const {
|
|
108
190
|
data: interactionLinks,
|
|
@@ -123,6 +205,9 @@ const { data: compositions } = $data.composition.getAll({
|
|
|
123
205
|
options: { immediate: true },
|
|
124
206
|
})
|
|
125
207
|
|
|
208
|
+
const { execute: executeBulkCreate } = await $data.interactionLink.createBulk({
|
|
209
|
+
options: { notify: false, cb: () => execute() },
|
|
210
|
+
})
|
|
126
211
|
const { execute: executeDelete } = $data.interactionLink.delete({
|
|
127
212
|
options: { cb: () => execute({ isDeleted: showDeleted.value }) },
|
|
128
213
|
})
|
|
@@ -172,6 +257,85 @@ const deleteInteractionLink = async (interactionLink: InteractionLink) => {
|
|
|
172
257
|
$socket.on("interactionLinkCreated", () =>
|
|
173
258
|
execute({ isDeleted: showDeleted.value }),
|
|
174
259
|
)
|
|
260
|
+
const createTitleString = (link) => {
|
|
261
|
+
let linkTitleString = `${link.slotType}`
|
|
262
|
+
if (link.triggers) {
|
|
263
|
+
link.triggers.forEach((trigger) => {
|
|
264
|
+
linkTitleString += `${trigger.type} - ${trigger.settings?.title?.default} `
|
|
265
|
+
})
|
|
266
|
+
}
|
|
267
|
+
link.description = linkTitleString + link.description
|
|
268
|
+
return link
|
|
269
|
+
}
|
|
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
|
+
|
|
297
|
+
// Import functions
|
|
298
|
+
const handleFileUpload = (content: string, file: File) => {
|
|
299
|
+
try {
|
|
300
|
+
const parsedData = JSON.parse(content)
|
|
301
|
+
const itemsArray = Object.values(parsedData)
|
|
302
|
+
|
|
303
|
+
importableItems.value = itemsArray
|
|
304
|
+
pendingImportData.value = parsedData
|
|
305
|
+
showImportDialog.value = true
|
|
306
|
+
} catch (error) {
|
|
307
|
+
console.error("Failed to parse JSON:", error)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const handleImportConfirm = (selectedIds: string[]) => {
|
|
312
|
+
const selectedItems = selectedIds.map((id) => pendingImportData.value[id])
|
|
313
|
+
importedItems.value = selectedItems
|
|
314
|
+
showImportSummary.value = true
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const finalizeImport = async () => {
|
|
318
|
+
const mapping = {
|
|
319
|
+
owner: $user.value._id,
|
|
320
|
+
lastEditor: $user.value._id,
|
|
321
|
+
createdBy: $user.value._id,
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const dataToImport = importedItems.value.reduce((acc, item) => {
|
|
325
|
+
const mappedItem = mapJsonValues(item, mapping)
|
|
326
|
+
acc[item._id] = createTitleString(mappedItem)
|
|
327
|
+
return acc
|
|
328
|
+
}, {} as any)
|
|
329
|
+
|
|
330
|
+
await executeBulkCreate(Object.values(dataToImport))
|
|
331
|
+
// TODO: Add your actual import logic here
|
|
332
|
+
// e.g., call an API endpoint to save the imported compositions
|
|
333
|
+
|
|
334
|
+
// Reset states
|
|
335
|
+
pendingImportData.value = null
|
|
336
|
+
importableItems.value = []
|
|
337
|
+
importedItems.value = []
|
|
338
|
+
}
|
|
175
339
|
watch(
|
|
176
340
|
showDeleted,
|
|
177
341
|
() => {
|