@raclettejs/core 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/CHANGELOG.md +23 -0
- package/bin/cli.js +1 -1
- package/dev/README.md +4 -0
- package/dist/cli.js +135 -124
- package/dist/cli.js.map +4 -4
- package/dist/index.js +9 -8
- package/dist/index.js.map +4 -4
- package/package.json +9 -5
- package/services/backend/delay-api-debugger.sh +0 -0
- package/services/backend/package.json +1 -0
- package/services/backend/src/app.ts +4 -0
- package/services/backend/src/core/config/configService.ts +10 -1
- package/services/backend/src/core/events/eventEmitter.ts +32 -7
- package/services/backend/src/core/http/request-params.plugin.ts +2 -2
- package/services/backend/src/core/pluginSystem/pluginFastifyInstance.ts +4 -1
- package/services/backend/src/core/pluginSystem/pluginTypes.ts +3 -1
- package/services/backend/src/core/sockets/createSystemEventHandlers.ts +7 -0
- package/services/backend/src/core/sockets/index.ts +61 -13
- package/services/backend/src/core/sockets/socketManager.ts +452 -105
- package/services/backend/src/core/sockets/socketTypes.ts +13 -1
- package/services/backend/src/core/sockets/workSessionJoinPayload.ts +106 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.get.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.getAll.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.hardDelete.ts +16 -19
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.patch.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.post.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.remove.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts +86 -3
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/index.ts +2 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.get.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.getAll.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.hardDelete.ts +16 -19
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.patch.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.post.bulk.ts +60 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.post.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.remove.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.restore.ts +12 -15
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.service.ts +96 -12
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/index.ts +2 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.get.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.getAll.ts +4 -2
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.hardDelete.ts +12 -17
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.patch.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.post.bulk.ts +62 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.post.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.remove.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.restore.ts +12 -15
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.service.ts +1 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.get.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.getAll.ts +4 -2
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.hardDelete.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.patch.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.post.ts +4 -2
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.remove.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/index.ts +2 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.get.ts +1 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.getAll.ts +1 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.hardDelete.ts +10 -15
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.patch.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.post.bulk.ts +60 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.post.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.remove.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.restore.ts +12 -15
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/tag.service.ts +73 -3
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.get.ts +1 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.getAll.ts +1 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.hardDelete.ts +10 -15
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.patch.ts +4 -6
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.patchLastProject.ts +4 -2
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.post.ts +3 -1
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.remove.ts +3 -4
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.restore.ts +12 -15
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/events/index.ts +45 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/helpers/crud.ts +12 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/helpers/payload.ts +21 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/index.ts +55 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/index.ts +19 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/route.workSession.get.ts +53 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/route.workSession.getAll.ts +39 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/route.workSession.hardDelete.ts +53 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/route.workSession.patch.ts +58 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/route.workSession.post.ts +53 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/route.workSession.remove.ts +52 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/workSession.model.ts +65 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/workSession.schema.ts +125 -0
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/workSession.service.ts +351 -0
- package/services/backend/src/corePlugins/raclette__core/backend/index.ts +2 -0
- package/services/backend/src/corePlugins/raclette__core/frontend/generated-config.ts +159 -24
- package/services/backend/src/modules/authentication/index.ts +255 -4
- package/services/backend/src/modules/hooks/rate-limiter.ts +21 -0
- package/services/backend/src/types/custom-fastify.d.ts +17 -0
- package/services/backend/src/utils/request.utils.ts +4 -1
- package/services/backend/types/index.d.ts +15 -1
- package/services/backend/yarn.lock +13 -4
- package/services/frontend/index.html +1 -1
- package/services/frontend/provision/entrypoint.sh +0 -0
- package/services/frontend/src/core/lib/configService.ts +1 -1
- package/services/frontend/src/core/lib/data/dataApi.ts +10 -1
- package/services/frontend/src/core/lib/dataHelper.ts +1 -1
- package/services/frontend/src/core/lib/eggs/snow.ts +66 -48
- package/services/frontend/src/core/lib/helpers/index.ts +1 -0
- package/services/frontend/src/core/lib/helpers/removeEmpty.ts +69 -0
- package/services/frontend/src/core/lib/httpClient.ts +19 -0
- package/services/frontend/src/core/main.ts +20 -10
- package/services/frontend/src/core/setup/socket.ts +4 -3
- package/services/frontend/src/core/setup/socketClient.ts +31 -3
- package/services/frontend/src/core/setup/socketEvents.ts +92 -8
- package/services/frontend/src/core/store/index.ts +5 -1
- package/services/frontend/src/core/store/reducers/compositions/index.ts +2 -2
- package/services/frontend/src/core/store/reducers/interactionLinks/index.ts +1 -1
- package/services/frontend/src/core/store/reducers/widgets/reducers.ts +1 -2
- package/services/frontend/src/core/store/types/index.ts +0 -1
- package/services/frontend/src/core/types/PluginRegistrar.ts +8 -6
- package/services/frontend/src/core/types/Store.ts +1 -0
- package/services/frontend/src/orchestrator/components/composition/WidgetsLayoutLoader.vue +68 -8
- package/services/frontend/src/orchestrator/components/menu/UserMenu.vue +17 -5
- package/services/frontend/src/orchestrator/components/welcomeScreen/AdminWelcomeScreen.vue +1 -1
- package/services/frontend/src/orchestrator/composables/index.ts +1 -0
- package/services/frontend/src/orchestrator/composables/usePluginApi.ts +0 -3
- package/services/frontend/src/orchestrator/composables/useRouteState.ts +23 -4
- package/services/frontend/src/orchestrator/composables/useWidgetLifecycle.ts +166 -0
- package/services/frontend/src/orchestrator/helpers/index.ts +1 -0
- package/services/frontend/src/orchestrator/i18n/de-DE.json +15 -1
- package/services/frontend/src/orchestrator/i18n/en-EU.json +15 -1
- package/services/frontend/src/orchestrator/i18n/sk.json +15 -1
- package/services/frontend/src/orchestrator/index.ts +34 -4
- package/services/frontend/src/orchestrator/router/routeParserHelper.ts +106 -1
- package/services/frontend/src/orchestrator/types/Widgets.ts +2 -0
- package/services/frontend/types.ts +1 -0
- package/src/README.md +404 -0
- package/src/types.ts +19 -5
- package/templates/README.md +0 -0
- package/types/index.ts +18 -4
- package/services/backend/.gitignore +0 -17
- package/services/frontend/.gitignore +0 -35
- package/yarn.lock +0 -2937
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
</template>
|
|
39
39
|
<v-list-item-title>{{ $t("core.toggle_theme") }}</v-list-item-title>
|
|
40
40
|
</v-list-item>
|
|
41
|
-
<v-list-item v-if="
|
|
41
|
+
<v-list-item v-if="isWinter()" @click="toggleSnow">
|
|
42
42
|
<template #prepend>
|
|
43
43
|
<v-icon>mdi-snowflake</v-icon>
|
|
44
44
|
</template>
|
|
@@ -145,13 +145,20 @@ const { getCompositionLinkForSlotName } = useRouteState()
|
|
|
145
145
|
|
|
146
146
|
const userLanguage = useLocalStorage<string>("user-language", null)
|
|
147
147
|
const userTheme = useLocalStorage<string>("user-theme", null)
|
|
148
|
+
const showSnow = useLocalStorage<boolean>("show-snow", true)
|
|
148
149
|
const { state: pageNavigation } = usePageNavigation()
|
|
149
150
|
|
|
150
151
|
const isOpen = ref(false)
|
|
151
152
|
const canRefresh = ref(true)
|
|
152
153
|
|
|
153
154
|
const requireAuthentication = configService.getRequireAuthentication()
|
|
154
|
-
const
|
|
155
|
+
const isWinter = () => {
|
|
156
|
+
const month = new Date().getMonth()
|
|
157
|
+
if (month === 11 || month === 0) {
|
|
158
|
+
return true
|
|
159
|
+
}
|
|
160
|
+
return false
|
|
161
|
+
}
|
|
155
162
|
const currentPageRoute = getCompositionLinkForSlotName(DEFAULT_SLOT_NAME)
|
|
156
163
|
|
|
157
164
|
const activeRouteName = computed(() => {
|
|
@@ -214,11 +221,16 @@ const openEgg = () =>
|
|
|
214
221
|
isOpen.value = false
|
|
215
222
|
eggMe("asteroids")
|
|
216
223
|
})
|
|
217
|
-
|
|
224
|
+
const toggleSnow = () => {
|
|
225
|
+
showSnow.value = !showSnow.value
|
|
226
|
+
eggMe("xmas")
|
|
227
|
+
}
|
|
218
228
|
onMounted(async () => {
|
|
219
|
-
if (
|
|
229
|
+
if (isWinter()) {
|
|
220
230
|
await awaitTime(600)
|
|
221
|
-
|
|
231
|
+
if (showSnow.value) {
|
|
232
|
+
eggMe("xmas")
|
|
233
|
+
}
|
|
222
234
|
}
|
|
223
235
|
})
|
|
224
236
|
</script>
|
|
@@ -112,7 +112,7 @@ const configurations = computed(() => [
|
|
|
112
112
|
// Methods
|
|
113
113
|
const handleWorkbenchClick = () => {
|
|
114
114
|
// Navigate to workbench or emit event
|
|
115
|
-
console.log("Navigate to
|
|
115
|
+
console.log("Navigate to racletteJS Workbench")
|
|
116
116
|
// router.push('/workbench') // Uncomment when route is available
|
|
117
117
|
}
|
|
118
118
|
</script>
|
|
@@ -169,7 +169,6 @@ export const usePluginApi = (
|
|
|
169
169
|
}
|
|
170
170
|
return getResult()
|
|
171
171
|
},
|
|
172
|
-
|
|
173
172
|
getQueryState: (actionId) => {
|
|
174
173
|
if (!actionId) return {}
|
|
175
174
|
const getResult = () => {
|
|
@@ -185,7 +184,6 @@ export const usePluginApi = (
|
|
|
185
184
|
console.error("not implemented yet")
|
|
186
185
|
},
|
|
187
186
|
get: (pathArray, filter) => {
|
|
188
|
-
const cacheString = createStableHash({ pathArray, filter })
|
|
189
187
|
const getResult = () => {
|
|
190
188
|
const { state } = getState(pathArray, filter)
|
|
191
189
|
return state
|
|
@@ -206,7 +204,6 @@ export const usePluginApi = (
|
|
|
206
204
|
payload,
|
|
207
205
|
})
|
|
208
206
|
},
|
|
209
|
-
|
|
210
207
|
updateWidgetState: (
|
|
211
208
|
payload: object,
|
|
212
209
|
widgetId: string = getWidgetId(),
|
|
@@ -7,11 +7,22 @@ import {
|
|
|
7
7
|
transformToOriginalState,
|
|
8
8
|
} from "@racletteOrchestrator/router/routeParserHelper"
|
|
9
9
|
import { compose, urlCompose } from "@racletteOrchestrator/router/routeParser"
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
clone,
|
|
12
|
+
mergeDeepRight,
|
|
13
|
+
pick,
|
|
14
|
+
evolve,
|
|
15
|
+
when,
|
|
16
|
+
is,
|
|
17
|
+
pickBy,
|
|
18
|
+
complement,
|
|
19
|
+
isNil,
|
|
20
|
+
} from "ramda"
|
|
11
21
|
import type { InteractionLink } from "@shared/types"
|
|
12
22
|
import {
|
|
13
23
|
getCompositionById,
|
|
14
24
|
getLocalizedName,
|
|
25
|
+
getCompositionIdByPath,
|
|
15
26
|
} from "@racletteCore/store/reducers/compositions/selectors"
|
|
16
27
|
import useInteractionLinks from "./useInteractionLinks"
|
|
17
28
|
|
|
@@ -48,9 +59,14 @@ export const useRouteState = () => {
|
|
|
48
59
|
)
|
|
49
60
|
|
|
50
61
|
const updateSlot = (slot: CompositionSlot) => {
|
|
51
|
-
const
|
|
62
|
+
const cleanedSlot = evolve(
|
|
63
|
+
{
|
|
64
|
+
slotParams: when(is(Object), pickBy(complement(isNil))),
|
|
65
|
+
},
|
|
66
|
+
slot,
|
|
67
|
+
)
|
|
52
68
|
|
|
53
|
-
|
|
69
|
+
const newUrl = getInteractionLinkUrl(cleanedSlot)
|
|
54
70
|
router.push({ path: "/" + newUrl })
|
|
55
71
|
}
|
|
56
72
|
|
|
@@ -112,7 +128,9 @@ export const useRouteState = () => {
|
|
|
112
128
|
console.error(`InteractionLink ${id} not found!`)
|
|
113
129
|
}
|
|
114
130
|
}
|
|
115
|
-
|
|
131
|
+
const getCurrentCompositionId = (compositionPath: string): string => {
|
|
132
|
+
return getCompositionIdByPath(compositionPath)
|
|
133
|
+
}
|
|
116
134
|
return {
|
|
117
135
|
composed,
|
|
118
136
|
compositionSlots,
|
|
@@ -122,6 +140,7 @@ export const useRouteState = () => {
|
|
|
122
140
|
removeSlot,
|
|
123
141
|
triggerInteractionLink,
|
|
124
142
|
triggerInteractionLinkById,
|
|
143
|
+
getCurrentCompositionId,
|
|
125
144
|
}
|
|
126
145
|
}
|
|
127
146
|
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { ref, type Ref } from "vue"
|
|
2
|
+
import getEnvVariable from "@racletteCore/lib/getEnvVariable"
|
|
3
|
+
import log from "@racletteCore/lib/logger"
|
|
4
|
+
|
|
5
|
+
export type OnAllWidgetsReady = (callback: () => void) => void
|
|
6
|
+
|
|
7
|
+
interface WidgetLifecycleHooks {
|
|
8
|
+
onAllWidgetsReady: OnAllWidgetsReady
|
|
9
|
+
onWidgetRendered: (element: HTMLElement) => void
|
|
10
|
+
onWidgetUnmounted: () => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface WidgetLifecycleManager {
|
|
14
|
+
createLifecycleHooks: (widgetId: string) => WidgetLifecycleHooks
|
|
15
|
+
allWidgetsReady: Ref<boolean>
|
|
16
|
+
reset: () => void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createWidgetLifecycleManager(
|
|
20
|
+
totalWidgets: number,
|
|
21
|
+
): WidgetLifecycleManager {
|
|
22
|
+
const renderedWidgets = ref(new Set<string>())
|
|
23
|
+
const allWidgetsReady = ref(false)
|
|
24
|
+
const readyCallbacks = new Set<() => void>()
|
|
25
|
+
const observers = new Map<string, MutationObserver>()
|
|
26
|
+
|
|
27
|
+
const DEV_MODE =
|
|
28
|
+
getEnvVariable("RACLETTE_DEBUG_MODE") === "true" ||
|
|
29
|
+
!getEnvVariable("RACLETTE_DEBUG_MODE")
|
|
30
|
+
|
|
31
|
+
const checkAllReady = () => {
|
|
32
|
+
if (renderedWidgets.value.size === totalWidgets && !allWidgetsReady.value) {
|
|
33
|
+
allWidgetsReady.value = true
|
|
34
|
+
|
|
35
|
+
if (DEV_MODE) {
|
|
36
|
+
log.widgets(
|
|
37
|
+
"useWidgetLifecycle",
|
|
38
|
+
`✅ All ${totalWidgets} widgets are fully rendered`,
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
readyCallbacks.forEach((callback) => callback())
|
|
43
|
+
readyCallbacks.clear()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const hasVisibleContent = (element: HTMLElement): boolean => {
|
|
48
|
+
// Check if element has any child nodes
|
|
49
|
+
if (element.childNodes.length === 0) {
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check if element has visible dimensions
|
|
54
|
+
const rect = element.getBoundingClientRect()
|
|
55
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check for text content or child elements
|
|
60
|
+
const hasTextContent = element.textContent?.trim().length > 0
|
|
61
|
+
const hasChildElements = element.children.length > 0
|
|
62
|
+
|
|
63
|
+
return hasTextContent || hasChildElements
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const registerWidgetAsRendered = (id: string) => {
|
|
67
|
+
if (!renderedWidgets.value.has(id)) {
|
|
68
|
+
renderedWidgets.value.add(id)
|
|
69
|
+
|
|
70
|
+
if (DEV_MODE) {
|
|
71
|
+
log.widgets(
|
|
72
|
+
"useWidgetLifecycle",
|
|
73
|
+
`📦 Widget rendered: ${id} (${renderedWidgets.value.size}/${totalWidgets})`,
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
checkAllReady()
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const unregisterWidget = (id: string) => {
|
|
82
|
+
if (DEV_MODE) {
|
|
83
|
+
log.widgets("useWidgetLifecycle", `📤 Widget unregistered: ${id}`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Clean up observer
|
|
87
|
+
const observer = observers.get(id)
|
|
88
|
+
if (observer) {
|
|
89
|
+
observer.disconnect()
|
|
90
|
+
observers.delete(id)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
renderedWidgets.value.delete(id)
|
|
94
|
+
|
|
95
|
+
if (renderedWidgets.value.size < totalWidgets) {
|
|
96
|
+
allWidgetsReady.value = false
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const observeWidgetRendering = (id: string, element: HTMLElement) => {
|
|
101
|
+
// Check immediately if content is already rendered
|
|
102
|
+
if (hasVisibleContent(element)) {
|
|
103
|
+
registerWidgetAsRendered(id)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Otherwise, observe for changes
|
|
108
|
+
const observer = new MutationObserver(() => {
|
|
109
|
+
if (hasVisibleContent(element)) {
|
|
110
|
+
registerWidgetAsRendered(id)
|
|
111
|
+
observer.disconnect()
|
|
112
|
+
observers.delete(id)
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
observer.observe(element, {
|
|
117
|
+
childList: true,
|
|
118
|
+
subtree: true,
|
|
119
|
+
characterData: true,
|
|
120
|
+
attributes: true,
|
|
121
|
+
attributeFilter: ["style", "class"],
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
observers.set(id, observer)
|
|
125
|
+
|
|
126
|
+
if (DEV_MODE) {
|
|
127
|
+
log.widgets(
|
|
128
|
+
"useWidgetLifecycle",
|
|
129
|
+
`👀 Observing widget for content: ${id}`,
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const createLifecycleHooks = (widgetId: string): WidgetLifecycleHooks => ({
|
|
135
|
+
onAllWidgetsReady: (callback: () => void) => {
|
|
136
|
+
if (allWidgetsReady.value) {
|
|
137
|
+
callback()
|
|
138
|
+
} else {
|
|
139
|
+
readyCallbacks.add(callback)
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
onWidgetRendered: (element: HTMLElement) => {
|
|
143
|
+
observeWidgetRendering(widgetId, element)
|
|
144
|
+
},
|
|
145
|
+
onWidgetUnmounted: () => unregisterWidget(widgetId),
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
const reset = () => {
|
|
149
|
+
// Disconnect all observers
|
|
150
|
+
observers.forEach((observer) => observer.disconnect())
|
|
151
|
+
observers.clear()
|
|
152
|
+
renderedWidgets.value.clear()
|
|
153
|
+
allWidgetsReady.value = false
|
|
154
|
+
readyCallbacks.clear()
|
|
155
|
+
|
|
156
|
+
if (DEV_MODE) {
|
|
157
|
+
log.widgets("useWidgetLifecycle", "🔄 Widget lifecycle manager reset")
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
createLifecycleHooks,
|
|
163
|
+
allWidgetsReady,
|
|
164
|
+
reset,
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"subtitle": "Konfiguriere eine Landingpage in der Workbench, damit diese Seite verschwindet! Sieh dir den Dialog zur Erstellung/Bearbeitung von InteractionLinks für den Landingpage-Umschalter an.",
|
|
6
6
|
"subtitleUser": "Deine Schnittstelle zur käsigen Seite der Dinge",
|
|
7
7
|
"noLandingPage": "Keine Landingpage konfiguriert",
|
|
8
|
-
"visitWorkbench": "Besuche die
|
|
8
|
+
"visitWorkbench": "Besuche die racletteJS-Workbench, um dein Portal zu konfigurieren und deine bevorzugte Landingpage einzurichten.",
|
|
9
9
|
"goToWorkbench": "Workbench öffnen",
|
|
10
10
|
"configurationsTitle": "Was du in der Workbench konfigurieren kannst",
|
|
11
11
|
"compositionInfoTitle": "Kompositionen",
|
|
@@ -75,8 +75,22 @@
|
|
|
75
75
|
"openInFloatingWindow": "In extrafenster öffnen",
|
|
76
76
|
"showDeleted": "Zeige gelöschte",
|
|
77
77
|
"hideDeleted": "Verstecke gelöschte",
|
|
78
|
+
"selectAll": "Alle auswählen",
|
|
79
|
+
"selected": "ausgewählt",
|
|
80
|
+
"noItemsSelected": "Keine Elemente ausgewählt. Bitte wählen Sie mindestens ein Element aus, um fortzufahren.",
|
|
78
81
|
"cancel": "Abbrechen",
|
|
79
82
|
"close": "Schließen",
|
|
83
|
+
"itemsCount": "Anzahl der Elemente",
|
|
84
|
+
"selectedItems": "Ausgewählte Elemente",
|
|
85
|
+
"selectItemsToExport": "Elemente zum Exportieren auswählen",
|
|
86
|
+
"export": "Exportieren",
|
|
87
|
+
"selectItemsToImport": "Elemente zum Importieren auswählen",
|
|
88
|
+
"import": "Importieren",
|
|
89
|
+
"exportSummary": "Export-Zusammenfassung",
|
|
90
|
+
"exportSuccessMessage": "Elemente wurden erfolgreich exportiert",
|
|
91
|
+
"importSummary": "Import-Zusammenfassung",
|
|
92
|
+
"importSuccessMessage": "Elemente bereit zum Importieren",
|
|
93
|
+
"saveImported": "Importierte Elemente speichern",
|
|
80
94
|
"clear": "Leeren",
|
|
81
95
|
"copyToClipboard$name": "{name} in Zwischenablage kopieren",
|
|
82
96
|
"timeAgo": "vor {time}",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"subtitle": "Configure a Landingpage in the Workbench to make this Page disapear! See the InteractionLink Creation/Edit Dialogue for the landingpage toggle",
|
|
6
6
|
"subtitleUser": "Your interface to the cheesy side of things",
|
|
7
7
|
"noLandingPage": "No landing page configured",
|
|
8
|
-
"visitWorkbench": "Visit the
|
|
8
|
+
"visitWorkbench": "Visit the racletteJS Workbench to configure your portal and set up your preferred landing page.",
|
|
9
9
|
"goToWorkbench": "Open Workbench",
|
|
10
10
|
"configurationsTitle": "What you can configure in the Workbench",
|
|
11
11
|
"compositionInfoTitle": "Compositions",
|
|
@@ -75,8 +75,22 @@
|
|
|
75
75
|
"openInFloatingWindow": "Open in floating window",
|
|
76
76
|
"showDeleted": "Show deleted",
|
|
77
77
|
"hideDeleted": "Hide deleted",
|
|
78
|
+
"selectAll": "Select All",
|
|
79
|
+
"selected": "selected",
|
|
80
|
+
"noItemsSelected": "No items selected. Please select at least one item to continue.",
|
|
78
81
|
"cancel": "Cancel",
|
|
79
82
|
"close": "Close",
|
|
83
|
+
"itemsCount": "Items count",
|
|
84
|
+
"selectedItems": "Selected items",
|
|
85
|
+
"selectItemsToExport": "Select Items to Export",
|
|
86
|
+
"export": "Export",
|
|
87
|
+
"selectItemsToImport": "Select Items to Import",
|
|
88
|
+
"import": "Import",
|
|
89
|
+
"exportSummary": "Export Summary",
|
|
90
|
+
"exportSuccessMessage": "Items have been successfully exported",
|
|
91
|
+
"importSummary": "Import Summary",
|
|
92
|
+
"importSuccessMessage": "Items ready to import",
|
|
93
|
+
"saveImported": "Save Imported Items",
|
|
80
94
|
"clear": "Clear",
|
|
81
95
|
"copyToClipboard$name": "Copy {name} to clipboard",
|
|
82
96
|
"timeAgo": "{time} ago",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"subtitle": "Nakonfigurujte vstupnú stránku vo Workbench, aby táto stránka zmizla! Pozrite si dialóg na vytváranie/upravenie InteractionLink pre prepínač vstupnej stránky.",
|
|
6
6
|
"subtitleUser": "Vaše rozhranie k syrovej strane vecí",
|
|
7
7
|
"noLandingPage": "Vstupná stránka nie je nakonfigurovaná",
|
|
8
|
-
"visitWorkbench": "Navštívte
|
|
8
|
+
"visitWorkbench": "Navštívte racletteJS Workbench na konfiguráciu vášho portálu a nastavenie preferovanej vstupnej stránky.",
|
|
9
9
|
"goToWorkbench": "Otvoriť Workbench",
|
|
10
10
|
"configurationsTitle": "Čo môžete nakonfigurovať vo Workbench",
|
|
11
11
|
"compositionInfoTitle": "Kompozície",
|
|
@@ -75,8 +75,22 @@
|
|
|
75
75
|
"openInFloatingWindow": "Otvoriť v plávajúcom okne",
|
|
76
76
|
"showDeleted": "relácia vymazaná",
|
|
77
77
|
"hideDeleted": "skryť odstránené",
|
|
78
|
+
"selectAll": "Vybrať všetko",
|
|
79
|
+
"selected": "vybrané",
|
|
80
|
+
"noItemsSelected": "Nie sú vybrané žiadne položky. Vyberte aspoň jednu položku pre pokračovanie.",
|
|
78
81
|
"cancel": "Zrušiť",
|
|
79
82
|
"close": "Zavrieť",
|
|
83
|
+
"itemsCount": "Počet položiek",
|
|
84
|
+
"selectedItems": "Vybrané položky",
|
|
85
|
+
"selectItemsToExport": "Vyberte položky na export",
|
|
86
|
+
"export": "Exportovať",
|
|
87
|
+
"selectItemsToImport": "Vyberte položky na import",
|
|
88
|
+
"import": "Importovať",
|
|
89
|
+
"exportSummary": "Súhrn exportu",
|
|
90
|
+
"exportSuccessMessage": "Položky boli úspešne exportované",
|
|
91
|
+
"importSummary": "Súhrn importu",
|
|
92
|
+
"importSuccessMessage": "Položky pripravené na import",
|
|
93
|
+
"saveImported": "Uložiť importované položky",
|
|
80
94
|
"clear": "Vyčistiť",
|
|
81
95
|
"copyToClipboard$name": "Skopírovať {name} do schránky",
|
|
82
96
|
"timeAgo": "pred {time}",
|
|
@@ -2,25 +2,55 @@ import type { App as VueApp } from "vue"
|
|
|
2
2
|
import createRacletteApp, { corePluginApi } from "@racletteCore/main"
|
|
3
3
|
import { createVueApp, createLoginVueApp } from "./main"
|
|
4
4
|
import { initApp } from "./setup/application"
|
|
5
|
+
import { validate } from "uuid"
|
|
5
6
|
export * from "./exports"
|
|
6
7
|
|
|
7
8
|
const VUE_MOUNTING_ELEMENT = "#app"
|
|
8
9
|
let currentApp: VueApp | undefined
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Check if URL matches :workSession/id={uuid} pattern and extract sessionId
|
|
13
|
+
* Supports racletteJS route format: /:workSession/id={uuid}
|
|
14
|
+
*/
|
|
15
|
+
const extractSessionIdFromUrl = (): string | null => {
|
|
16
|
+
const path = window.location.pathname + (window.location.hash || "")
|
|
17
|
+
// Match /:workSession/id={uuid} pattern
|
|
18
|
+
// The route parser will handle this, but we need to extract it here for bootstrapping
|
|
19
|
+
const workSessionMatch = path.match(/\/:workSession\/id=([^/&#]+)/)
|
|
20
|
+
|
|
21
|
+
if (workSessionMatch && workSessionMatch[1]) {
|
|
22
|
+
const sessionId = workSessionMatch[1]
|
|
23
|
+
// Validate UUID format
|
|
24
|
+
if (validate(sessionId)) {
|
|
25
|
+
return sessionId
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
10
32
|
const startApp = async () => {
|
|
11
33
|
try {
|
|
12
|
-
|
|
34
|
+
// Check if URL indicates a work session
|
|
35
|
+
const sessionId = extractSessionIdFromUrl()
|
|
36
|
+
|
|
37
|
+
const res = await createRacletteApp({
|
|
38
|
+
requireAuth: !sessionId, // Skip auth requirement if sessionId present
|
|
39
|
+
sessionId: sessionId || undefined,
|
|
40
|
+
})
|
|
13
41
|
|
|
14
42
|
if (currentApp) {
|
|
15
43
|
currentApp.unmount()
|
|
16
44
|
currentApp = undefined
|
|
17
45
|
}
|
|
18
46
|
|
|
19
|
-
if (!res.authenticated) {
|
|
47
|
+
if (!res.authenticated && !sessionId) {
|
|
20
48
|
const noUsers = res.code === "RCLT_DB_NO_USERS"
|
|
21
|
-
|
|
49
|
+
if (res.login) {
|
|
50
|
+
currentApp = await createLoginVueApp(VUE_MOUNTING_ELEMENT, res.login, noUsers)
|
|
51
|
+
}
|
|
22
52
|
} else {
|
|
23
|
-
currentApp = createVueApp(VUE_MOUNTING_ELEMENT)
|
|
53
|
+
currentApp = await createVueApp(VUE_MOUNTING_ELEMENT)
|
|
24
54
|
await initApp(corePluginApi)
|
|
25
55
|
}
|
|
26
56
|
} catch (err) {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { compose, type ComposedRouteState } from "./routeParser"
|
|
2
2
|
import { $store } from "@racletteCore"
|
|
3
|
+
import { validate } from "uuid"
|
|
4
|
+
import type { Composition } from "@shared/types/core/Composition.types"
|
|
5
|
+
import type { InteractionLink } from "@shared/types/core/InteractionLink.types"
|
|
3
6
|
|
|
4
7
|
export type WidgetLayoutType = "default" | "spaced" | "card" | "border" | "full"
|
|
5
8
|
|
|
@@ -15,11 +18,50 @@ export const DEFAULT_SLOT_NAME = "page"
|
|
|
15
18
|
export const DEFAULT_MODAL_SLOT_NAME = "modal"
|
|
16
19
|
export const DEFAULT_INLINE_SLOT_NAME = "inline"
|
|
17
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Reserved composition name for work sessions
|
|
23
|
+
* When detected, triggers special handling for shared sessions
|
|
24
|
+
*/
|
|
25
|
+
export const WORK_SESSION_COMPOSITION_NAME = "workSession"
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Extract sessionId from workSession composition route
|
|
29
|
+
* Handles URLs like /workSession/id={uuid}
|
|
30
|
+
* Returns the UUID if found, null otherwise
|
|
31
|
+
*/
|
|
32
|
+
const extractWorkSessionId = (route: string): string | null => {
|
|
33
|
+
// Parse the route to check if it contains workSession composition
|
|
34
|
+
const composed = compose(route)
|
|
35
|
+
|
|
36
|
+
// Check if any composition in the route is "workSession"
|
|
37
|
+
for (const [composition, paramsArray] of composed.map) {
|
|
38
|
+
if (composition === WORK_SESSION_COMPOSITION_NAME) {
|
|
39
|
+
// Extract id parameter from the first params object
|
|
40
|
+
const params =
|
|
41
|
+
Array.isArray(paramsArray) && paramsArray.length > 0
|
|
42
|
+
? paramsArray[0]
|
|
43
|
+
: {}
|
|
44
|
+
|
|
45
|
+
if (typeof params === "object" && params !== null && "id" in params) {
|
|
46
|
+
const sessionId = params.id as string
|
|
47
|
+
// Validate UUID format
|
|
48
|
+
if (validate(sessionId)) {
|
|
49
|
+
return sessionId
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
|
|
18
58
|
export const parseRouteToCompositionSlots = (
|
|
19
59
|
route: string,
|
|
20
60
|
defaultSlotName = DEFAULT_SLOT_NAME,
|
|
21
61
|
addDefaultSlotIfMissing = false,
|
|
22
62
|
) => {
|
|
63
|
+
// If it's a workSession route, we'll handle it specially
|
|
64
|
+
// For now, parse normally - the composition will be updated after socket connection
|
|
23
65
|
const composed = compose(route)
|
|
24
66
|
return parseComposedToCompositionSlots(
|
|
25
67
|
composed,
|
|
@@ -27,6 +69,25 @@ export const parseRouteToCompositionSlots = (
|
|
|
27
69
|
addDefaultSlotIfMissing,
|
|
28
70
|
)
|
|
29
71
|
}
|
|
72
|
+
// Fix number and boolean params
|
|
73
|
+
const coerceParams = (
|
|
74
|
+
params: Record<string, string>,
|
|
75
|
+
): Record<string, string | number | boolean> =>
|
|
76
|
+
Object.fromEntries(
|
|
77
|
+
Object.entries(params).map(([key, value]) => {
|
|
78
|
+
if (value === "true") return [key, true]
|
|
79
|
+
if (value === "false") return [key, false]
|
|
80
|
+
if (value !== "" && !isNaN(Number(value))) return [key, Number(value)]
|
|
81
|
+
return [key, value]
|
|
82
|
+
}),
|
|
83
|
+
)
|
|
84
|
+
/**
|
|
85
|
+
* Check if a route contains a workSession composition
|
|
86
|
+
* Returns the sessionId if found, null otherwise
|
|
87
|
+
*/
|
|
88
|
+
export const getWorkSessionIdFromRoute = (route: string): string | null => {
|
|
89
|
+
return extractWorkSessionId(route)
|
|
90
|
+
}
|
|
30
91
|
|
|
31
92
|
export const parseComposedToCompositionSlots = (
|
|
32
93
|
composed: ComposedRouteState,
|
|
@@ -37,7 +98,9 @@ export const parseComposedToCompositionSlots = (
|
|
|
37
98
|
([slotValue, paramsArray, ...slots]) => ({
|
|
38
99
|
slotName: (slots[0] as string) || defaultSlotName,
|
|
39
100
|
slotValue: slotValue as string,
|
|
40
|
-
slotParams: (
|
|
101
|
+
slotParams: coerceParams(
|
|
102
|
+
(paramsArray as Record<string, string>[])[0] || {},
|
|
103
|
+
),
|
|
41
104
|
}),
|
|
42
105
|
)
|
|
43
106
|
|
|
@@ -75,3 +138,45 @@ export const transformToOriginalState = (
|
|
|
75
138
|
// TODO: care about anchor
|
|
76
139
|
return { map, anchor: null }
|
|
77
140
|
}
|
|
141
|
+
export const findInteractionLinkByPathname = (
|
|
142
|
+
pathname: string,
|
|
143
|
+
): InteractionLink | null => {
|
|
144
|
+
const interactionLinks = $store.getState("interactionLinks")
|
|
145
|
+
const composition =
|
|
146
|
+
$store.selectors.compositions.getCompositionByPath(pathname)
|
|
147
|
+
|
|
148
|
+
if (!composition) {
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Find interactionLink that references this composition
|
|
153
|
+
const found = Object.values(interactionLinks).find(
|
|
154
|
+
(link: InteractionLink) => link.composition === composition._id,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
return found || null
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Get active compositions from current route
|
|
161
|
+
*/
|
|
162
|
+
export const getActiveCompositionsFromRoute = (
|
|
163
|
+
fullPath: string,
|
|
164
|
+
): Composition[] => {
|
|
165
|
+
const compositionSlots = parseRouteToCompositionSlots(
|
|
166
|
+
fullPath,
|
|
167
|
+
undefined,
|
|
168
|
+
true,
|
|
169
|
+
)
|
|
170
|
+
const compositions: Composition[] = []
|
|
171
|
+
|
|
172
|
+
compositionSlots.forEach((slot) => {
|
|
173
|
+
const composition = $store.selectors.compositions.getCompositionByPath(
|
|
174
|
+
slot.slotValue,
|
|
175
|
+
)
|
|
176
|
+
if (composition) {
|
|
177
|
+
compositions.push(composition)
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
return compositions
|
|
182
|
+
}
|