@raclettejs/core 0.1.10 → 0.1.12
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 +59 -2
- package/dist/cli.js +104 -104
- package/dist/cli.js.map +7 -0
- package/dist/index.js +4 -4
- package/dist/index.js.map +7 -0
- package/package.json +32 -31
- package/services/backend/package.json +35 -45
- package/services/backend/src/core/payload/payloadTypes.ts +7 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.get.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.getAll.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.model.ts +5 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.schema.ts +1 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.get.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.getAll.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/helpers/payload.ts +6 -2
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.get.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.getAll.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.get.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.getAll.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/index.ts +2 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.get.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.getAll.ts +6 -2
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/tag.service.ts +22 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.get.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.getAll.ts +1 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.remove.ts +3 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/user.service.ts +1 -2
- package/services/backend/src/corePlugins/raclette__core/frontend/generated-config.ts +12 -12
- package/services/backend/src/domains/index.ts +3 -0
- package/services/backend/src/domains/system/collections.service.ts +119 -0
- package/services/backend/src/domains/system/index.ts +8 -0
- package/services/backend/src/domains/system/routes/index.ts +5 -0
- package/services/backend/src/domains/system/routes/route.collection.getAll.ts +61 -0
- package/services/backend/src/domains/system/routes/route.collection.remove.ts +93 -0
- package/services/backend/src/types/custom-fastify.d.ts +1 -0
- package/services/backend/src/utils/request.utils.ts +28 -1
- package/services/backend/yarn.lock +431 -1180
- package/services/frontend/package.json +29 -40
- package/services/frontend/src/core/constants/ApiClient.ts +6 -1
- package/services/frontend/src/core/lib/data/dataApi.ts +8 -1
- package/services/frontend/src/core/lib/data/fetchDataHandler.ts +58 -27
- package/services/frontend/src/core/lib/data/queryApi.ts +29 -8
- package/services/frontend/src/core/lib/data/responseTypeHandler.ts +27 -4
- package/services/frontend/src/core/lib/data/writeDataHandler.ts +1 -1
- package/services/frontend/src/core/lib/dataHelper.ts +8 -1
- package/services/frontend/src/core/lib/easteregg.ts +1 -1
- package/services/frontend/src/core/lib/eventEmitter.ts +1 -0
- package/services/frontend/src/core/lib/eventWhitelist.ts +11 -1
- package/services/frontend/src/core/lib/logger.ts +11 -3
- package/services/frontend/src/core/lib/userNotifier.ts +17 -17
- package/services/frontend/src/core/main.ts +3 -3
- package/services/frontend/src/core/setup/plugin-system/api/wrappers.ts +18 -1
- package/services/frontend/src/core/setup/plugin-system/registration/index.ts +1 -1
- package/services/frontend/src/core/setup/socketClient.ts +2 -2
- package/services/frontend/src/core/setup/socketEvents.ts +2 -2
- package/services/frontend/src/core/store/reducers/compositions/selectors.ts +2 -2
- package/services/frontend/src/core/store/reducers/data/index.ts +1 -1
- package/services/frontend/src/core/store/reducers/data/reducers.ts +3 -3
- package/services/frontend/src/core/store/reducers/queries/effects.ts +20 -1
- package/services/frontend/src/core/store/reducers/queries/index.ts +1 -0
- package/services/frontend/src/core/store/reducers/queries/reducers.ts +28 -1
- package/services/frontend/src/core/store/reducers/queriesCache/queryCacheHelper.ts +5 -4
- package/services/frontend/src/core/store/reducers/queriesCache/reducers.ts +4 -3
- package/services/frontend/src/core/store/state.ts +1 -1
- package/services/frontend/src/core/types/ApiClient.ts +1 -1
- package/services/frontend/src/core/types/DataApi.ts +4 -0
- package/services/frontend/src/core/types/Queries.ts +1 -0
- package/services/frontend/src/orchestrator/ProductOrchestrator.vue +19 -7
- package/services/frontend/src/orchestrator/WelcomeScreen.vue +39 -41
- package/services/frontend/src/orchestrator/assets/styles/tailwindStyles.css +3 -0
- package/services/frontend/src/orchestrator/assets/styles/themes/dark.ts +2 -2
- package/services/frontend/src/orchestrator/assets/styles/vuetifyStyles.scss +0 -1
- package/services/frontend/src/orchestrator/components/composition/CompositionOverlay.vue +11 -2
- package/services/frontend/src/orchestrator/components/composition/WidgetsLayoutLoader.vue +167 -58
- package/services/frontend/src/orchestrator/components/menu/DevIndicator.vue +122 -7
- package/services/frontend/src/orchestrator/components/menu/UserMenu.vue +38 -20
- package/services/frontend/src/orchestrator/components/snackbar/SnackStack.vue +17 -5
- package/services/frontend/src/orchestrator/composables/useCurrentComposition.ts +32 -4
- package/services/frontend/src/orchestrator/composables/usePageNavigation.ts +5 -1
- package/services/frontend/src/orchestrator/composables/usePluginApi.ts +125 -26
- package/services/frontend/src/orchestrator/composables/useRouteState.ts +0 -1
- package/services/frontend/src/orchestrator/composables/useVueQueryObservableHelper.ts +2 -0
- package/services/frontend/src/orchestrator/composables/useVueWriteOperationHelper.ts +5 -3
- package/services/frontend/src/orchestrator/composables/useWidgets/helperFunctions.ts +4 -1
- package/services/frontend/src/orchestrator/helpers/loginUserNotifications.ts +1 -1
- package/services/frontend/src/orchestrator/helpers/uiHelper.ts +1 -1
- package/services/frontend/src/orchestrator/i18n/de-DE.json +2 -1
- package/services/frontend/src/orchestrator/i18n/en-EU.json +2 -1
- package/services/frontend/src/orchestrator/i18n/sk.json +2 -1
- package/services/frontend/src/orchestrator/i18n/tl-TL.json +1 -1
- package/services/frontend/src/orchestrator/router/routeParserHelper.ts +4 -1
- package/services/frontend/src/orchestrator/router/routeStore.ts +78 -11
- package/services/frontend/tsconfig.app.json +1 -0
- package/services/frontend/yarn.lock +796 -1872
- package/yarn.lock +435 -618
|
@@ -1,97 +1,206 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
<div
|
|
3
|
+
class="tw:h-full tw:max-w-full tw:bg-gray-50 tw:dark:bg-[#252525]"
|
|
4
|
+
:class="{
|
|
5
|
+
'tw:p-4':
|
|
6
|
+
slotLayout !== 'default' &&
|
|
7
|
+
slotLayout !== 'inline' &&
|
|
8
|
+
slotLayout !== 'full',
|
|
9
|
+
}"
|
|
10
|
+
>
|
|
11
|
+
<Transition
|
|
12
|
+
enter-active-class="tw:transition-opacity tw:duration-200 tw:ease-in-out"
|
|
13
|
+
leave-active-class="tw:transition-opacity tw:duration-200 tw:ease-in-out"
|
|
14
|
+
enter-from-class="tw:opacity-0"
|
|
15
|
+
leave-to-class="tw:opacity-0"
|
|
16
|
+
mode="out-in"
|
|
17
|
+
appear
|
|
18
|
+
>
|
|
19
|
+
<v-row
|
|
20
|
+
:key="gridKey"
|
|
21
|
+
:no-gutters="slotLayout === 'default' || slotLayout === 'full'"
|
|
22
|
+
class="tw:max-w-full tw:self-start"
|
|
23
|
+
:class="{ 'tw:h-full': slotLayout === 'full' }"
|
|
11
24
|
>
|
|
12
|
-
<v-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
25
|
+
<v-col
|
|
26
|
+
v-for="(column, colIndex) in props.widgetsLayout"
|
|
27
|
+
:key="colIndex"
|
|
28
|
+
:cols="getGridCols(props.widgetsLayout.length)"
|
|
29
|
+
:class="{ 'tw:h-full': slotLayout === 'full' }"
|
|
30
|
+
>
|
|
31
|
+
<v-row
|
|
32
|
+
:no-gutters="slotLayout === 'default' || slotLayout === 'full'"
|
|
33
|
+
:class="{ 'tw:h-full': slotLayout === 'full', ...rowClasses }"
|
|
17
34
|
>
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
<TransitionGroup
|
|
36
|
+
name="grid-item"
|
|
37
|
+
tag="div"
|
|
38
|
+
class="tw:w-full tw:flex tw:flex-wrap"
|
|
39
|
+
appear
|
|
40
|
+
>
|
|
41
|
+
<v-col
|
|
42
|
+
v-for="(slot, slotIndex) in column"
|
|
43
|
+
:key="`${gridKey}-${colIndex}-${slotIndex}`"
|
|
44
|
+
:cols="getSlotCols(slot)"
|
|
45
|
+
:style="{
|
|
46
|
+
'transition-delay': `${slotIndex * 50}ms`,
|
|
47
|
+
'max-height': `${maxWidgetHeight}px`,
|
|
48
|
+
...(slotLayout === 'default' || slotLayout === 'full'
|
|
49
|
+
? { padding: '0px' }
|
|
50
|
+
: {}),
|
|
51
|
+
}"
|
|
24
52
|
>
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
:cols="getSlotCols(slot)"
|
|
30
|
-
:style="{ 'transition-delay': `${slotIndex * 50}ms` }"
|
|
53
|
+
<div
|
|
54
|
+
:class="widgetContainerClasses"
|
|
55
|
+
class="tw:h-full"
|
|
56
|
+
:style="{ 'max-height': `${maxWidgetHeight}px` }"
|
|
31
57
|
>
|
|
32
58
|
<component
|
|
33
59
|
v-if="slot.widget"
|
|
34
60
|
:is="getSlotWidget(slot.widget)"
|
|
35
61
|
:uuid="slot.widget.uuid"
|
|
62
|
+
class="tw:h-full"
|
|
63
|
+
:class="[
|
|
64
|
+
hasSpacing &&
|
|
65
|
+
widgetStates[slot.widget.uuid].config
|
|
66
|
+
?.allowPaddingOverride
|
|
67
|
+
? 'tw:p-4'
|
|
68
|
+
: '',
|
|
69
|
+
]"
|
|
36
70
|
v-bind="{
|
|
37
71
|
...widgetStates[slot.widget.uuid].config,
|
|
38
72
|
...widgetStates[slot.widget.uuid].public,
|
|
39
|
-
...
|
|
40
|
-
|
|
73
|
+
...routeParams,
|
|
74
|
+
compositionName: currentRouteConfig?.slotValue,
|
|
75
|
+
slotType,
|
|
41
76
|
}"
|
|
42
77
|
/>
|
|
43
|
-
</
|
|
44
|
-
</
|
|
45
|
-
</
|
|
46
|
-
</v-
|
|
47
|
-
</v-
|
|
48
|
-
</
|
|
49
|
-
</
|
|
78
|
+
</div>
|
|
79
|
+
</v-col>
|
|
80
|
+
</TransitionGroup>
|
|
81
|
+
</v-row>
|
|
82
|
+
</v-col>
|
|
83
|
+
</v-row>
|
|
84
|
+
</Transition>
|
|
50
85
|
</div>
|
|
51
86
|
</template>
|
|
52
87
|
|
|
53
88
|
<script setup lang="ts">
|
|
54
|
-
import {
|
|
89
|
+
import type { Composition } from "@shared/types/core/Composition.types"
|
|
90
|
+
import type { WidgetBase } from "@racletteCore/store/types"
|
|
91
|
+
import { watch, ref, h, computed, type Component } from "vue"
|
|
55
92
|
import { widgetLoader } from "@racletteOrchestrator/helpers/widgetLoader"
|
|
56
93
|
import { useDisplay } from "vuetify"
|
|
94
|
+
import { useWindowSize } from "@vueuse/core"
|
|
57
95
|
import useStateSubscriber from "@racletteOrchestrator/composables/useStateSubscriber"
|
|
58
|
-
import
|
|
59
|
-
import type { WidgetBase } from "@racletteCore/store/types"
|
|
96
|
+
import useRouteState from "@racletteOrchestrator/composables/useRouteState"
|
|
60
97
|
|
|
61
98
|
const props = defineProps<{
|
|
62
99
|
widgetsLayout: Composition["widgetsLayout"]
|
|
100
|
+
slotType: String
|
|
101
|
+
slotLayout: String
|
|
63
102
|
}>()
|
|
64
103
|
|
|
104
|
+
const { compositionSlots } = useRouteState()
|
|
105
|
+
|
|
106
|
+
const currentRouteConfig = computed(() =>
|
|
107
|
+
compositionSlots.value?.find((slot) => slot.slotName === props.slotType),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
const widgetContainerClasses = computed(() => {
|
|
111
|
+
const baseClasses = "tw:h-auto tw:overflow-auto"
|
|
112
|
+
|
|
113
|
+
switch (props.slotLayout) {
|
|
114
|
+
case "spaced":
|
|
115
|
+
return `${baseClasses} tw:rounded-lg`
|
|
116
|
+
case "card":
|
|
117
|
+
return `${baseClasses} tw:rounded-lg tw:shadow-lg hover:tw:shadow-xl tw:transition-shadow tw:duration-200`
|
|
118
|
+
case "border":
|
|
119
|
+
return `${baseClasses} tw:rounded-lg tw:border tw:border-gray-300 tw:dark:border-gray-700`
|
|
120
|
+
default:
|
|
121
|
+
return baseClasses
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const hasSpacing = computed(() =>
|
|
126
|
+
["spaced", "card", "border"].includes(props.slotLayout),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
const rowClasses = computed(() => {
|
|
130
|
+
return hasSpacing.value ? "tw:gap-2" : ""
|
|
131
|
+
})
|
|
132
|
+
|
|
65
133
|
const { mdAndDown } = useDisplay()
|
|
134
|
+
const { height: windowHeight } = useWindowSize()
|
|
66
135
|
|
|
67
136
|
const { state: widgetStates } = useStateSubscriber("widgets")
|
|
137
|
+
const routeParams = computed(() => {
|
|
138
|
+
const cleanedParams = {}
|
|
139
|
+
if (currentRouteConfig.value?.slotParams) {
|
|
140
|
+
for (const [key, value] of Object.entries(
|
|
141
|
+
currentRouteConfig.value.slotParams,
|
|
142
|
+
)) {
|
|
143
|
+
if (value && value !== "undefined") {
|
|
144
|
+
cleanedParams[key] = value
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return cleanedParams
|
|
150
|
+
})
|
|
68
151
|
|
|
69
152
|
// Create a reactive key that changes when widgetsLayout changes
|
|
70
153
|
const gridKey = ref(0)
|
|
71
154
|
|
|
155
|
+
// Calculate available height for widgets
|
|
156
|
+
const availableHeight = computed(() => {
|
|
157
|
+
// this is really just a simplified approach for now
|
|
158
|
+
|
|
159
|
+
// TODO: this needs to be more dynamic
|
|
160
|
+
const appBarHeight = 48 // Vuetify compact app bar height
|
|
161
|
+
const progressBarHeight = 2 // Progress bar height
|
|
162
|
+
const borderAndPadding = 8 // Border and padding buffer
|
|
163
|
+
|
|
164
|
+
const calculated =
|
|
165
|
+
windowHeight.value - appBarHeight - progressBarHeight - borderAndPadding
|
|
166
|
+
return calculated
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// Calculate max height per widget based on grid layout
|
|
170
|
+
const maxWidgetHeight = computed(() => {
|
|
171
|
+
// Always set max height to the calculated available screen height (with buffer)
|
|
172
|
+
// Later we should determine a max value based on other widgets in each row, so this value will be different across rows and columns
|
|
173
|
+
|
|
174
|
+
return Math.min(availableHeight.value, 2400)
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
const widgetComponentCache = new Map<string, Component>()
|
|
178
|
+
|
|
72
179
|
const getSlotWidget = (widget: WidgetBase) => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
class: "p-4 text-red-500 border border-red-300 rounded",
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
180
|
+
const cacheKey = `${widget.pluginKey}-${widget.name}-${widget.uuid}`
|
|
181
|
+
|
|
182
|
+
if (!widgetComponentCache.has(cacheKey)) {
|
|
183
|
+
try {
|
|
184
|
+
const component = widgetLoader(widget)
|
|
185
|
+
widgetComponentCache.set(cacheKey, component)
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error("🚨 Error creating widget component:", error)
|
|
188
|
+
// cache error component too
|
|
189
|
+
widgetComponentCache.set(cacheKey, {
|
|
190
|
+
name: "ComponentCreationError",
|
|
191
|
+
setup() {
|
|
192
|
+
return () =>
|
|
193
|
+
h(
|
|
194
|
+
"div",
|
|
195
|
+
{ class: "p-4 text-red-500 border border-red-300 rounded" },
|
|
196
|
+
"Error creating component",
|
|
197
|
+
)
|
|
198
|
+
},
|
|
199
|
+
})
|
|
93
200
|
}
|
|
94
201
|
}
|
|
202
|
+
|
|
203
|
+
return widgetComponentCache.get(cacheKey)
|
|
95
204
|
}
|
|
96
205
|
|
|
97
206
|
const getGridCols = (rowLength) => (mdAndDown.value ? 12 : 12 / rowLength)
|
|
@@ -6,38 +6,114 @@
|
|
|
6
6
|
<v-btn v-bind="props" icon="mdi-dev-to" color="#ffc400" />
|
|
7
7
|
</div>
|
|
8
8
|
</template>
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
<v-list
|
|
10
|
+
v-model:selected="dropCollections"
|
|
11
|
+
v-model:opened="openGroups"
|
|
12
|
+
select-strategy="leaf"
|
|
13
|
+
>
|
|
11
14
|
<v-list-subheader>Development mode</v-list-subheader>
|
|
12
|
-
|
|
13
15
|
<v-list-item
|
|
14
16
|
:title="toggleDevIndicatorMessage"
|
|
15
17
|
prepend-icon="mdi-border-outside"
|
|
16
18
|
@click="toggleShowDevIndicator"
|
|
17
19
|
/>
|
|
20
|
+
<v-list-group value="Users">
|
|
21
|
+
<template v-slot:activator="{ props }">
|
|
22
|
+
<v-list-item v-bind="props" prepend-icon="mdi-delete-sweep">
|
|
23
|
+
Drop Collections
|
|
24
|
+
</v-list-item>
|
|
25
|
+
</template>
|
|
26
|
+
<div class="tw:relative">
|
|
27
|
+
<v-btn
|
|
28
|
+
v-if="dropCollections.length"
|
|
29
|
+
color="error"
|
|
30
|
+
class="tw:absolute! tw:top-5! tw:right-5! tw:z-9999!"
|
|
31
|
+
@click="showConfirmation = true"
|
|
32
|
+
icon="mdi-delete-circle-outline"
|
|
33
|
+
>
|
|
34
|
+
</v-btn>
|
|
35
|
+
|
|
36
|
+
<v-list-item
|
|
37
|
+
v-for="collection in allCollections"
|
|
38
|
+
:key="collection.name"
|
|
39
|
+
:title="collection.name"
|
|
40
|
+
:subtitle="`Item Count: ${collection.count}`"
|
|
41
|
+
:value="collection.name"
|
|
42
|
+
:color="getColor(collection.name)"
|
|
43
|
+
:base-color="getColor(collection.name)"
|
|
44
|
+
>
|
|
45
|
+
<template v-slot:prepend="{ isSelected, select }">
|
|
46
|
+
<v-list-item-action start>
|
|
47
|
+
<v-checkbox-btn
|
|
48
|
+
:model-value="isSelected"
|
|
49
|
+
@update:model-value="select"
|
|
50
|
+
></v-checkbox-btn>
|
|
51
|
+
</v-list-item-action>
|
|
52
|
+
</template>
|
|
53
|
+
<template v-slot:append>
|
|
54
|
+
<v-list-item-action
|
|
55
|
+
v-if="isCoreCollection(collection.name)"
|
|
56
|
+
end
|
|
57
|
+
>
|
|
58
|
+
<v-icon color="warning">mdi-alert</v-icon>
|
|
59
|
+
</v-list-item-action>
|
|
60
|
+
</template>
|
|
61
|
+
</v-list-item>
|
|
62
|
+
</div>
|
|
63
|
+
</v-list-group>
|
|
18
64
|
</v-list>
|
|
19
65
|
</v-bottom-sheet>
|
|
66
|
+
<v-dialog v-model="showConfirmation" max-width="400">
|
|
67
|
+
<v-card>
|
|
68
|
+
<v-card-title class="tw:text-lg tw:font-semibold tw:text-red-600">
|
|
69
|
+
Delete Database Collections
|
|
70
|
+
</v-card-title>
|
|
71
|
+
<v-card-text>
|
|
72
|
+
Are you sure you want to delete the selected Collections?
|
|
73
|
+
</v-card-text>
|
|
74
|
+
<v-card-actions>
|
|
75
|
+
<v-spacer />
|
|
76
|
+
<v-btn @click="showConfirmation = false" variant="text"> Nope </v-btn>
|
|
77
|
+
<v-btn
|
|
78
|
+
@click="handleDropCollections"
|
|
79
|
+
color="error"
|
|
80
|
+
variant="elevated"
|
|
81
|
+
>
|
|
82
|
+
Aigh
|
|
83
|
+
</v-btn>
|
|
84
|
+
</v-card-actions>
|
|
85
|
+
</v-card>
|
|
86
|
+
</v-dialog>
|
|
20
87
|
</div>
|
|
21
88
|
</template>
|
|
22
89
|
|
|
23
90
|
<script setup lang="ts">
|
|
24
91
|
import { useLocalStorage } from "@vueuse/core"
|
|
25
|
-
import { computed, ref, watch } from "vue"
|
|
92
|
+
import { computed, ref, watch, onMounted } from "vue"
|
|
93
|
+
import { $api, $eventbus } from "@racletteCore"
|
|
94
|
+
import { useRouter } from "vue-router"
|
|
26
95
|
|
|
96
|
+
const router = useRouter()
|
|
97
|
+
const openGroups = ref([])
|
|
27
98
|
const showMenu = ref(false)
|
|
28
99
|
const showDevIndicator = useLocalStorage<boolean>("show-dev-indicator", true)
|
|
29
|
-
|
|
100
|
+
const showConfirmation = ref(false)
|
|
101
|
+
const allCollections = ref([])
|
|
30
102
|
const toggleShowDevIndicator = () => {
|
|
31
103
|
showDevIndicator.value = !showDevIndicator.value
|
|
32
104
|
showMenu.value = false
|
|
33
105
|
}
|
|
34
|
-
|
|
106
|
+
const dropCollections = ref([])
|
|
35
107
|
const toggleDevIndicatorMessage = computed(() => {
|
|
36
108
|
const word = showDevIndicator.value ? "Hide" : "Show"
|
|
37
109
|
|
|
38
110
|
return `${word} dev indication borders`
|
|
39
111
|
})
|
|
40
|
-
|
|
112
|
+
watch(showMenu, (value) => {
|
|
113
|
+
if (!value) {
|
|
114
|
+
dropCollections.value = []
|
|
115
|
+
}
|
|
116
|
+
})
|
|
41
117
|
watch(
|
|
42
118
|
showDevIndicator,
|
|
43
119
|
(value) => {
|
|
@@ -49,6 +125,45 @@ watch(
|
|
|
49
125
|
},
|
|
50
126
|
{ immediate: true },
|
|
51
127
|
)
|
|
128
|
+
onMounted(async () => {
|
|
129
|
+
const res = await $api.axios
|
|
130
|
+
.get("/core/admin/collections/all", {})
|
|
131
|
+
.catch((e) => {
|
|
132
|
+
$eventbus.emit("ui/addToSnackbar", {
|
|
133
|
+
message: e.response.data.message,
|
|
134
|
+
color: "error",
|
|
135
|
+
})
|
|
136
|
+
throw e
|
|
137
|
+
})
|
|
138
|
+
const data = res?.data?.data || []
|
|
139
|
+
allCollections.value = data.sort((a, b) => a.name.localeCompare(b.name))
|
|
140
|
+
})
|
|
141
|
+
const handleDropCollections = async () => {
|
|
142
|
+
if (dropCollections.value.length) {
|
|
143
|
+
const res = await $api.axios
|
|
144
|
+
.delete("/core/admin/collections", {
|
|
145
|
+
data: {
|
|
146
|
+
collections: dropCollections.value,
|
|
147
|
+
},
|
|
148
|
+
})
|
|
149
|
+
.catch((e) => {
|
|
150
|
+
$eventbus.emit("ui/addToSnackbar", {
|
|
151
|
+
message: e.response.data.message,
|
|
152
|
+
color: "error",
|
|
153
|
+
})
|
|
154
|
+
throw e
|
|
155
|
+
})
|
|
156
|
+
router.go()
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const isCoreCollection = (name) => {
|
|
160
|
+
return name.indexOf("raclette__core") > -1
|
|
161
|
+
}
|
|
162
|
+
const getColor = (name) => {
|
|
163
|
+
if (isCoreCollection(name)) {
|
|
164
|
+
return "error"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
52
167
|
</script>
|
|
53
168
|
|
|
54
169
|
<style lang="scss">
|
|
@@ -43,25 +43,42 @@
|
|
|
43
43
|
</template>
|
|
44
44
|
<v-list-item-title>{{ $t("core.toggle_snow") }}</v-list-item-title>
|
|
45
45
|
</v-list-item>
|
|
46
|
-
<v-
|
|
47
|
-
v-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<v-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
item.title
|
|
63
|
-
|
|
64
|
-
|
|
46
|
+
<template v-for="item in pageNavigation.userMenuItems" :key="item.route">
|
|
47
|
+
<v-list-item
|
|
48
|
+
:key="locale"
|
|
49
|
+
v-if="item.slotType !== 'inline'"
|
|
50
|
+
:disabled="activeRouteName === item.route || !item.route"
|
|
51
|
+
:active="activeRouteName === item.route"
|
|
52
|
+
:value="item.route || ''"
|
|
53
|
+
color="primary"
|
|
54
|
+
class="nav-link"
|
|
55
|
+
link
|
|
56
|
+
@click="navigate(item)"
|
|
57
|
+
>
|
|
58
|
+
<!-- item prepend icon -->
|
|
59
|
+
<template v-if="item.icon" #prepend>
|
|
60
|
+
<v-icon :icon="item.icon" />
|
|
61
|
+
</template>
|
|
62
|
+
<v-list-item-title v-if="item.title">{{
|
|
63
|
+
item.title
|
|
64
|
+
}}</v-list-item-title>
|
|
65
|
+
</v-list-item>
|
|
66
|
+
<v-list-group v-else>
|
|
67
|
+
<template v-slot:activator="{ props }">
|
|
68
|
+
<v-list-item
|
|
69
|
+
v-bind="props"
|
|
70
|
+
:prepend-icon="item.icon"
|
|
71
|
+
:title="item.title"
|
|
72
|
+
></v-list-item>
|
|
73
|
+
</template>
|
|
74
|
+
<WidgetsLayoutLoader
|
|
75
|
+
v-if="compositions[item.compositionId]"
|
|
76
|
+
:widgets-layout="compositions[item.compositionId].widgetsLayout"
|
|
77
|
+
slot-type="inline"
|
|
78
|
+
:slot-layout="compositions[item.compositionId].slotLayout"
|
|
79
|
+
/>
|
|
80
|
+
</v-list-group>
|
|
81
|
+
</template>
|
|
65
82
|
|
|
66
83
|
<v-divider />
|
|
67
84
|
|
|
@@ -87,7 +104,7 @@ import { awaitTime, debounceHelper } from "@racletteCore/lib/dataHelper"
|
|
|
87
104
|
import useRouteState from "@racletteOrchestrator/composables/useRouteState"
|
|
88
105
|
import { DEFAULT_SLOT_NAME } from "@racletteOrchestrator/router/routeParserHelper"
|
|
89
106
|
import { useLocalStorage } from "@vueuse/core"
|
|
90
|
-
|
|
107
|
+
import WidgetsLayoutLoader from "../composition/WidgetsLayoutLoader.vue"
|
|
91
108
|
import {
|
|
92
109
|
debounceHelper as asteroidsDebounceHelper,
|
|
93
110
|
eggMe,
|
|
@@ -101,6 +118,7 @@ const router = useRouter()
|
|
|
101
118
|
const asteroidsDebounce = asteroidsDebounceHelper(3)
|
|
102
119
|
const debounce = debounceHelper(1000)
|
|
103
120
|
const { state: user } = useStateSubscriber("user")
|
|
121
|
+
const { state: compositions } = useStateSubscriber("compositions")
|
|
104
122
|
const { locale, theme } = useUiState()
|
|
105
123
|
const { getCompositionLinkForSlotName } = useRouteState()
|
|
106
124
|
|
|
@@ -41,8 +41,18 @@ export type SnackbarMessage = {
|
|
|
41
41
|
i18n?: string
|
|
42
42
|
color?: string
|
|
43
43
|
timeout?: number
|
|
44
|
+
sender?: string
|
|
45
|
+
}
|
|
46
|
+
export type ScopedSnackbarMessage = {
|
|
47
|
+
sender: string
|
|
48
|
+
data: {
|
|
49
|
+
message?: string
|
|
50
|
+
content?: Record<string, any>
|
|
51
|
+
i18n?: string
|
|
52
|
+
color?: string
|
|
53
|
+
timeout?: number
|
|
54
|
+
}
|
|
44
55
|
}
|
|
45
|
-
|
|
46
56
|
const snackbars = ref<(SnackbarMessage & { show: boolean })[]>([])
|
|
47
57
|
|
|
48
58
|
const defaultTimeout = 4000
|
|
@@ -54,8 +64,10 @@ const calcMargin = (index: number) => `${index * 60 + 10}px`
|
|
|
54
64
|
// the close icon will only be shown if the bar has no timeout
|
|
55
65
|
const handleSnackbarClick = (index: number) => snackbars.value.splice(index, 1)
|
|
56
66
|
|
|
57
|
-
const addToSnackBar = (data: SnackbarMessage) => {
|
|
58
|
-
|
|
67
|
+
const addToSnackBar = (data: SnackbarMessage | ScopedSnackbarMessage) => {
|
|
68
|
+
// TODO implement proper handling of sender attribute
|
|
69
|
+
const _data = data?.sender ? data.data : data
|
|
70
|
+
const snackbar = { ..._data, show: true }
|
|
59
71
|
|
|
60
72
|
// Prevent duplicate messages (check last two)
|
|
61
73
|
const lastMessages = snackbars.value.slice(-2)
|
|
@@ -80,7 +92,7 @@ const addToSnackBar = (data: SnackbarMessage) => {
|
|
|
80
92
|
}
|
|
81
93
|
}
|
|
82
94
|
|
|
83
|
-
onMounted(() => $eventbus.on("
|
|
95
|
+
onMounted(() => $eventbus.on("ui/addToSnackbar", addToSnackBar))
|
|
84
96
|
|
|
85
|
-
onUnmounted(() => $eventbus.removeListener("
|
|
97
|
+
onUnmounted(() => $eventbus.removeListener("ui/addToSnackbar", addToSnackBar))
|
|
86
98
|
</script>
|
|
@@ -4,15 +4,21 @@ import { getCompositionPathById } from "@racletteCore/store/reducers/composition
|
|
|
4
4
|
import { computed } from "vue"
|
|
5
5
|
import { DEFAULT_SLOT_NAME } from "@racletteOrchestrator/router/routeParserHelper"
|
|
6
6
|
|
|
7
|
+
// Simple reference cache to prevent unnecessary re-renders
|
|
8
|
+
let cachedCompositionId: string | null = null
|
|
9
|
+
let cachedTitle: string | null = null
|
|
10
|
+
let cachedWidgetsLayout: any = null
|
|
11
|
+
|
|
7
12
|
export const useCurrentComposition = (key: string = DEFAULT_SLOT_NAME) => {
|
|
8
13
|
const { state: currentComposableId } = useStateSubscriber(["ui", key])
|
|
9
14
|
const { state: compositions } = useStateSubscriber("compositions")
|
|
10
15
|
|
|
11
|
-
const state = computed<Composition | undefined>(
|
|
12
|
-
|
|
13
|
-
)
|
|
16
|
+
const state = computed<Composition | undefined>(() => {
|
|
17
|
+
return compositions.value[currentComposableId.value]
|
|
18
|
+
})
|
|
14
19
|
|
|
15
20
|
const id = computed(() => state.value?._id)
|
|
21
|
+
const slotLayout = computed(() => state.value?.slotLayout || "default")
|
|
16
22
|
|
|
17
23
|
const localizedPath = computed(() =>
|
|
18
24
|
id.value ? getCompositionPathById(id.value) : undefined,
|
|
@@ -25,7 +31,28 @@ export const useCurrentComposition = (key: string = DEFAULT_SLOT_NAME) => {
|
|
|
25
31
|
const pathname = computed(() => state.value?.pathname)
|
|
26
32
|
const status = computed(() => state.value?.status)
|
|
27
33
|
|
|
28
|
-
|
|
34
|
+
// Cached widgetsLayout that only updates when core composition changes
|
|
35
|
+
const widgetsLayout = computed(() => {
|
|
36
|
+
const currentComposition = state.value
|
|
37
|
+
if (!currentComposition || !currentComposition._id) {
|
|
38
|
+
return undefined
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const compositionId = currentComposition._id
|
|
42
|
+
const currentTitle = currentComposition.title
|
|
43
|
+
const currentWidgetsLayout = currentComposition.widgetsLayout
|
|
44
|
+
|
|
45
|
+
// If composition ID or title changed, it's a different composition - update cache
|
|
46
|
+
if (cachedCompositionId !== compositionId || cachedTitle !== currentTitle) {
|
|
47
|
+
cachedCompositionId = compositionId
|
|
48
|
+
cachedTitle = currentTitle
|
|
49
|
+
cachedWidgetsLayout = currentWidgetsLayout
|
|
50
|
+
return currentWidgetsLayout
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Same composition, return cached reference to prevent re-renders
|
|
54
|
+
return cachedWidgetsLayout
|
|
55
|
+
})
|
|
29
56
|
|
|
30
57
|
return {
|
|
31
58
|
state,
|
|
@@ -38,6 +65,7 @@ export const useCurrentComposition = (key: string = DEFAULT_SLOT_NAME) => {
|
|
|
38
65
|
pathname,
|
|
39
66
|
status,
|
|
40
67
|
widgetsLayout,
|
|
68
|
+
slotLayout,
|
|
41
69
|
}
|
|
42
70
|
}
|
|
43
71
|
|
|
@@ -5,17 +5,20 @@ import {
|
|
|
5
5
|
import useInteractionLinks from "@racletteOrchestrator/composables/useInteractionLinks"
|
|
6
6
|
import { computed } from "vue"
|
|
7
7
|
import useRouteState from "./useRouteState"
|
|
8
|
+
import useUiState from "@racletteOrchestrator/composables/useUiState"
|
|
8
9
|
|
|
9
10
|
export type NavigationItem = {
|
|
10
11
|
icon: string
|
|
11
12
|
route?: string
|
|
12
13
|
sortOrder: number
|
|
14
|
+
compositionId: string
|
|
13
15
|
title: string
|
|
14
16
|
slotType: string
|
|
15
17
|
navigateTo: () => void
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export default () => {
|
|
21
|
+
const { locale } = useUiState()
|
|
19
22
|
const { state } = useInteractionLinks(["page-navigation", "user-menu"])
|
|
20
23
|
const { triggerInteractionLink } = useRouteState()
|
|
21
24
|
|
|
@@ -27,7 +30,7 @@ export default () => {
|
|
|
27
30
|
const navigationItems: NavigationItem[] = []
|
|
28
31
|
const footerItems: NavigationItem[] = []
|
|
29
32
|
const userMenuItems: NavigationItem[] = []
|
|
30
|
-
|
|
33
|
+
locale.value
|
|
31
34
|
state.value.forEach((interactionLink) =>
|
|
32
35
|
interactionLink.triggers
|
|
33
36
|
|
|
@@ -42,6 +45,7 @@ export default () => {
|
|
|
42
45
|
const item: NavigationItem = {
|
|
43
46
|
icon,
|
|
44
47
|
sortOrder,
|
|
48
|
+
compositionId: interactionLink.composition,
|
|
45
49
|
title: getLocalizedName(title),
|
|
46
50
|
route: getCompositionPathById(interactionLink.composition),
|
|
47
51
|
slotType: interactionLink.slotType,
|