@vc-shell/framework 1.2.1 → 1.2.3-beta.0

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 (231) hide show
  1. package/core/composables/index.ts +2 -0
  2. package/core/composables/useAssets/index.ts +72 -28
  3. package/core/composables/useAsync/index.ts +4 -1
  4. package/core/composables/useBladeRegistry/index.ts +6 -5
  5. package/core/composables/useBreadcrumbs/index.ts +4 -1
  6. package/core/composables/useErrorHandler/index.ts +4 -1
  7. package/core/composables/useFunctions/debounce.ts +0 -1
  8. package/core/composables/useFunctions/delay.ts +0 -1
  9. package/core/composables/useFunctions/index.ts +0 -1
  10. package/core/composables/useFunctions/once.ts +0 -1
  11. package/core/composables/useFunctions/sleep.ts +0 -1
  12. package/core/composables/useFunctions/throttle.ts +0 -1
  13. package/core/composables/useGlobalSearch/index.ts +3 -3
  14. package/core/composables/useMenuService/index.ts +5 -2
  15. package/core/composables/useNotifications/index.ts +5 -2
  16. package/core/composables/useTheme/index.ts +4 -1
  17. package/core/composables/useUser/index.ts +189 -20
  18. package/core/composables/useWidgets/index.ts +5 -2
  19. package/core/constants/defaults.ts +76 -0
  20. package/core/constants/index.ts +2 -0
  21. package/core/constants/ui.ts +68 -0
  22. package/core/interceptors/index.ts +5 -2
  23. package/core/plugins/ai-agent/README.md +336 -0
  24. package/core/plugins/ai-agent/components/VcAiAgentPanel.vue +125 -0
  25. package/core/plugins/ai-agent/components/_internal/VcAiAgentHeader.vue +182 -0
  26. package/core/plugins/ai-agent/components/_internal/VcAiAgentIframe.vue +77 -0
  27. package/core/plugins/ai-agent/components/index.ts +1 -0
  28. package/core/plugins/ai-agent/composables/index.ts +4 -0
  29. package/core/plugins/ai-agent/composables/useAiAgent.ts +231 -0
  30. package/core/plugins/ai-agent/composables/useAiAgentContext.ts +280 -0
  31. package/core/plugins/ai-agent/constants.ts +89 -0
  32. package/core/plugins/ai-agent/index.ts +91 -0
  33. package/core/plugins/ai-agent/services/ai-agent-service.ts +598 -0
  34. package/core/plugins/ai-agent/types.ts +310 -0
  35. package/core/plugins/modularity/index.ts +8 -6
  36. package/core/plugins/modularity/loader.ts +36 -33
  37. package/core/plugins/signalR/index.ts +6 -3
  38. package/core/services/app-bar-menu-service.ts +4 -1
  39. package/core/services/dashboard-service.ts +4 -1
  40. package/core/services/index.ts +2 -0
  41. package/core/services/menu-service.ts +4 -1
  42. package/core/services/settings-menu-service.ts +4 -1
  43. package/core/services/toolbar-service.ts +18 -3
  44. package/core/services/widget-service.ts +7 -4
  45. package/core/types/index.ts +3 -0
  46. package/core/types/services.ts +194 -0
  47. package/core/utilities/errorTypes.ts +126 -0
  48. package/core/utilities/index.ts +2 -0
  49. package/core/utilities/logger.ts +120 -0
  50. package/dist/core/composables/useAssets/index.d.ts.map +1 -1
  51. package/dist/core/composables/useAsync/index.d.ts.map +1 -1
  52. package/dist/core/composables/useBladeRegistry/index.d.ts.map +1 -1
  53. package/dist/core/composables/useBreadcrumbs/index.d.ts.map +1 -1
  54. package/dist/core/composables/useErrorHandler/index.d.ts.map +1 -1
  55. package/dist/core/composables/useFunctions/debounce.d.ts.map +1 -1
  56. package/dist/core/composables/useFunctions/delay.d.ts.map +1 -1
  57. package/dist/core/composables/useFunctions/index.d.ts.map +1 -1
  58. package/dist/core/composables/useFunctions/once.d.ts.map +1 -1
  59. package/dist/core/composables/useFunctions/sleep.d.ts.map +1 -1
  60. package/dist/core/composables/useFunctions/throttle.d.ts.map +1 -1
  61. package/dist/core/composables/useGlobalSearch/index.d.ts.map +1 -1
  62. package/dist/core/composables/useMenuService/index.d.ts.map +1 -1
  63. package/dist/core/composables/useNotifications/index.d.ts.map +1 -1
  64. package/dist/core/composables/useTheme/index.d.ts.map +1 -1
  65. package/dist/core/composables/useUser/index.d.ts +8 -0
  66. package/dist/core/composables/useUser/index.d.ts.map +1 -1
  67. package/dist/core/composables/useWidgets/index.d.ts.map +1 -1
  68. package/dist/core/constants/defaults.d.ts +63 -0
  69. package/dist/core/constants/defaults.d.ts.map +1 -0
  70. package/dist/core/constants/index.d.ts +2 -0
  71. package/dist/core/constants/index.d.ts.map +1 -1
  72. package/dist/core/constants/ui.d.ts +50 -0
  73. package/dist/core/constants/ui.d.ts.map +1 -0
  74. package/dist/core/interceptors/index.d.ts.map +1 -1
  75. package/dist/core/plugins/ai-agent/components/VcAiAgentPanel.vue.d.ts +3 -0
  76. package/dist/core/plugins/ai-agent/components/VcAiAgentPanel.vue.d.ts.map +1 -0
  77. package/dist/core/plugins/ai-agent/components/_internal/VcAiAgentHeader.vue.d.ts +15 -0
  78. package/dist/core/plugins/ai-agent/components/_internal/VcAiAgentHeader.vue.d.ts.map +1 -0
  79. package/dist/core/plugins/ai-agent/components/_internal/VcAiAgentIframe.vue.d.ts +10 -0
  80. package/dist/core/plugins/ai-agent/components/_internal/VcAiAgentIframe.vue.d.ts.map +1 -0
  81. package/dist/core/plugins/ai-agent/components/index.d.ts +2 -0
  82. package/dist/core/plugins/ai-agent/components/index.d.ts.map +1 -0
  83. package/dist/core/plugins/ai-agent/composables/index.d.ts +4 -0
  84. package/dist/core/plugins/ai-agent/composables/index.d.ts.map +1 -0
  85. package/dist/core/plugins/ai-agent/composables/useAiAgent.d.ts +95 -0
  86. package/dist/core/plugins/ai-agent/composables/useAiAgent.d.ts.map +1 -0
  87. package/dist/core/plugins/ai-agent/composables/useAiAgentContext.d.ts +55 -0
  88. package/dist/core/plugins/ai-agent/composables/useAiAgentContext.d.ts.map +1 -0
  89. package/dist/core/plugins/ai-agent/constants.d.ts +47 -0
  90. package/dist/core/plugins/ai-agent/constants.d.ts.map +1 -0
  91. package/dist/core/plugins/ai-agent/index.d.ts +48 -0
  92. package/dist/core/plugins/ai-agent/index.d.ts.map +1 -0
  93. package/dist/core/plugins/ai-agent/services/ai-agent-service.d.ts +45 -0
  94. package/dist/core/plugins/ai-agent/services/ai-agent-service.d.ts.map +1 -0
  95. package/dist/core/plugins/ai-agent/types.d.ts +258 -0
  96. package/dist/core/plugins/ai-agent/types.d.ts.map +1 -0
  97. package/dist/core/plugins/modularity/index.d.ts.map +1 -1
  98. package/dist/core/plugins/modularity/loader.d.ts.map +1 -1
  99. package/dist/core/plugins/signalR/index.d.ts.map +1 -1
  100. package/dist/core/services/app-bar-menu-service.d.ts.map +1 -1
  101. package/dist/core/services/dashboard-service.d.ts.map +1 -1
  102. package/dist/core/services/menu-service.d.ts.map +1 -1
  103. package/dist/core/services/settings-menu-service.d.ts.map +1 -1
  104. package/dist/core/services/toolbar-service.d.ts.map +1 -1
  105. package/dist/core/services/widget-service.d.ts.map +1 -1
  106. package/dist/core/types/index.d.ts.map +1 -1
  107. package/dist/core/types/services.d.ts +169 -0
  108. package/dist/core/types/services.d.ts.map +1 -0
  109. package/dist/core/utilities/errorTypes.d.ts +61 -0
  110. package/dist/core/utilities/errorTypes.d.ts.map +1 -0
  111. package/dist/core/utilities/index.d.ts +2 -0
  112. package/dist/core/utilities/index.d.ts.map +1 -1
  113. package/dist/core/utilities/logger.d.ts +259 -0
  114. package/dist/core/utilities/logger.d.ts.map +1 -0
  115. package/dist/framework.js +9623 -8417
  116. package/dist/index.css +1 -1
  117. package/dist/index.d.ts +19 -0
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/injection-keys.d.ts +21 -6
  120. package/dist/injection-keys.d.ts.map +1 -1
  121. package/dist/shared/components/app-switcher/composables/useAppSwitcher/index.d.ts.map +1 -1
  122. package/dist/shared/components/blade-navigation/components/vc-blade-navigation/vc-blade-navigation.vue.d.ts.map +1 -1
  123. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts.map +1 -1
  124. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeActions.d.ts.map +1 -1
  125. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeRouteResolver.d.ts.map +1 -1
  126. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/routerUtils.d.ts.map +1 -1
  127. package/dist/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.d.ts.map +1 -1
  128. package/dist/shared/components/draggable-dashboard/composables/useLayoutPersistence.d.ts.map +1 -1
  129. package/dist/shared/components/notifications/composables/useContainer/index.d.ts.map +1 -1
  130. package/dist/shared/components/notifications/composables/useInstance/index.d.ts.map +1 -1
  131. package/dist/shared/components/notifications/core/notification.d.ts.map +1 -1
  132. package/dist/shared/components/popup-handler/components/vc-popup-container/vc-popup-container.vue.d.ts.map +1 -1
  133. package/dist/shared/components/sign-in/useExternalProvider.d.ts.map +1 -1
  134. package/dist/shared/composables/index.d.ts +1 -0
  135. package/dist/shared/composables/index.d.ts.map +1 -1
  136. package/dist/shared/composables/useExternalWidgets.d.ts.map +1 -1
  137. package/dist/shared/composables/useMenuExpanded.d.ts.map +1 -1
  138. package/dist/shared/composables/useTableSelection.d.ts +57 -0
  139. package/dist/shared/composables/useTableSelection.d.ts.map +1 -0
  140. package/dist/shared/composables/useTableSort.d.ts.map +1 -1
  141. package/dist/shared/modules/assets-manager/components/assets-manager/assets-manager.vue.d.ts.map +1 -1
  142. package/dist/shared/pages/LoginPage/components/login/Login.vue.d.ts.map +1 -1
  143. package/dist/shared/utilities/colorUtils.d.ts +0 -6
  144. package/dist/shared/utilities/colorUtils.d.ts.map +1 -1
  145. package/dist/tsconfig.tsbuildinfo +1 -1
  146. package/dist/ui/components/atoms/vc-banner/vc-banner.vue.d.ts.map +1 -1
  147. package/dist/ui/components/atoms/vc-button/vc-button.vue.d.ts +0 -15
  148. package/dist/ui/components/atoms/vc-button/vc-button.vue.d.ts.map +1 -1
  149. package/dist/ui/components/atoms/vc-container/vc-container.vue.d.ts.map +1 -1
  150. package/dist/ui/components/atoms/vc-icon/vc-icon.vue.d.ts.map +1 -1
  151. package/dist/ui/components/atoms/vc-icon/vc-lucide-icon.vue.d.ts.map +1 -1
  152. package/dist/ui/components/atoms/vc-image/vc-image.vue.d.ts.map +1 -1
  153. package/dist/ui/components/atoms/vc-link/vc-link.vue.d.ts.map +1 -1
  154. package/dist/ui/components/atoms/vc-loading/vc-loading.vue.d.ts.map +1 -1
  155. package/dist/ui/components/atoms/vc-status/vc-status.vue.d.ts +0 -5
  156. package/dist/ui/components/atoms/vc-status/vc-status.vue.d.ts.map +1 -1
  157. package/dist/ui/components/atoms/vc-tooltip/vc-tooltip.vue.d.ts.map +1 -1
  158. package/dist/ui/components/atoms/vc-video/vc-video.vue.d.ts.map +1 -1
  159. package/dist/ui/components/atoms/vc-widget/vc-widget.vue.d.ts.map +1 -1
  160. package/dist/ui/components/molecules/vc-breadcrumbs/vc-breadcrumbs.vue.d.ts.map +1 -1
  161. package/dist/ui/components/molecules/vc-input/vc-input.vue.d.ts.map +1 -1
  162. package/dist/ui/components/molecules/vc-pagination/vc-pagination.vue.d.ts.map +1 -1
  163. package/dist/ui/components/molecules/vc-toast/vc-toast.vue.d.ts.map +1 -1
  164. package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
  165. package/dist/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/_internal/vc-blade-toolbar-button/vc-blade-toolbar-button.vue.d.ts.map +1 -1
  166. package/dist/ui/components/organisms/vc-blade/vc-blade.vue.d.ts.map +1 -1
  167. package/dist/ui/components/organisms/vc-login-form/vc-login-form.vue.d.ts.map +1 -1
  168. package/dist/ui/components/organisms/vc-table/_internal/vc-table-cell/vc-table-cell.vue.d.ts.map +1 -1
  169. package/dist/ui/components/organisms/vc-table/composables/useTableActions.d.ts.map +1 -1
  170. package/dist/ui/components/organisms/vc-table/composables/useTableColumnResize.d.ts.map +1 -1
  171. package/dist/ui/components/organisms/vc-table/composables/useTableRowReorder.d.ts.map +1 -1
  172. package/dist/ui/components/organisms/vc-table/composables/useTableSelection.d.ts.map +1 -1
  173. package/dist/ui/components/organisms/vc-table/composables/useTableState.d.ts.map +1 -1
  174. package/dist/{vendor-lodash-es-BqkGj3Jl.js → vendor-lodash-es-SgOIjJF8.js} +2 -0
  175. package/package.json +5 -5
  176. package/shared/components/app-switcher/composables/useAppSwitcher/index.ts +4 -1
  177. package/shared/components/blade-navigation/components/vc-blade-navigation/vc-blade-navigation.vue +67 -4
  178. package/shared/components/blade-navigation/composables/useBladeNavigation/index.ts +13 -10
  179. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeActions.ts +7 -4
  180. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeRouteResolver.ts +4 -1
  181. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/routerUtils.ts +4 -1
  182. package/shared/components/change-password/change-password.vue +1 -1
  183. package/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.ts +14 -5
  184. package/shared/components/draggable-dashboard/composables/useLayoutPersistence.ts +5 -2
  185. package/shared/components/index.ts +2 -0
  186. package/shared/components/notifications/composables/useContainer/index.ts +8 -6
  187. package/shared/components/notifications/composables/useInstance/index.ts +4 -1
  188. package/shared/components/notifications/core/notification.ts +10 -7
  189. package/shared/components/popup-handler/components/vc-popup-container/vc-popup-container.vue +20 -1
  190. package/shared/components/sign-in/useExternalProvider.ts +6 -4
  191. package/shared/composables/index.ts +1 -0
  192. package/shared/composables/useExternalWidgets.ts +7 -4
  193. package/shared/composables/useMenuExpanded.ts +15 -1
  194. package/shared/composables/useTableSelection.ts +151 -0
  195. package/shared/composables/useTableSort.ts +4 -4
  196. package/shared/modules/assets-manager/components/assets-manager/assets-manager.vue +6 -3
  197. package/shared/pages/LoginPage/components/login/Login.vue +4 -1
  198. package/shared/utilities/colorUtils.ts +5 -12
  199. package/ui/components/atoms/vc-banner/vc-banner.vue +4 -1
  200. package/ui/components/atoms/vc-button/vc-button.vue +2 -25
  201. package/ui/components/atoms/vc-container/vc-container.vue +12 -3
  202. package/ui/components/atoms/vc-icon/vc-icon.vue +0 -10
  203. package/ui/components/atoms/vc-icon/vc-lucide-icon.vue +5 -2
  204. package/ui/components/atoms/vc-image/vc-image.vue +4 -1
  205. package/ui/components/atoms/vc-link/vc-link.vue +59 -54
  206. package/ui/components/atoms/vc-loading/vc-loading.vue +4 -0
  207. package/ui/components/atoms/vc-status/vc-status.vue +0 -5
  208. package/ui/components/atoms/vc-status-icon/vc-status-icon.vue +4 -4
  209. package/ui/components/atoms/vc-tooltip/vc-tooltip.vue +8 -1
  210. package/ui/components/atoms/vc-video/vc-video.vue +4 -2
  211. package/ui/components/atoms/vc-widget/vc-widget.vue +4 -1
  212. package/ui/components/molecules/vc-breadcrumbs/vc-breadcrumbs.vue +7 -2
  213. package/ui/components/molecules/vc-input/vc-input.vue +0 -1
  214. package/ui/components/molecules/vc-pagination/vc-pagination.vue +6 -1
  215. package/ui/components/molecules/vc-rating/vc-rating.vue +1 -1
  216. package/ui/components/molecules/vc-textarea/vc-textarea.vue +1 -1
  217. package/ui/components/molecules/vc-toast/vc-toast.vue +11 -1
  218. package/ui/components/organisms/vc-app/vc-app.vue +22 -3
  219. package/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/_internal/vc-blade-toolbar-button/vc-blade-toolbar-button.vue +4 -1
  220. package/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/vc-blade-toolbar.vue +14 -14
  221. package/ui/components/organisms/vc-blade/vc-blade.vue +3 -1
  222. package/ui/components/organisms/vc-login-form/vc-login-form.vue +3 -1
  223. package/ui/components/organisms/vc-table/_internal/vc-table-cell/vc-table-cell.vue +34 -2
  224. package/ui/components/organisms/vc-table/composables/useTableActions.ts +7 -10
  225. package/ui/components/organisms/vc-table/composables/useTableColumnResize.ts +4 -1
  226. package/ui/components/organisms/vc-table/composables/useTableRowReorder.ts +5 -2
  227. package/ui/components/organisms/vc-table/composables/useTableSelection.ts +26 -18
  228. package/ui/components/organisms/vc-table/composables/useTableState.ts +4 -1
  229. package/core/services/global-search-service.ts +0 -36
  230. package/dist/core/services/global-search-service.d.ts +0 -10
  231. package/dist/core/services/global-search-service.d.ts.map +0 -1
