@vc-shell/framework 1.2.2 → 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
@@ -21,3 +21,5 @@ export * from "./useSettingsMenu";
21
21
  export * from "./useToolbar";
22
22
  export * from "./useDynamicProperties";
23
23
  export * from "./useBlade";
24
+ // useBladeSelection and useAiAgent are now part of the ai-agent plugin
25
+ // export * from "./core/plugins/ai-agent";
@@ -1,6 +1,12 @@
1
- import { Ref, computed, ref, ComputedRef } from "vue";
1
+ import { computed, ref, ComputedRef } from "vue";
2
2
  import * as _ from "lodash-es";
3
3
  import { ICommonAsset } from "../../types";
4
+ import { createLogger } from "../../utilities";
5
+
6
+ const logger = createLogger("use-assets");
7
+
8
+ /** Maximum number of concurrent uploads */
9
+ const MAX_CONCURRENT_UPLOADS = 4;
4
10
 
5
11
  export interface IUseAssets {
6
12
  upload: (files: FileList, uploadPath: string, startingSortOrder?: number) => Promise<ICommonAsset[]>;
@@ -9,6 +15,60 @@ export interface IUseAssets {
9
15
  loading: ComputedRef<boolean>;
10
16
  }
11
17
 
18
+ /**
19
+ * Uploads a single file and returns the asset
20
+ */
21
+ async function uploadSingleFile(
22
+ file: File,
23
+ uploadPath: string,
24
+ index: number,
25
+ startingSortOrder?: number,
26
+ ): Promise<ICommonAsset | null> {
27
+ const formData = new FormData();
28
+ formData.append("file", file);
29
+
30
+ const result = await fetch(`/api/assets?folderUrl=/${uploadPath}`, {
31
+ method: "POST",
32
+ body: formData,
33
+ });
34
+
35
+ const response = await result.json();
36
+
37
+ if (response?.length) {
38
+ const uploadedFile = response[0];
39
+ uploadedFile.createdDate = new Date();
40
+ uploadedFile.sortOrder = startingSortOrder !== undefined && startingSortOrder >= 0 ? startingSortOrder + index + 1 : 0;
41
+ uploadedFile.url = uploadedFile.url ? decodeURI(uploadedFile.url) : "";
42
+
43
+ if ("size" in uploadedFile) {
44
+ uploadedFile.size = file.size;
45
+ }
46
+
47
+ return uploadedFile;
48
+ }
49
+
50
+ return null;
51
+ }
52
+
53
+ /**
54
+ * Processes items in batches with concurrency limit
55
+ */
56
+ async function processBatched<T, R>(
57
+ items: T[],
58
+ processor: (item: T, index: number) => Promise<R>,
59
+ concurrency: number,
60
+ ): Promise<R[]> {
61
+ const results: R[] = [];
62
+
63
+ for (let i = 0; i < items.length; i += concurrency) {
64
+ const batch = items.slice(i, i + concurrency);
65
+ const batchResults = await Promise.all(batch.map((item, batchIndex) => processor(item, i + batchIndex)));
66
+ results.push(...batchResults);
67
+ }
68
+
69
+ return results;
70
+ }
71
+
12
72
  export function useAssets(): IUseAssets {
13
73
  const loading = ref(false);
14
74
 
@@ -16,35 +76,19 @@ export function useAssets(): IUseAssets {
16
76
  try {
17
77
  loading.value = true;
18
78
 
19
- const uploadedAssets: Ref<ICommonAsset[]> = ref([]);
20
- for (let i = 0; i < files.length; i++) {
21
- const formData = new FormData();
22
- formData.append("file", files[i]);
23
-
24
- const result = await fetch(`/api/assets?folderUrl=/${uploadPath}`, {
25
- method: "POST",
26
- body: formData,
27
- });
28
-
29
- const response = await result.json();
79
+ const fileArray = Array.from(files);
30
80
 
31
- if (response?.length) {
32
- const file = response[0];
33
- file.createdDate = new Date();
34
- file.sortOrder = startingSortOrder !== undefined && startingSortOrder >= 0 ? startingSortOrder + i + 1 : 0;
35
- file.url = file.url ? decodeURI(file.url) : "";
36
-
37
- if ("size" in file) {
38
- file.size = files[i].size;
39
- }
40
-
41
- uploadedAssets.value.push(file);
42
- }
43
- }
81
+ // Upload files in parallel batches
82
+ const uploadResults = await processBatched(
83
+ fileArray,
84
+ (file, index) => uploadSingleFile(file, uploadPath, index, startingSortOrder),
85
+ MAX_CONCURRENT_UPLOADS,
86
+ );
44
87
 
45
- return uploadedAssets.value;
88
+ // Filter out null results and return successful uploads
89
+ return uploadResults.filter((asset): asset is ICommonAsset => asset !== null);
46
90
  } catch (error) {
47
- console.error(error);
91
+ logger.error("Upload failed:", error);
48
92
  throw error;
49
93
  } finally {
50
94
  loading.value = false;
@@ -63,7 +107,7 @@ export function useAssets(): IUseAssets {
63
107
 
64
108
  return updatedAssetArr;
65
109
  } catch (error) {
66
- console.error(error);
110
+ logger.error("Remove failed:", error);
67
111
  throw error;
68
112
  } finally {
69
113
  loading.value = false;
@@ -1,6 +1,9 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { readonly, ref } from "vue";
3
3
  import { HasLoading } from "../useLoading";
4
+ import { createLogger } from "../../utilities";
5
+
6
+ const logger = createLogger("use-async");
4
7
 
5
8
  export type AsyncAction<Payload = void, Result = void> = (payload?: Payload, ...rest: any[]) => Promise<Result>;
6
9
 
@@ -18,7 +21,7 @@ export function useAsync<Payload = void, Result = void>(
18
21
  try {
19
22
  return await innerAction(payload, ...rest);
20
23
  } catch (e) {
21
- console.error(e);
24
+ logger.error("Async action failed:", e);
22
25
  throw e;
23
26
  } finally {
24
27
  loading.value = false;
@@ -1,5 +1,8 @@
1
1
  import { App, inject, shallowRef, computed, ComputedRef, readonly as vueReadonly } from "vue";
2
2
  import { BladeInstanceConstructor } from "../../../shared/components/blade-navigation/types";
3
+ import { createLogger } from "../../utilities";
4
+
5
+ const logger = createLogger("blade-registry");
3
6
 
4
7
  /**
5
8
  * Interface for blade registration data
@@ -81,16 +84,14 @@ export function createBladeRegistry(app: App): IBladeRegistryInstance {
81
84
  const newMap = new Map(registeredBladesInternal.value);
82
85
 
83
86
  if (newMap.has(name)) {
84
- console.warn(`BladeRegistry: Blade '${name}' is already registered. It will be overwritten.`);
87
+ logger.warn(`Blade '${name}' is already registered. It will be overwritten.`);
85
88
  }
86
89
 
87
90
  // Register component globally if not already registered or different
88
91
  const existingGlobalComponent = app.component(name);
89
92
  if (!existingGlobalComponent || existingGlobalComponent !== registrationData.component) {
90
93
  if (existingGlobalComponent && existingGlobalComponent !== registrationData.component) {
91
- console.warn(
92
- `BladeRegistry: Global component '${name}' already exists and is different. Overwriting with new blade component.`,
93
- );
94
+ logger.warn(`Global component '${name}' already exists and is different. Overwriting with new blade component.`);
94
95
  }
95
96
  app.component(name, registrationData.component);
96
97
  }
@@ -137,7 +138,7 @@ export function createBladeRegistry(app: App): IBladeRegistryInstance {
137
138
  return globalComponent;
138
139
  }
139
140
  } catch (error) {
140
- console.warn(`BladeRegistry: Error accessing global component '${name}':`, error);
141
+ logger.warn(`Error accessing global component '${name}':`, error);
141
142
  }
142
143
 
143
144
  return undefined;
@@ -1,6 +1,9 @@
1
1
  import { Breadcrumbs } from "./../../../ui/types/index";
2
2
  import { ComputedRef, computed, reactive, toValue } from "vue";
3
3
  import * as _ from "lodash-es";
4
+ import { createLogger } from "../../utilities";
5
+
6
+ const logger = createLogger("use-breadcrumbs");
4
7
 
5
8
  interface HistoryRecord {
6
9
  records: Breadcrumbs[];
@@ -45,7 +48,7 @@ export function useBreadcrumbs() {
45
48
  removeNext(id);
46
49
  }
47
50
  } catch (e) {
48
- console.error(e);
51
+ logger.error("Breadcrumb click handler failed:", e);
49
52
  }
50
53
  }
51
54
  },
@@ -2,6 +2,9 @@ import { onErrorCaptured, getCurrentInstance, ref, Ref, nextTick } from "vue";
2
2
  import { useAppInsights } from "..";
3
3
  import { useUserManagement } from "../useUserManagement";
4
4
  import { DisplayableError, parseError } from "../../utilities/error";
5
+ import { createLogger } from "../../utilities";
6
+
7
+ const logger = createLogger("use-error-handler");
5
8
 
6
9
  interface IUseErrorHandler {
7
10
  error: Ref<DisplayableError | null>;
@@ -51,7 +54,7 @@ export function useErrorHandler(capture?: boolean): IUseErrorHandler {
51
54
  });
52
55
  }
53
56
 
54
- console.error("Captured Error:", capturedError.originalError);
57
+ logger.error("Captured Error:", capturedError.originalError);
55
58
 
56
59
  if (instance) {
57
60
  instance.emit("error", capturedError);
@@ -1,6 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  export default function debounce(func: (...args: any[]) => void, delay: number): (...args: unknown[]) => void {
3
- console.debug(`[@vc-shell/framework#useFunctions:debounce] - Entry point`);
4
3
  let timer: number | null = null;
5
4
 
6
5
  return function (...args: unknown[]): void {
@@ -1,4 +1,3 @@
1
1
  export default function delay(func: (...args: unknown[]) => void, delay = 0): void {
2
- console.debug(`[@vc-shell/framework#useFunctions:delay] - Entry point`);
3
2
  setTimeout(func, delay);
4
3
  }
@@ -11,7 +11,6 @@ interface IUseFunctions {
11
11
  }
12
12
 
13
13
  export function useFunctions(): IUseFunctions {
14
- console.debug("useFunctions entry point");
15
14
  return {
16
15
  debounce,
17
16
  delay,
@@ -1,7 +1,6 @@
1
1
  const resultsMap = new WeakMap();
2
2
 
3
3
  export default function once(func: (...args: unknown[]) => void): (...args: unknown[]) => unknown {
4
- console.debug(`[@vc-shell/framework#useFunctions:once] - Entry point`);
5
4
  return function (...args: unknown[]): unknown {
6
5
  if (!resultsMap.has(func)) {
7
6
  const result = func(...args);
@@ -1,4 +1,3 @@
1
1
  export default function sleep(ms: number): Promise<void> {
2
- console.debug(`[@vc-shell/framework#useFunctions:sleep] - Entry point`);
3
2
  return new Promise((resolve) => setTimeout(resolve, ms));
4
3
  }
@@ -1,5 +1,4 @@
1
1
  export default function throttle(func: (...args: unknown[]) => void, delay: number): (...args: unknown[]) => void {
2
- console.debug(`[@vc-shell/framework#useFunctions:throttle] - Entry point`);
3
2
  let wasThrottled = false;
4
3
 
5
4
  return function (...args: unknown[]): void {
@@ -2,17 +2,17 @@ import { inject, provide } from "vue";
2
2
  import { GlobalSearchKey } from "../../../injection-keys";
3
3
  import { GlobalSearchState, createGlobalSearchService } from "../../services/global-search-service";
4
4
 
5
- export function provideGlobalSearch() {
5
+ export function provideGlobalSearch(): GlobalSearchState {
6
6
  const state = createGlobalSearchService();
7
7
  provide(GlobalSearchKey, state);
8
8
  return state;
9
9
  }
10
10
 
11
- export function useGlobalSearch() {
11
+ export function useGlobalSearch(): GlobalSearchState {
12
12
  const state = inject<GlobalSearchState>(GlobalSearchKey);
13
13
 
14
14
  if (!state) {
15
- throw new Error("useGlobalSearch must be used within a component that has called createGlobalSearch");
15
+ throw new Error("useGlobalSearch must be used within a component that has called provideGlobalSearch");
16
16
  }
17
17
 
18
18
  return state;
@@ -1,6 +1,9 @@
1
1
  import { provide, inject, getCurrentInstance } from "vue";
2
2
  import { MenuService, createMenuService, addMenuItem } from "../../services/menu-service";
3
3
  import { MenuServiceKey } from "../../../injection-keys";
4
+ import { createLogger, InjectionError } from "../../utilities";
5
+
6
+ const logger = createLogger("use-menu-service");
4
7
 
5
8
  export function provideMenuService(): MenuService {
6
9
  const service = createMenuService();
@@ -11,8 +14,8 @@ export function provideMenuService(): MenuService {
11
14
  export function useMenuService(): MenuService {
12
15
  const service = inject(MenuServiceKey);
13
16
  if (!service) {
14
- console.error("Menu service not found in current context. Injection chain:", getCurrentInstance());
15
- throw new Error("MenuService not provided");
17
+ logger.error("Menu service not found in current context. Injection chain:", getCurrentInstance());
18
+ throw new InjectionError("MenuService");
16
19
  }
17
20
  return service;
18
21
  }
@@ -1,6 +1,9 @@
1
1
  import { PushNotification, PushNotificationClient } from "./../../api/platform";
2
2
  import { computed, ComputedRef, ref, onUnmounted } from "vue";
3
3
  import * as _ from "lodash-es";
4
+ import { createLogger } from "../../utilities";
5
+
6
+ const logger = createLogger("use-notifications");
4
7
 
5
8
  const notificationsClient = new PushNotificationClient();
6
9
 
@@ -64,7 +67,7 @@ export function useNotifications(notifyType?: string | string[]): INotifications
64
67
  notifications.value = <PushNotification[]>JSON.parse(response).notifyEvents ?? [];
65
68
  });
66
69
  } catch (e) {
67
- console.error(e);
70
+ logger.error("loadFromHistory failed:", e);
68
71
  throw e;
69
72
  }
70
73
  }
@@ -121,7 +124,7 @@ export function useNotifications(notifyType?: string | string[]): INotifications
121
124
  });
122
125
  await notificationsClient.markAllAsRead();
123
126
  } catch (e) {
124
- console.error(e);
127
+ logger.error("markAllAsRead failed:", e);
125
128
  throw e;
126
129
  }
127
130
  }
@@ -3,6 +3,9 @@ import { computed, watchEffect, ref, type Ref } from "vue";
3
3
  import { useI18n } from "vue-i18n";
4
4
  import * as _ from "lodash-es";
5
5
  import { i18n } from "../../plugins/i18n";
6
+ import { createLogger } from "../../utilities";
7
+
8
+ const logger = createLogger("use-theme");
6
9
 
7
10
  export interface ThemeDefinition {
8
11
  key: string;
@@ -82,7 +85,7 @@ export const useTheme = (): IUseTheme => {
82
85
  if (themeKeys.value.includes(themeKey)) {
83
86
  state.value = themeKey;
84
87
  } else {
85
- console.warn(`[useTheme] Attempted to set an unregistered theme: ${themeKey}`);
88
+ logger.warn(`Attempted to set an unregistered theme: ${themeKey}`);
86
89
  }
87
90
  }
88
91
 
@@ -15,6 +15,18 @@ import {
15
15
  import { RequestPasswordResult } from "./../../types";
16
16
  import { createSharedComposable } from "@vueuse/core";
17
17
  import { useExternalProvider } from "../../../shared/components/sign-in/useExternalProvider";
18
+ import { createLogger } from "../../utilities";
19
+
20
+ export interface TokenData {
21
+ token: string | null;
22
+ access_token: string | null;
23
+ refresh_token: string | null;
24
+ expires_at: number | null;
25
+ }
26
+
27
+ const AUTH_STORAGE_KEY = "vc_auth_data";
28
+
29
+ const logger = createLogger("use-user");
18
30
 
19
31
  // Interface for the full internal API provided by _createInternalUserLogic
20
32
  export interface IUserInternalAPI {
@@ -30,6 +42,7 @@ export interface IUserInternalAPI {
30
42
  requestPasswordReset: (loginOrEmail: string) => Promise<RequestPasswordResult>;
31
43
  changeUserPassword: (oldPassword: string, newPassword: string) => Promise<SecurityResult | undefined>;
32
44
  getLoginType: () => Promise<LoginType[]>;
45
+ getAccessToken: () => Promise<string | null>;
33
46
  isAuthenticated: ComputedRef<boolean>;
34
47
  }
35
48
 
@@ -40,12 +53,89 @@ export interface IAppUserAPI {
40
53
  isAdministrator: ComputedRef<boolean | undefined>;
41
54
  loadUser: () => Promise<UserDetail>;
42
55
  signOut: () => Promise<void>;
56
+ getAccessToken: () => Promise<string | null>;
43
57
  }
44
58
 
45
59
  const user: Ref<UserDetail | undefined> = ref();
46
60
 
61
+ function readAuthData(): TokenData | null {
62
+ try {
63
+ const stored = localStorage.getItem(AUTH_STORAGE_KEY);
64
+ if (stored) {
65
+ return JSON.parse(stored) as TokenData;
66
+ }
67
+ } catch (e) {
68
+ logger.error("Failed to read auth data from storage:", e);
69
+ }
70
+ return null;
71
+ }
72
+
73
+ function storeAuthData(data: TokenData): void {
74
+ try {
75
+ logger.debug("storeAuthData - Saving:", data);
76
+ localStorage.setItem(AUTH_STORAGE_KEY, JSON.stringify(data));
77
+ logger.debug("storeAuthData - Saved successfully");
78
+ } catch (e) {
79
+ logger.error("Failed to store auth data:", e);
80
+ }
81
+ }
82
+
83
+ function clearAuthData(): void {
84
+ try {
85
+ localStorage.removeItem(AUTH_STORAGE_KEY);
86
+ } catch (e) {
87
+ logger.error("Failed to clear auth data:", e);
88
+ }
89
+ }
90
+
91
+ // Direct fetch to /connect/token endpoint
92
+ async function fetchToken(params: Record<string, string>): Promise<TokenData | null> {
93
+ try {
94
+ const body = new URLSearchParams(params).toString();
95
+
96
+ const response = await fetch("/connect/token", {
97
+ method: "POST",
98
+ headers: {
99
+ "Content-Type": "application/x-www-form-urlencoded",
100
+ },
101
+ body,
102
+ });
103
+
104
+ if (!response.ok) {
105
+ const errorBody = await response.text();
106
+ logger.error("fetchToken - HTTP error:", response.status, response.statusText, errorBody);
107
+ return null;
108
+ }
109
+
110
+ const data = await response.json();
111
+ logger.debug("fetchToken - Response:", data);
112
+
113
+ const accessToken = data.access_token;
114
+ const refreshToken = data.refresh_token;
115
+ const expiresIn = data.expires_in;
116
+
117
+ if (data.error || !accessToken) {
118
+ logger.error("fetchToken - Error:", data.error_description || data.error || "No access token");
119
+ return null;
120
+ }
121
+
122
+ const expiresAt = expiresIn ? Date.now() + expiresIn * 1000 : null;
123
+
124
+ return {
125
+ token: accessToken,
126
+ access_token: accessToken,
127
+ refresh_token: refreshToken ?? null,
128
+ expires_at: expiresAt,
129
+ };
130
+ } catch (e) {
131
+ logger.error("fetchToken - Exception:", e);
132
+ return null;
133
+ }
134
+ }
135
+
47
136
  export function _createInternalUserLogic(): IUserInternalAPI {
48
137
  const loading: Ref<boolean> = ref(false);
138
+ const authData: Ref<TokenData | null> = ref(null);
49
139
 
50
140
  const { storage: externalSignInStorage, signOut: externalSignOut } = useExternalProvider();
51
141
 
@@ -83,25 +173,40 @@ export function _createInternalUserLogic(): IUserInternalAPI {
83
173
  username: string,
84
174
  password: string,
85
175
  ): Promise<SignInResult | { succeeded: boolean; error?: any; status?: number }> {
86
- console.debug(`[@vc-shell/framework#_createInternalUserLogic:signIn] - Entry point`);
176
+ logger.debug("signIn - Entry point");
87
177
  try {
88
178
  loading.value = true;
179
+
180
+ // First do the standard login to set cookies/session
89
181
  const result = await securityClient.login(new LoginRequest({ userName: username, password }));
90
- return await securityClient
91
- .getCurrentUser()
92
- .then((res) => {
93
- if (res) {
94
- user.value = res;
95
- return result;
96
- }
97
- throw { succeeded: false };
98
- })
99
- .catch((e) => {
100
- throw e;
101
- });
182
+ logger.debug("signIn - Cookie login completed:", result);
183
+
184
+ // Then get OAuth token for API calls (direct fetch, bypasses API client)
185
+ // Request offline_access scope to get refresh_token for automatic token renewal
186
+ logger.debug("signIn - Requesting token...");
187
+ const tokenData = await fetchToken({
188
+ grant_type: "password",
189
+ username,
190
+ password,
191
+ scope: "offline_access",
192
+ });
193
+
194
+ if (tokenData) {
195
+ authData.value = tokenData;
196
+ storeAuthData(authData.value);
197
+ logger.debug("signIn - Token saved:", authData.value);
198
+ }
199
+
200
+ // Load current user info
201
+ const userInfo = await securityClient.getCurrentUser();
202
+ if (userInfo) {
203
+ user.value = userInfo;
204
+ return result;
205
+ }
206
+
207
+ throw { succeeded: false };
102
208
  } catch (e: any) {
103
- //TODO: log error
104
- console.log(e);
209
+ logger.error("signIn failed:", e);
105
210
  return { succeeded: false, error: e.message, status: e.status };
106
211
  } finally {
107
212
  loading.value = false;
@@ -109,10 +214,14 @@ export function _createInternalUserLogic(): IUserInternalAPI {
109
214
  }
110
215
 
111
216
  async function signOut(): Promise<void> {
112
- console.debug(`[@vc-shell/framework#_createInternalUserLogic:signOut] - Entry point`);
217
+ logger.debug("signOut - Entry point");
113
218
 
114
219
  user.value = undefined;
115
220
 
221
+ // Clear stored auth data
222
+ clearAuthData();
223
+ authData.value = null;
224
+
116
225
  if (externalSignInStorage.value?.providerType) {
117
226
  await externalSignOut(externalSignInStorage.value.providerType);
118
227
  } else {
@@ -121,14 +230,15 @@ export function _createInternalUserLogic(): IUserInternalAPI {
121
230
  }
122
231
 
123
232
  async function loadUser(): Promise<UserDetail> {
124
- console.debug(`[@vc-shell/framework#_createInternalUserLogic:loadUser] - Entry point`);
233
+ logger.debug("loadUser - Entry point");
125
234
 
126
235
  try {
127
236
  loading.value = true;
128
237
  user.value = await securityClient.getCurrentUser();
129
- console.log("[_createInternalUserLogic]: an user details has been loaded", user.value);
238
+ await getAccessToken();
239
+ logger.debug("User details loaded:", user.value);
130
240
  } catch (e: any) {
131
- console.error(e);
241
+ logger.error("loadUser failed:", e);
132
242
  } finally {
133
243
  loading.value = false;
134
244
  }
@@ -174,13 +284,70 @@ export function _createInternalUserLogic(): IUserInternalAPI {
174
284
  try {
175
285
  result = await securityClient.getLoginTypes();
176
286
  } catch (e) {
177
- console.error(e);
287
+ logger.error("getLoginType failed:", e);
178
288
  throw e;
179
289
  }
180
290
 
181
291
  return result;
182
292
  }
183
293
 
294
+ // Buffer time in ms - refresh token this much before actual expiration
295
+ const TOKEN_REFRESH_BUFFER_MS = 60 * 1000; // 60 seconds
296
+
297
+ async function getAccessToken(): Promise<string | null> {
298
+ logger.debug("getAccessToken - Entry point");
299
+
300
+ // Load auth data from storage if not yet loaded
301
+ if (!authData.value) {
302
+ authData.value = readAuthData();
303
+ }
304
+
305
+ // No auth data available
306
+ if (!authData.value) {
307
+ logger.debug("getAccessToken - No auth data available");
308
+ return null;
309
+ }
310
+
311
+ // Check if token is expired or about to expire (with buffer)
312
+ const now = Date.now();
313
+ const expiresAt = authData.value.expires_at;
314
+ const shouldRefresh = expiresAt ? now >= expiresAt - TOKEN_REFRESH_BUFFER_MS : false;
315
+
316
+ logger.debug("getAccessToken - Token status:", {
317
+ hasToken: !!authData.value.access_token,
318
+ expiresAt: expiresAt ? new Date(expiresAt).toISOString() : "not set",
319
+ now: new Date(now).toISOString(),
320
+ shouldRefresh,
321
+ hasRefreshToken: !!authData.value.refresh_token,
322
+ });
323
+
324
+ if (shouldRefresh && authData.value.refresh_token) {
325
+ logger.debug("getAccessToken - Token expired or expiring soon, attempting refresh");
326
+
327
+ const tokenData = await fetchToken({
328
+ grant_type: "refresh_token",
329
+ refresh_token: authData.value.refresh_token,
330
+ scope: "offline_access",
331
+ });
332
+
333
+ if (tokenData) {
334
+ // Keep old refresh_token if new one not provided
335
+ tokenData.refresh_token = tokenData.refresh_token ?? authData.value.refresh_token;
336
+ authData.value = tokenData;
337
+ storeAuthData(authData.value);
338
+ logger.debug("getAccessToken - Token refreshed successfully, new expiry:", {
339
+ expiresAt: tokenData.expires_at ? new Date(tokenData.expires_at).toISOString() : "not set",
340
+ });
341
+ } else {
342
+ // Refresh failed - log error but return existing token (it might still work)
343
+ // Don't clear auth data - let the API call fail naturally if token is truly invalid
344
+ logger.warn("getAccessToken - Failed to refresh token, returning existing token");
345
+ }
346
+ }
347
+
348
+ return authData.value?.access_token ?? authData.value?.token ?? null;
349
+ }
350
+
184
351
  return {
185
352
  user: computed(() => user.value),
186
353
  loading: computed(() => loading.value),
@@ -195,6 +362,7 @@ export function _createInternalUserLogic(): IUserInternalAPI {
195
362
  requestPasswordReset,
196
363
  changeUserPassword,
197
364
  getLoginType,
365
+ getAccessToken,
198
366
  };
199
367
  }
200
368
 
@@ -207,5 +375,6 @@ export const useUser = createSharedComposable((): IAppUserAPI => {
207
375
  isAdministrator: internals.isAdministrator,
208
376
  loadUser: internals.loadUser,
209
377
  signOut: internals.signOut,
378
+ getAccessToken: internals.getAccessToken,
210
379
  };
211
380
  });