@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.
Files changed (136) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/bin/cli.js +1 -1
  3. package/dev/README.md +4 -0
  4. package/dist/cli.js +135 -124
  5. package/dist/cli.js.map +4 -4
  6. package/dist/index.js +9 -8
  7. package/dist/index.js.map +4 -4
  8. package/package.json +9 -5
  9. package/services/backend/delay-api-debugger.sh +0 -0
  10. package/services/backend/package.json +1 -0
  11. package/services/backend/src/app.ts +4 -0
  12. package/services/backend/src/core/config/configService.ts +10 -1
  13. package/services/backend/src/core/events/eventEmitter.ts +32 -7
  14. package/services/backend/src/core/http/request-params.plugin.ts +2 -2
  15. package/services/backend/src/core/pluginSystem/pluginFastifyInstance.ts +4 -1
  16. package/services/backend/src/core/pluginSystem/pluginTypes.ts +3 -1
  17. package/services/backend/src/core/sockets/createSystemEventHandlers.ts +7 -0
  18. package/services/backend/src/core/sockets/index.ts +61 -13
  19. package/services/backend/src/core/sockets/socketManager.ts +452 -105
  20. package/services/backend/src/core/sockets/socketTypes.ts +13 -1
  21. package/services/backend/src/core/sockets/workSessionJoinPayload.ts +106 -0
  22. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.get.ts +3 -1
  23. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.getAll.ts +3 -1
  24. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.hardDelete.ts +16 -19
  25. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.patch.ts +3 -1
  26. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.post.ts +3 -1
  27. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/account/routes/route.account.remove.ts +3 -1
  28. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.service.ts +86 -3
  29. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/index.ts +2 -0
  30. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.get.ts +3 -1
  31. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.getAll.ts +3 -1
  32. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.hardDelete.ts +16 -19
  33. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.patch.ts +3 -1
  34. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.post.bulk.ts +60 -0
  35. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.post.ts +3 -1
  36. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.remove.ts +3 -1
  37. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/routes/route.composition.restore.ts +12 -15
  38. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.service.ts +96 -12
  39. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/index.ts +2 -0
  40. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.get.ts +3 -1
  41. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.getAll.ts +4 -2
  42. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.hardDelete.ts +12 -17
  43. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.patch.ts +3 -1
  44. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.post.bulk.ts +62 -0
  45. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.post.ts +3 -1
  46. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.remove.ts +3 -1
  47. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/routes/route.interactionLink.restore.ts +12 -15
  48. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/project.service.ts +1 -0
  49. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.get.ts +3 -1
  50. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.getAll.ts +4 -2
  51. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.hardDelete.ts +3 -1
  52. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.patch.ts +3 -1
  53. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.post.ts +4 -2
  54. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/project/routes/route.project.remove.ts +3 -1
  55. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/index.ts +2 -0
  56. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.get.ts +1 -0
  57. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.getAll.ts +1 -0
  58. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.hardDelete.ts +10 -15
  59. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.patch.ts +3 -1
  60. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.post.bulk.ts +60 -0
  61. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.post.ts +3 -1
  62. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.remove.ts +3 -1
  63. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/routes/route.tag.restore.ts +12 -15
  64. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/tag/tag.service.ts +73 -3
  65. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.get.ts +1 -0
  66. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.getAll.ts +1 -0
  67. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.hardDelete.ts +10 -15
  68. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.patch.ts +4 -6
  69. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.patchLastProject.ts +4 -2
  70. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.post.ts +3 -1
  71. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.remove.ts +3 -4
  72. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/user/routes/route.user.restore.ts +12 -15
  73. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/events/index.ts +45 -0
  74. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/helpers/crud.ts +12 -0
  75. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/helpers/payload.ts +21 -0
  76. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/index.ts +55 -0
  77. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/index.ts +19 -0
  78. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/route.workSession.get.ts +53 -0
  79. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/route.workSession.getAll.ts +39 -0
  80. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/route.workSession.hardDelete.ts +53 -0
  81. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/route.workSession.patch.ts +58 -0
  82. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/route.workSession.post.ts +53 -0
  83. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/routes/route.workSession.remove.ts +52 -0
  84. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/workSession.model.ts +65 -0
  85. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/workSession.schema.ts +125 -0
  86. package/services/backend/src/corePlugins/raclette__core/backend/datatypes/workSession/workSession.service.ts +351 -0
  87. package/services/backend/src/corePlugins/raclette__core/backend/index.ts +2 -0
  88. package/services/backend/src/corePlugins/raclette__core/frontend/generated-config.ts +159 -24
  89. package/services/backend/src/modules/authentication/index.ts +255 -4
  90. package/services/backend/src/modules/hooks/rate-limiter.ts +21 -0
  91. package/services/backend/src/types/custom-fastify.d.ts +17 -0
  92. package/services/backend/src/utils/request.utils.ts +4 -1
  93. package/services/backend/types/index.d.ts +15 -1
  94. package/services/backend/yarn.lock +13 -4
  95. package/services/frontend/index.html +1 -1
  96. package/services/frontend/provision/entrypoint.sh +0 -0
  97. package/services/frontend/src/core/lib/configService.ts +1 -1
  98. package/services/frontend/src/core/lib/data/dataApi.ts +10 -1
  99. package/services/frontend/src/core/lib/dataHelper.ts +1 -1
  100. package/services/frontend/src/core/lib/eggs/snow.ts +66 -48
  101. package/services/frontend/src/core/lib/helpers/index.ts +1 -0
  102. package/services/frontend/src/core/lib/helpers/removeEmpty.ts +69 -0
  103. package/services/frontend/src/core/lib/httpClient.ts +19 -0
  104. package/services/frontend/src/core/main.ts +20 -10
  105. package/services/frontend/src/core/setup/socket.ts +4 -3
  106. package/services/frontend/src/core/setup/socketClient.ts +31 -3
  107. package/services/frontend/src/core/setup/socketEvents.ts +92 -8
  108. package/services/frontend/src/core/store/index.ts +5 -1
  109. package/services/frontend/src/core/store/reducers/compositions/index.ts +2 -2
  110. package/services/frontend/src/core/store/reducers/interactionLinks/index.ts +1 -1
  111. package/services/frontend/src/core/store/reducers/widgets/reducers.ts +1 -2
  112. package/services/frontend/src/core/store/types/index.ts +0 -1
  113. package/services/frontend/src/core/types/PluginRegistrar.ts +8 -6
  114. package/services/frontend/src/core/types/Store.ts +1 -0
  115. package/services/frontend/src/orchestrator/components/composition/WidgetsLayoutLoader.vue +68 -8
  116. package/services/frontend/src/orchestrator/components/menu/UserMenu.vue +17 -5
  117. package/services/frontend/src/orchestrator/components/welcomeScreen/AdminWelcomeScreen.vue +1 -1
  118. package/services/frontend/src/orchestrator/composables/index.ts +1 -0
  119. package/services/frontend/src/orchestrator/composables/usePluginApi.ts +0 -3
  120. package/services/frontend/src/orchestrator/composables/useRouteState.ts +23 -4
  121. package/services/frontend/src/orchestrator/composables/useWidgetLifecycle.ts +166 -0
  122. package/services/frontend/src/orchestrator/helpers/index.ts +1 -0
  123. package/services/frontend/src/orchestrator/i18n/de-DE.json +15 -1
  124. package/services/frontend/src/orchestrator/i18n/en-EU.json +15 -1
  125. package/services/frontend/src/orchestrator/i18n/sk.json +15 -1
  126. package/services/frontend/src/orchestrator/index.ts +34 -4
  127. package/services/frontend/src/orchestrator/router/routeParserHelper.ts +106 -1
  128. package/services/frontend/src/orchestrator/types/Widgets.ts +2 -0
  129. package/services/frontend/types.ts +1 -0
  130. package/src/README.md +404 -0
  131. package/src/types.ts +19 -5
  132. package/templates/README.md +0 -0
  133. package/types/index.ts +18 -4
  134. package/services/backend/.gitignore +0 -17
  135. package/services/frontend/.gitignore +0 -35
  136. 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="isDecember()" @click="eggMe('xmas')">
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 isDecember = () => new Date().getMonth() === 11
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 (isDecember()) {
229
+ if (isWinter()) {
220
230
  await awaitTime(600)
221
- eggMe("xmas")
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 Raclette Workbench")
115
+ console.log("Navigate to racletteJS Workbench")
116
116
  // router.push('/workbench') // Uncomment when route is available
117
117
  }
118
118
  </script>
@@ -3,3 +3,4 @@ export * from "./useRouteState"
3
3
  export * from "./useCurrentComposition"
4
4
  export * from "./useWidgets"
5
5
  export * from "./useInteractionLinks"
6
+ export * from "./useWidgetLifecycle"
@@ -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 { clone, mergeDeepRight, pick } from "ramda"
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 newUrl = getInteractionLinkUrl(slot)
62
+ const cleanedSlot = evolve(
63
+ {
64
+ slotParams: when(is(Object), pickBy(complement(isNil))),
65
+ },
66
+ slot,
67
+ )
52
68
 
53
- // router.push({ name: "app.space.page", params: { pageId: newUrl } })
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
+ }
@@ -1 +1,2 @@
1
1
  export * from "./loginUserNotifications"