@@ -11,6 +11,15 @@ import { useGridPosition, type CellSize } from "./useGridPosition";
11
11
  // Value of 80px is defined in DraggableDashboard.vue
12
12
  const CELL_HEIGHT = 80;
13
13
 
14
+ /** Minimum movement threshold in pixels to start dragging */
15
+ const DRAG_THRESHOLD = 3;
16
+ /** Gap between widgets in pixels */
17
+ const WIDGET_GAP = 20;
18
+ /** Delay in ms for animation style application */
19
+ const ANIMATION_STYLE_DELAY = 20;
20
+ /** Delay in ms for state reset after drag ends */
21
+ const STATE_RESET_DELAY = 50;
22
+
14
23
  /**
15
24
  * Return type for the useDashboardDragAndDrop composable.
16
25
  */
@@ -169,7 +178,7 @@ export function useDashboardDragAndDrop(
169
178
 
170
179
  if (originalWidget) {
171
180
  const cellSize = calculateCellSize();
172
- const widgetGap = 20; // Widget gap in pixels
181
+ const widgetGap = WIDGET_GAP;
173
182
 
174
183
  // Get the current position of the clone (where the user released the mouse)
175
184
  const cloneRect = dragClone.value.getBoundingClientRect();
@@ -215,7 +224,7 @@ export function useDashboardDragAndDrop(
215
224
  setTimeout(() => {
216
225
  originalWidget.style.transition = `transform var(--dashboard-transition-duration) var(--dashboard-transition-timing)`;
217
226
  originalWidget.style.transform = `translate(${finalX}px, ${finalY}px)`;
218
- }, 20); // Small delay for correct style application
227
+ }, ANIMATION_STYLE_DELAY);
219
228
  } else {
220
229
  // If the original is not found, simply remove the clone and update the data
221
230
  removeDragClone();
@@ -250,7 +259,7 @@ export function useDashboardDragAndDrop(
250
259
  isDragging.value = false;
251
260
  draggedWidget.value = null;
252
261
  resetPosition();
253
- }, 50);
262
+ }, STATE_RESET_DELAY);
254
263
  };
