@raclettejs/core 0.1.19 → 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 +17 -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 +2 -3
- 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 +3 -1
- 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 +2 -3
- 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 +3 -1
- 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/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 +3 -1
- 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 +1 -2
- 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/dataHelper.ts +1 -1
- package/services/frontend/src/core/lib/eggs/snow.ts +49 -20
- 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 +1 -1
- package/services/frontend/src/orchestrator/i18n/en-EU.json +1 -1
- package/services/frontend/src/orchestrator/i18n/sk.json +1 -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
|
@@ -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",
|
|
@@ -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",
|
|
@@ -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",
|
|
@@ -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
|
+
}
|