2
+ export * from "./widgetLoader"
@@ -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 Raclette-Workbench, um dein Portal zu konfigurieren und deine bevorzugte Landingpage einzurichten.",
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 Raclette Workbench to configure your portal and set up your preferred landing page.",
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 Raclette Workbench na konfiguráciu vášho portálu a nastavenie preferovanej vstupnej stránky.",
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
- const res = await createRacletteApp()
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
- currentApp = createLoginVueApp(VUE_MOUNTING_ELEMENT, res.login, noUsers) // No await needed
49
+ if (res.login) {
50
+ currentApp = await createLoginVueApp(VUE_MOUNTING_ELEMENT, res.login, noUsers)
51
+ }
22
52
  } else {
23
- currentApp = createVueApp(VUE_MOUNTING_ELEMENT) // No await needed
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: (paramsArray as Record<string, string>[])[0] || {},
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
+ }
@@ -31,6 +31,8 @@ export type WidgetConf = {
31
31
  }
32
32
  }
33
33
 
34
+ export type OnAllWidgetsReady = (callback: () => void) => void
35
+
34
36
  export type ConfigurableParameters = Record<
35
37
  string,
36
38
  EditorSettings & { defaultValue: any }
@@ -1,2 +1,3 @@
1
1
  export type * from "./src/core/types"
2
2
  export type * from "./src/orchestrator/types"
3
+ export type * from "./src/core/store/types"