255
264
 
256
265
  // Mouse/touch event handler with passive listeners
@@ -284,8 +293,8 @@ export function useDashboardDragAndDrop(
284
293
 
285
294
  const currentCoords = getEventCoordinates(moveEvent);
286
295
 
287
- // Start dragging only if the mouse has moved more than 3 pixels
288
- if (hasMovedBeyondThreshold(currentCoords, 3)) {
296
+ // Start dragging only if the mouse has moved beyond threshold
297
+ if (hasMovedBeyondThreshold(currentCoords, DRAG_THRESHOLD)) {
289
298
  hasDragStarted = true;
290
299
  isDragging.value = true;
291
300
  draggedWidget.value = widget;
@@ -1,5 +1,8 @@
1
1
  import { useLocalStorage } from "@vueuse/core";
2
2
  import type { DashboardWidgetPosition } from "../types";
3
+ import { createLogger } from "../../../../core/utilities";
4
+
5
+ const logger = createLogger("use-layout-persistence");
3
6
 
4
7
  /**
5
8
  * Hook for managing the saving and loading of the dashboard layout
@@ -34,7 +37,7 @@ export function useLayoutPersistence(
34
37
  // Save to localStorage through the reactive variable
35
38
  savedLayout.value = layoutData;
36
39
  } catch (error) {
37
- console.error("Failed to save dashboard layout to localStorage:", error);
40
+ logger.error("Failed to save dashboard layout to localStorage:", error);
38
41
  }
39
42
  };
40
43
 
@@ -61,7 +64,7 @@ export function useLayoutPersistence(
61
64
 
62
65
  return true;
63
66
  } catch (error) {
64
- console.error("Failed to load dashboard layout from localStorage:", error);
67
+ logger.error("Failed to load dashboard layout from localStorage:", error);
65
68
  return false;
66
69
  }
67
70
  };
@@ -16,3 +16,5 @@ export * from "./generic-dropdown";
16
16
  export * from "./draggable-dashboard";
17
17
  export * from "./dashboard-widget-card";
18
18
  export * from "./multilanguage-selector";
19
+ // ai-agent-panel is now part of the ai-agent plugin
20
+ // export * from "./core/plugins/ai-agent";
@@ -3,7 +3,9 @@ import { NotificationOptions, NotificationPosition } from "../../types";
3
3
  import { NotificationContainer } from "../../components";
4
4
  import { useInstance } from "../useInstance";
5
5
  import * as _ from "lodash-es";
6
- import { generateId } from "../../../../../core/utilities";
6
+ import { generateId, createLogger } from "../../../../../core/utilities";
7
+
8
+ const logger = createLogger("notification-container");
7
9
 
8
10
  export interface PendingNotification {
9
11
  notificationId: string | number;
@@ -304,7 +306,7 @@ export function useContainer(): IUseContainer {
304
306
 
305
307
  // If we found the notification and container to update
306
308
  if (notification && containerToUpdate) {
307
- console.log(`Updating notification ${option.notificationId} in position ${containerPosition}`);
309
+ logger.debug(`Updating notification ${option.notificationId} in position ${containerPosition}`);
308
310
 
309
311
  // If the position changes, move the notification between containers
310
312
  if (option.position && option.position !== containerPosition) {
@@ -325,16 +327,16 @@ export function useContainer(): IUseContainer {
325
327
  // Check if a container already exists for the target position
326
328
  if (!getInstanceByPosition(targetPosition)) {
327
329
  // If no container exists for the target position, create a new one
328
- console.log(`Creating new container for position ${targetPosition}`);
330
+ logger.debug(`Creating new container for position ${targetPosition}`);
329
331
  appendInstance(updatedNotification);
330
332
  } else {
331
333
  // If a container exists for the target position, add the updated notification
332
- console.log(`Adding to existing container for position ${targetPosition}`);
334
+ logger.debug(`Adding to existing container for position ${targetPosition}`);
333
335
  notificationContainers[targetPosition].value.push(updatedNotification);
334
336
  }
335
337
  } else {
336
338
  // Standard update of notification properties
337
- console.log(`Standard update of notification properties`);
339
+ logger.debug(`Standard update of notification properties`);
338
340
 
339
341
  // Apply all new properties from option to notification
340
342
  Object.keys(option).forEach((key) => {
@@ -353,7 +355,7 @@ export function useContainer(): IUseContainer {
353
355
 
354
356
  return true;
355
357
  } else {
356
- console.warn(`Notification with ID ${option.notificationId} not found for update`);
358
+ logger.warn(`Notification with ID ${option.notificationId} not found for update`);
357
359
  return false;
358
360
  }
359
361
  }
@@ -1,5 +1,8 @@
1
1
  import { App, Ref, ref } from "vue";
2
2
  import { NotificationPosition } from "../../types";
3
+ import { createLogger } from "../../../../../core/utilities";
4
+
5
+ const logger = createLogger("notification-instance");
3
6
 
4
7
  interface IUseInstance {
5
8
  saveInstance(app: App<Element>, id: string, position: NotificationPosition): void;
@@ -51,7 +54,7 @@ export function useInstance(): IUseInstance {
51
54
  }
52
55
  }
53
56
  } catch (error) {
54
- console.error(error);
57
+ logger.error("Failed to unmount notification component:", error);
55
58
  }
56
59
  }
57
60
 
@@ -1,6 +1,9 @@
1
1
  import { mergeProps } from "vue";
2
2
  import { Content, InternalNotificationOptions, NotificationOptions, NotificationPosition } from "../types";
3
3
  import { useContainer } from "../composables/useContainer";
4
+ import { createLogger } from "../../../../core/utilities";
5
+
6
+ const logger = createLogger("notification");
4
7
 
5
8
  const {
6
9
  defaultOptions,
@@ -103,7 +106,7 @@ notification.remove = (notificationId?: number | string) => {
103
106
  notification.update = (notificationId: string | number, options: NotificationOptions) => {
104
107
  // Check if notification exists
105
108
  if (!hasNotification(notificationId)) {
106
- console.warn(`Cannot update: notification with ID ${notificationId} not found`);
109
+ logger.warn(`Cannot update: notification with ID ${notificationId} not found`);
107
110
  return notificationId;
108
111
  }
109
112
 
@@ -195,12 +198,12 @@ notification.clearPosition = (position: NotificationPosition) => {
195
198
  notification.debug = () => {
196
199
  // Default settings information
197
200
  Object.entries(defaultOptions).forEach(([key, value]) => {
198
- console.log(`Default option ${key}: `, value);
201
+ logger.debug(`Default option ${key}: `, value);
199
202
  });
200
203
 
201
204
  // Available actions
202
205
  Object.entries(actions).forEach(([key]) => {
203
- console.log(`Available action: ${key}`);
206
+ logger.debug(`Available action: ${key}`);
204
207
  });
205
208
 
206
209
  // Collect statistics on notifications in different positions
@@ -231,14 +234,14 @@ notification.debug = () => {
231
234
 
232
235
  // Display group information
233
236
  Object.entries(groupedNotifications).forEach(([position, items]) => {
234
- console.log(`Position ${position}: ${items.length} notifications`);
235
- items.forEach((item) => console.log(` - ${item.id} (${item.type}): ${item.content}`));
237
+ logger.debug(`Position ${position}: ${items.length} notifications`);
238
+ items.forEach((item) => logger.debug(` - ${item.id} (${item.type}): ${item.content}`));
236
239
  });
237
240
 
238
241
  // Collect statistics on pending notifications
239
- console.log(`Pending notifications: ${pending.items.length}`);
242
+ logger.debug(`Pending notifications: ${pending.items.length}`);
240
243
  pending.items.forEach((item) => {
241
- console.log(` - ${item.notificationId} (position: ${item.position})`);
244
+ logger.debug(` - ${item.notificationId} (position: ${item.position})`);
242
245
  });
243
246
 
244
247
  return {
@@ -14,7 +14,7 @@
14
14
  <div
15
15
  v-if="typeof slot === 'string'"
16
16
  class="tw-h-full tw-w-full"
17
- v-html="slot"
17
+ v-html="sanitizeHtml(slot)"
18
18
  ></div>
19
19
  <component
20
20
  :is="slot"
@@ -25,6 +25,25 @@
25
25
  </template>
26
26
 
27
27
  <script setup lang="ts">
28
+ // eslint-disable-next-line import/no-named-as-default
29
+ import DOMPurify from "dompurify";
28
30
  import { getPopupPlugin } from "./../../utils";
31
+
29
32
  const popupPlugin = getPopupPlugin();
33
+
34
+ /**
35
+ * Sanitize HTML content to prevent XSS attacks
36
+ */
37
+ function sanitizeHtml(html: string): string {
38
+ return DOMPurify.sanitize(html, {
39
+ ALLOWED_TAGS: [
40
+ "p", "br", "strong", "em", "u", "s", "h1", "h2", "h3", "h4", "h5", "h6",
41
+ "ul", "ol", "li", "blockquote", "pre", "code", "a", "img", "table",
42
+ "thead", "tbody", "tr", "th", "td", "hr", "div", "span",
43
+ ],
44
+ ALLOWED_ATTR: ["href", "src", "alt", "title", "class", "id", "colspan", "rowspan", "align", "valign"],
45
+ FORBID_TAGS: ["script", "object", "embed", "form", "input"],
46
+ FORBID_ATTR: ["onerror", "onload", "onclick", "onmouseover", "onfocus", "onblur", "style"],
47
+ });
48
+ }
30
49
  </script>
@@ -1,6 +1,9 @@
1
1
  import { useLocalStorage } from "@vueuse/core";
2
2
  import type { Ref } from "vue";
3
3
  import { ExternalSignInClient, ExternalSignInProviderInfo } from "../../../core/api/platform";
4
+ import { createLogger } from "../../../core/utilities";
5
+
6
+ const logger = createLogger("use-external-provider");
4
7
 
5
8
  export interface IUseExternalProvider {
6
9
  storage: Ref<{ providerType?: string | undefined }>;
@@ -53,7 +56,7 @@ export const useExternalProvider = (): IUseExternalProvider => {
53
56
  // Redirect to the constructed URL
54
57
  window.location.assign(url);
55
58
  } catch (e) {
56
- console.error(e);
59
+ logger.error("External sign-in failed:", e);
57
60
  throw e;
58
61
  }
59
62
  }
@@ -75,7 +78,7 @@ export const useExternalProvider = (): IUseExternalProvider => {
75
78
  // Redirect to the sign-out URL
76
79
  window.location.assign(url);
77
80
  } catch (e) {
78
- console.error(e);
81
+ logger.error("External sign-out failed:", e);
79
82
  throw e;
80
83
  }
81
84
  }
@@ -85,8 +88,7 @@ export const useExternalProvider = (): IUseExternalProvider => {
85
88
  try {
86
89
  result = await externalSecurityClient.getExternalLoginProviders();
87
90
  } catch (e) {
88
- console.error(e);
89
- // TODO check error in app!!!
91
+ logger.error("Failed to get external login providers:", e);
90
92
  }
91
93
 
92
94
  return result;
@@ -1,4 +1,5 @@
1
1
  export * from "./useMenuExpanded";
2
2
  export * from "./useModificationTracker";
3
3
  export * from "./useTableSort";
4
+ export * from "./useTableSelection";
4
5
  export * from "./useExternalWidgets";
@@ -2,6 +2,9 @@ import { computed, inject, onMounted, onUnmounted, watch, toValue, Ref, Computed
2
2
  import { WidgetServiceKey, BladeInstance } from "../../injection-keys";
3
3
  import { IWidget } from "../../core/services";
4
4
  import { useBlade } from "../../core/composables";
5
+ import { createLogger } from "../../core/utilities";
6
+
7
+ const logger = createLogger("use-external-widgets");
5
8
 
6
9
  export interface UseExternalWidgetsOptions {
7
10
  bladeId: Ref<string> | string;
@@ -21,7 +24,7 @@ export function useExternalWidgets(options: UseExternalWidgetsOptions) {
21
24
 
22
25
  const registerExternalWidgets = () => {
23
26
  if (!widgetService || !blade?.value.id) {
24
- console.warn("useExternalWidgets: Widget service or blade instance not available");
27
+ logger.warn("Widget service or blade instance not available");
25
28
  return;
26
29
  }
27
30
 
@@ -48,7 +51,7 @@ export function useExternalWidgets(options: UseExternalWidgetsOptions) {
48
51
  widgetService.registerWidget(newWidget, normalizedBladeId.value);
49
52
  registeredExternalWidgetIds.add(externalWidget.id);
50
53
  } catch (error) {
51
- console.error(`Failed to register external widget '${externalWidget.id}':`, error);
54
+ logger.error(`Failed to register external widget '${externalWidget.id}':`, error);
52
55
  }
53
56
  });
54
57
  };
@@ -68,7 +71,7 @@ export function useExternalWidgets(options: UseExternalWidgetsOptions) {
68
71
  widget: { props: resolvedProps },
69
72
  });
70
73
  } catch (error) {
71
- console.error(`Failed to update props for widget '${widget.id}':`, error);
74
+ logger.error(`Failed to update props for widget '${widget.id}':`, error);
72
75
  }
73
76
  }
74
77
  });
@@ -81,7 +84,7 @@ export function useExternalWidgets(options: UseExternalWidgetsOptions) {
81
84
  try {
82
85
  widgetService.unregisterWidget(widgetId, normalizedBladeId.value);
83
86
  } catch (error) {
84
- console.error(`Failed to unregister external widget '${widgetId}':`, error);
87
+ logger.error(`Failed to unregister external widget '${widgetId}':`, error);
85
88
  }
86
89
  });
87
90
  registeredExternalWidgetIds.clear();
@@ -1,5 +1,5 @@
1
1
  import { useLocalStorage } from "@vueuse/core";
2
- import { ref } from "vue";
2
+ import { ref, onScopeDispose } from "vue";
3
3
 
4
4
  const STORAGE_KEY_PREFIX = "VC_APP_MENU_EXPANDED";
5
5
  const HOVER_DELAY = 200;
@@ -42,6 +42,20 @@ export const useMenuExpanded = () => {
42
42
  }
43
43
  };
44
44
 
45
+ /**
46
+ * Cleanup timeout when the effect scope is disposed
47
+ * This prevents memory leaks when component using this composable is unmounted
48
+ */
49
+ const cleanup = () => {
50
+ if (expandTimeout) {
51
+ clearTimeout(expandTimeout);
52
+ expandTimeout = null;
53
+ }
54
+ };
55
+
56
+ // Register cleanup function to be called when effect scope is disposed
57
+ onScopeDispose(cleanup);
58
+
45
59
  return {
46
60
  isExpanded,
47
61
  toggleExpanded,
@@ -0,0 +1,151 @@
1
+ import { ref, computed, Ref, ComputedRef } from "vue";
2
+ import { createLogger } from "../../core/utilities";
3
+
4
+ const logger = createLogger("use-table-selection");
5
+
6
+ export interface UseTableSelectionOptions<T> {
7
+ /**
8
+ * The field to use for extracting IDs from items.
9
+ * Can be a key of T or a function that extracts the ID.
10
+ * @default 'id'
11
+ */
12
+ idField?: keyof T | ((item: T) => string | undefined);
13
+ }
14
+
15
+ export interface UseTableSelectionReturn<T> {
16
+ /**
17
+ * Array of currently selected item objects.
18
+ */
19
+ selectedItems: Ref<T[]>;
20
+
21
+ /**
22
+ * Computed array of IDs extracted from selected items.
23
+ */
24
+ selectedIds: ComputedRef<string[]>;
25
+
26
+ /**
27
+ * Whether "select all" across pagination is active.
28
+ */
29
+ allSelected: Ref<boolean>;
30
+
31
+ /**
32
+ * Number of currently selected items.
33
+ */
34
+ selectionCount: ComputedRef<number>;
35
+
36
+ /**
37
+ * Whether any items are currently selected.
38
+ */
39
+ hasSelection: ComputedRef<boolean>;
40
+
41
+ /**
42
+ * Handler for VcTable's @selection-changed event.
43
+ */
44
+ handleSelectionChange: (items: T[]) => void;
45
+
46
+ /**
47
+ * Handler for VcTable's @select:all event.
48
+ */
49
+ handleSelectAll: (selected: boolean) => void;
50
+
51
+ /**
52
+ * Clears all selection state.
53
+ */
54
+ resetSelection: () => void;
55
+
56
+ /**
57
+ * Checks if a specific item is selected.
58
+ */
59
+ isSelected: (item: T) => boolean;
60
+
61
+ /**
62
+ * Programmatically select items.
63
+ */
64
+ selectItems: (items: T[]) => void;
65
+
66
+ /**
67
+ * Deselect items by their IDs.
68
+ */
69
+ deselectByIds: (ids: string[]) => void;
70
+ }
71
+
72
+ export function useTableSelection<T extends object>(
73
+ options?: UseTableSelectionOptions<T>
74
+ ): UseTableSelectionReturn<T> {
75
+ const idField = options?.idField ?? ("id" as keyof T);
76
+
77
+ const getItemId = (item: T): string | undefined => {
78
+ if (typeof idField === "function") {
79
+ return idField(item);
80
+ }
81
+ const value = item[idField as keyof T];
82
+ return typeof value === "string" ? value : undefined;
83
+ };
84
+
85
+ const selectedItems = ref<T[]>([]) as Ref<T[]>;
86
+ const allSelected = ref(false);
87
+
88
+ const selectedIds = computed<string[]>(() => {
89
+ return selectedItems.value.flatMap((item) => {
90
+ const id = getItemId(item);
91
+ return id ? [id] : [];
92
+ });
93
+ });
94
+
95
+ const selectionCount = computed(() => selectedItems.value.length);
96
+ const hasSelection = computed(() => selectedItems.value.length > 0);
97
+ const selectedIdSet = computed(() => new Set(selectedIds.value));
98
+
99
+ const handleSelectionChange = (items: T[]): void => {
100
+ selectedItems.value = items;
101
+ if (items.length === 0) {
102
+ allSelected.value = false;
103
+ }
104
+ logger.debug("Selection changed", { count: items.length });
105
+ };
106
+
107
+ const handleSelectAll = (selected: boolean): void => {
108
+ allSelected.value = selected;
109
+ if (!selected) {
110
+ selectedItems.value = [];
111
+ }
112
+ logger.debug("Select all changed", { selected });
113
+ };
114
+
115
+ const resetSelection = (): void => {
116
+ selectedItems.value = [];
117
+ allSelected.value = false;
118
+ logger.debug("Selection reset");
119
+ };
120
+
121
+ const isSelected = (item: T): boolean => {
122
+ const id = getItemId(item);
123
+ return id ? selectedIdSet.value.has(id) : false;
124
+ };
125
+
126
+ const selectItems = (items: T[]): void => {
127
+ selectedItems.value = items;
128
+ };
129
+
130
+ const deselectByIds = (ids: string[]): void => {
131
+ const idsToRemove = new Set(ids);
132
+ selectedItems.value = selectedItems.value.filter((item) => {
133
+ const id = getItemId(item);
134
+ return id ? !idsToRemove.has(id) : true;
135
+ });
136
+ };
137
+
138
+ return {
139
+ selectedItems,
140
+ selectedIds,
141
+ allSelected,
142
+ selectionCount,
143
+ hasSelection,
144
+ handleSelectionChange,
145
+ handleSelectAll,
146
+ resetSelection,
147
+ isSelected,
148
+ selectItems,
149
+ deselectByIds,
150
+ };
151
+ }
@@ -1,6 +1,9 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  // framework/composables/useTableSort.ts
3
3
  import { ref, computed, Ref, WritableComputedRef } from "vue";
4
+ import { createLogger } from "../../core/utilities";
5
+
6
+ const logger = createLogger("use-table-sort");
4
7
 
5
8
  export type ITableSortDirection = "ASC" | "DESC";
6
9
 
@@ -48,8 +51,6 @@ export function useTableSort(options?: UseTableSortOptions): UseTableSortReturn
48
51
  });
49
52
 
50
53
  const handleSortChange = (sortParam: SortParam) => {
51
- console.log("[useTableSort] handleSortChange triggered. Received sortParam:", JSON.stringify(sortParam));
52
-
53
54
  let newSortProperty: string | undefined = undefined;
54
55
  let newSortDirection: ITableSortDirection | undefined = undefined;
55
56
 
@@ -79,9 +80,8 @@ export function useTableSort(options?: UseTableSortOptions): UseTableSortReturn
79
80
  currentSortProperty.value = newSortProperty;
80
81
  currentSortDirection.value = newSortDirection || "ASC"; // Default to ASC if no direction provided
81
82
  }
82
- console.log(`[useTableSort] New sort state: ${currentSortProperty.value}:${currentSortDirection.value}`);
83
83
  } else {
84
- console.warn("[useTableSort] Could not determine valid sort property from sortParam:", sortParam);
84
+ logger.warn("Could not determine valid sort property from sortParam:", sortParam);
85
85
  }
86
86
  };
87
87
 
@@ -160,6 +160,9 @@ import moment from "moment";
160
160
  import { isImage, getFileThumbnail, readableSize } from "./../../../../utilities/assets";
161
161
  import * as _ from "lodash-es";
162
162
  import { IParentCallArgs, useBladeNavigation } from "../../../../components";
163
+ import { createLogger } from "../../../../../core/utilities";
164
+
165
+ const logger = createLogger("assets-manager");
163
166
 
164
167
  export interface Props {
165
168
  expanded?: boolean;
@@ -317,7 +320,7 @@ async function onDrop(event: DragEvent) {
317
320
  try {
318
321
  await upload(fileList);
319
322
  } catch (error) {
320
- console.error(error);
323
+ logger.error("Failed to upload dropped files:", error);
321
324
  throw error;
322
325
  }
323
326
 
@@ -362,7 +365,7 @@ async function upload(files: FileList) {
362
365
  try {
363
366
  defaultAssets.value = await props.options.assetsUploadHandler(modifiedFileList.files);
364
367
  } catch (error) {
365
- console.error(error);
368
+ logger.error("Failed to upload assets:", error);
366
369
  throw error;
367
370
  }
368
371
  }
@@ -376,7 +379,7 @@ async function inputUpload(event: Event) {
376
379
  try {
377
380
  await upload(fileList);
378
381
  } catch (error) {
379
- console.error(error);
382
+ logger.error("Failed to upload files:", error);
380
383
  throw error;
381
384
  }
382
385
 
@@ -183,6 +183,9 @@ import { useI18n } from "vue-i18n";
183
183
  import { default as ExternalProviders } from "./../../../../../shared/components/sign-in/external-providers.vue";
184
184
  import { useExternalProvider } from "./../../../../../shared/components/sign-in/useExternalProvider";
185
185
  import { ExtensionSlot, useExtensionSlot } from '../../../../../core/plugins/extension-points';
186
+ import { createLogger } from "../../../../../core/utilities";
187
+
188
+ const logger = createLogger("login-page");
186
189
 
187
190
  type ForgotPasswordFunc = (args: { loginOrEmail: string }) => Promise<void>;
188
191
 
@@ -305,7 +308,7 @@ const togglePassRequest = () => {
305
308
  }
306
309
  };
307
310
 
308
- console.debug("Init login-page");
311
+ logger.debug("Init login-page");
309
312
  </script>
310
313
 
311
314
  <style lang="scss">
@@ -1,3 +1,7 @@
1
+ import { createLogger } from "../../core/utilities";
2
+
3
+ const logger = createLogger("color-utils");
4
+
1
5
  /**
2
6
  * Convert CSS color name to hex code using Canvas API
3
7
  * @param colorName - CSS color name (e.g., "red", "blue", "lime")
@@ -27,22 +31,11 @@ export function convertColorNameToHex(colorName: string): string | null {
27
31
 
28
32
  return ctx.fillStyle; // Returns hex format
29
33
  } catch (error) {
30
- console.warn("Error converting color name to hex:", error);
34
+ logger.warn("Error converting color name to hex:", error);
31
35
  return null;
32
36
  }
33
37
  }
34
38
 
35
- /**
36
- * Convert hex code to human-readable color name
37
- * @param hex - Hex color code (e.g., "#ff0000", "ff0000")
38
- * @returns Always returns null - don't auto-fill color names
39
- */
40
- export function convertHexToColorName(hex: string): string | null {
41
- // Always return null - don't auto-fill
42
- // User should enter custom color name manually
43
- return null;
44
- }
45
-
46
39
  /**
47
40
  * Check if a string is a valid hex color
48
41
  * @param value - String to check
@@ -73,6 +73,9 @@ const props = withDefaults(defineProps<Props>(), {
73
73
  iconSize: "xxl",
74
74
  });
75
75
 
76
+ /** Maximum height in pixels before content is collapsed */
77
+ const COLLAPSED_MAX_HEIGHT = 100;
78
+
76
79
  const contentRef = ref<HTMLDivElement>();
77
80
  const isExpanded = ref(false);
78
81
  const hasOverflow = ref(false);
@@ -83,7 +86,7 @@ const toggle = () => {
83
86
 
84
87
  const checkOverflow = () => {
85
88
  if (contentRef.value) {
86
- hasOverflow.value = contentRef.value.scrollHeight > 100;
89
+ hasOverflow.value = contentRef.value.scrollHeight > COLLAPSED_MAX_HEIGHT;
87
90
  }
88
91
  };
89
92