@vc-shell/vc-app-skill 2.0.0-alpha.32 → 2.0.0-alpha.33-pr220.455e322
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -47
- package/README.md +18 -12
- package/package.json +4 -4
- package/runtime/VERSION +1 -1
- package/runtime/agents/api-analyzer.md +31 -16
- package/runtime/agents/blade-enhancer.md +15 -9
- package/runtime/agents/details-blade-generator.md +47 -31
- package/runtime/agents/list-blade-generator.md +21 -37
- package/runtime/agents/locales-generator.md +3 -0
- package/runtime/agents/migration-agent.md +94 -0
- package/runtime/agents/module-analyzer.md +2 -0
- package/runtime/agents/module-assembler.md +15 -0
- package/runtime/agents/promote-agent.md +15 -4
- package/runtime/agents/type-checker.md +11 -0
- package/runtime/knowledge/docs/_BUILD_HASH.md +1 -1
- package/runtime/knowledge/docs/core/api/platform.docs.md +30 -30
- package/runtime/knowledge/docs/core/blade-navigation/blade-nav-composables.docs.md +41 -41
- package/runtime/knowledge/docs/core/composables/bladeContext/index.docs.md +12 -10
- package/runtime/knowledge/docs/core/composables/useApiClient/useApiClient.docs.md +11 -14
- package/runtime/knowledge/docs/core/composables/useAppBarMobileButtons/useAppBarMobileButtons.docs.md +35 -35
- package/runtime/knowledge/docs/core/composables/useAppBarWidget/useAppBarWidget.docs.md +35 -35
- package/runtime/knowledge/docs/core/composables/useAppInsights/useAppInsights.docs.md +15 -15
- package/runtime/knowledge/docs/core/composables/useAssets/useAssets.docs.md +21 -18
- package/runtime/knowledge/docs/core/composables/useAssetsManager/useAssetsManager.docs.md +31 -27
- package/runtime/knowledge/docs/core/composables/useAsync/useAsync.docs.md +90 -61
- package/runtime/knowledge/docs/core/composables/useBeforeUnload/useBeforeUnload.docs.md +19 -18
- package/runtime/knowledge/docs/core/composables/useBlade/useBlade.docs.md +89 -68
- package/runtime/knowledge/docs/core/composables/useBladeForm/useBladeForm.docs.md +75 -19
- package/runtime/knowledge/docs/core/composables/useBladeRegistry/useBladeRegistry.docs.md +15 -15
- package/runtime/knowledge/docs/core/composables/useBladeWidgets/index.docs.md +74 -78
- package/runtime/knowledge/docs/core/composables/useBreadcrumbs/useBreadcrumbs.docs.md +11 -11
- package/runtime/knowledge/docs/core/composables/useConnectionStatus/useConnectionStatus.docs.md +27 -15
- package/runtime/knowledge/docs/core/composables/useDashboard/useDashboard.docs.md +30 -30
- package/runtime/knowledge/docs/core/composables/useDynamicProperties/useDynamicProperties.docs.md +34 -36
- package/runtime/knowledge/docs/core/composables/useErrorHandler/useErrorHandler.docs.md +44 -23
- package/runtime/knowledge/docs/core/composables/useFunctions/useFunctions.docs.md +14 -11
- package/runtime/knowledge/docs/core/composables/useKeyboardNavigation/useKeyboardNavigation.docs.md +47 -38
- package/runtime/knowledge/docs/core/composables/useLanguages/useLanguages.docs.md +37 -28
- package/runtime/knowledge/docs/core/composables/useLoading/useLoading.docs.md +23 -17
- package/runtime/knowledge/docs/core/composables/useMenuExpanded/index.docs.md +10 -10
- package/runtime/knowledge/docs/core/composables/useMenuService/useMenuService.docs.md +42 -42
- package/runtime/knowledge/docs/core/composables/useModificationTracker/useModificationTracker.docs.md +22 -12
- package/runtime/knowledge/docs/core/composables/useNotifications/useNotifications.docs.md +33 -41
- package/runtime/knowledge/docs/core/composables/usePermissions/usePermissions.docs.md +16 -16
- package/runtime/knowledge/docs/core/composables/usePopup/usePopup.docs.md +32 -24
- package/runtime/knowledge/docs/core/composables/useResponsive/useResponsive.docs.md +32 -11
- package/runtime/knowledge/docs/core/composables/useSettings/useSettings.docs.md +24 -15
- package/runtime/knowledge/docs/core/composables/useSettingsMenu/useSettingsMenu.docs.md +7 -7
- package/runtime/knowledge/docs/core/composables/useSidebarState/useSidebarState.docs.md +32 -24
- package/runtime/knowledge/docs/core/composables/useSlowNetworkDetection/useSlowNetworkDetection.docs.md +21 -17
- package/runtime/knowledge/docs/core/composables/useTheme/useTheme.docs.md +24 -24
- package/runtime/knowledge/docs/core/composables/useToolbar/useToolbar.docs.md +28 -31
- package/runtime/knowledge/docs/core/composables/useUser/useUser.docs.md +43 -24
- package/runtime/knowledge/docs/core/composables/useUserManagement/useUserManagement.docs.md +68 -48
- package/runtime/knowledge/docs/core/composables/useWebVitals/useWebVitals.docs.md +19 -19
- package/runtime/knowledge/docs/core/composables/useWidgets/useWidgets.docs.md +42 -47
- package/runtime/knowledge/docs/core/directives/autofocus/autofocus.docs.md +10 -4
- package/runtime/knowledge/docs/core/directives/loading/loading.docs.md +35 -20
- package/runtime/knowledge/docs/core/notifications/notifications.docs.md +36 -35
- package/runtime/knowledge/docs/core/plugins/ai-agent/ai-agent.docs.md +38 -38
- package/runtime/knowledge/docs/core/plugins/extension-points/extension-points.docs.md +107 -91
- package/runtime/knowledge/docs/core/plugins/global-error-handler/global-error-handler.docs.md +10 -10
- package/runtime/knowledge/docs/core/plugins/i18n/i18n.docs.md +21 -23
- package/runtime/knowledge/docs/core/plugins/modularity/modularity.docs.md +98 -90
- package/runtime/knowledge/docs/core/plugins/permissions/permissions.docs.md +10 -16
- package/runtime/knowledge/docs/core/plugins/signalR/signalR.docs.md +9 -9
- package/runtime/knowledge/docs/core/plugins/validation/validation.docs.md +65 -22
- package/runtime/knowledge/docs/core/services/services.docs.md +19 -22
- package/runtime/knowledge/docs/core/types/types.docs.md +40 -40
- package/runtime/knowledge/docs/core/utilities/date/date-utilities.docs.md +27 -27
- package/runtime/knowledge/docs/core/utilities/shared-utilities.docs.md +23 -23
- package/runtime/knowledge/docs/core/utilities/thumbnail/thumbnail.docs.md +22 -25
- package/runtime/knowledge/docs/core/utilities/utilities.docs.md +64 -64
- package/runtime/knowledge/docs/injection-keys.docs.md +52 -51
- package/runtime/knowledge/docs/modules/assets-manager/assets-manager.docs.md +9 -9
- package/runtime/knowledge/docs/shell/_internal/popup/common/popup-common.docs.md +23 -43
- package/runtime/knowledge/docs/shell/auth/ChangePasswordPage/change-password-page.docs.md +102 -0
- package/runtime/knowledge/docs/shell/auth/ForgotPasswordPage/forgot-password-page.docs.md +5 -5
- package/runtime/knowledge/docs/shell/auth/InvitePage/invite-page.docs.md +8 -7
- package/runtime/knowledge/docs/shell/auth/LoginPage/login-page.docs.md +7 -7
- package/runtime/knowledge/docs/shell/auth/ResetPasswordPage/reset-password-page.docs.md +8 -7
- package/runtime/knowledge/docs/shell/auth/sign-in/sign-in.docs.md +29 -13
- package/runtime/knowledge/docs/shell/components/change-password/change-password.docs.md +13 -16
- package/runtime/knowledge/docs/shell/components/change-password-button/change-password-button.docs.md +1 -7
- package/runtime/knowledge/docs/shell/components/error-interceptor/error-interceptor.docs.md +5 -5
- package/runtime/knowledge/docs/shell/components/language-selector/language-selector.docs.md +1 -1
- package/runtime/knowledge/docs/shell/components/logout-button/logout-button.docs.md +1 -1
- package/runtime/knowledge/docs/shell/components/notification-template/notification-template.docs.md +17 -9
- package/runtime/knowledge/docs/shell/components/settings-menu/settings-menu.docs.md +12 -18
- package/runtime/knowledge/docs/shell/components/settings-menu-item/settings-menu-item.docs.md +34 -65
- package/runtime/knowledge/docs/shell/components/sidebar/sidebar.docs.md +16 -26
- package/runtime/knowledge/docs/shell/components/theme-selector/theme-selector.docs.md +2 -2
- package/runtime/knowledge/docs/shell/components/user-dropdown-button/user-dropdown-button.docs.md +7 -9
- package/runtime/knowledge/docs/shell/dashboard/dashboard-charts/dashboard-charts.docs.md +30 -40
- package/runtime/knowledge/docs/shell/dashboard/dashboard-widget-card/dashboard-widget-card.docs.md +26 -19
- package/runtime/knowledge/docs/shell/dashboard/draggable-dashboard/draggable-dashboard.docs.md +15 -12
- package/runtime/knowledge/docs/ui/components/atoms/vc-badge/vc-badge.docs.md +15 -26
- package/runtime/knowledge/docs/ui/components/atoms/vc-banner/vc-banner.docs.md +21 -19
- package/runtime/knowledge/docs/ui/components/atoms/vc-button/vc-button.docs.md +83 -67
- package/runtime/knowledge/docs/ui/components/atoms/vc-card/vc-card.docs.md +104 -59
- package/runtime/knowledge/docs/ui/components/atoms/vc-col/vc-col.docs.md +28 -11
- package/runtime/knowledge/docs/ui/components/atoms/vc-container/vc-container.docs.md +20 -17
- package/runtime/knowledge/docs/ui/components/atoms/vc-hint/vc-hint.docs.md +26 -17
- package/runtime/knowledge/docs/ui/components/atoms/vc-icon/vc-icon.docs.md +30 -32
- package/runtime/knowledge/docs/ui/components/atoms/vc-image/vc-image.docs.md +25 -48
- package/runtime/knowledge/docs/ui/components/atoms/vc-label/vc-label.docs.md +29 -24
- package/runtime/knowledge/docs/ui/components/atoms/vc-link/vc-link.docs.md +23 -15
- package/runtime/knowledge/docs/ui/components/atoms/vc-loading/vc-loading.docs.md +22 -13
- package/runtime/knowledge/docs/ui/components/atoms/vc-progress/vc-progress.docs.md +33 -18
- package/runtime/knowledge/docs/ui/components/atoms/vc-row/vc-row.docs.md +56 -15
- package/runtime/knowledge/docs/ui/components/atoms/vc-scrollable-container/vc-scrollable-container.docs.md +28 -15
- package/runtime/knowledge/docs/ui/components/atoms/vc-skeleton/vc-skeleton.docs.md +40 -20
- package/runtime/knowledge/docs/ui/components/atoms/vc-status/vc-status.docs.md +25 -14
- package/runtime/knowledge/docs/ui/components/atoms/vc-status-icon/vc-status-icon.docs.md +40 -14
- package/runtime/knowledge/docs/ui/components/atoms/vc-tooltip/vc-tooltip.docs.md +54 -42
- package/runtime/knowledge/docs/ui/components/atoms/vc-video/vc-video.docs.md +17 -17
- package/runtime/knowledge/docs/ui/components/atoms/vc-widget/vc-widget.docs.md +21 -21
- package/runtime/knowledge/docs/ui/components/molecules/multilanguage-selector/multilanguage-selector.docs.md +23 -10
- package/runtime/knowledge/docs/ui/components/molecules/vc-accordion/vc-accordion.docs.md +59 -44
- package/runtime/knowledge/docs/ui/components/molecules/vc-breadcrumbs/vc-breadcrumbs.docs.md +23 -20
- package/runtime/knowledge/docs/ui/components/molecules/vc-checkbox/vc-checkbox.docs.md +96 -64
- package/runtime/knowledge/docs/ui/components/molecules/vc-checkbox-group/vc-checkbox-group.docs.md +26 -35
- package/runtime/knowledge/docs/ui/components/molecules/vc-color-input/vc-color-input.docs.md +69 -22
- package/runtime/knowledge/docs/ui/components/molecules/vc-date-picker/vc-date-picker.docs.md +58 -72
- package/runtime/knowledge/docs/ui/components/molecules/vc-dropdown/vc-dropdown.docs.md +91 -85
- package/runtime/knowledge/docs/ui/components/molecules/vc-dropdown-panel/vc-dropdown-panel.docs.md +38 -42
- package/runtime/knowledge/docs/ui/components/molecules/vc-editor/vc-editor.docs.md +60 -72
- package/runtime/knowledge/docs/ui/components/molecules/vc-field/vc-field.docs.md +65 -26
- package/runtime/knowledge/docs/ui/components/molecules/vc-file-upload/vc-file-upload.docs.md +46 -49
- package/runtime/knowledge/docs/ui/components/molecules/vc-form/vc-form.docs.md +35 -64
- package/runtime/knowledge/docs/ui/components/molecules/vc-image-tile/vc-image-tile.docs.md +38 -41
- package/runtime/knowledge/docs/ui/components/molecules/vc-input/vc-input.docs.md +115 -130
- package/runtime/knowledge/docs/ui/components/molecules/vc-input-currency/vc-input-currency.docs.md +53 -87
- package/runtime/knowledge/docs/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.docs.md +56 -63
- package/runtime/knowledge/docs/ui/components/molecules/vc-input-group/vc-input-group.docs.md +29 -24
- package/runtime/knowledge/docs/ui/components/molecules/vc-menu/vc-menu.docs.md +32 -28
- package/runtime/knowledge/docs/ui/components/molecules/vc-multivalue/vc-multivalue.docs.md +63 -64
- package/runtime/knowledge/docs/ui/components/molecules/vc-pagination/vc-pagination.docs.md +28 -26
- package/runtime/knowledge/docs/ui/components/molecules/vc-radio-button/vc-radio-button.docs.md +59 -19
- package/runtime/knowledge/docs/ui/components/molecules/vc-radio-group/vc-radio-group.docs.md +25 -34
- package/runtime/knowledge/docs/ui/components/molecules/vc-rating/vc-rating.docs.md +42 -32
- package/runtime/knowledge/docs/ui/components/molecules/vc-select/vc-select.docs.md +78 -82
- package/runtime/knowledge/docs/ui/components/molecules/vc-slider/vc-slider.docs.md +25 -15
- package/runtime/knowledge/docs/ui/components/molecules/vc-switch/vc-switch.docs.md +59 -63
- package/runtime/knowledge/docs/ui/components/molecules/vc-textarea/vc-textarea.docs.md +57 -69
- package/runtime/knowledge/docs/ui/components/molecules/vc-toast/vc-toast.docs.md +58 -57
- package/runtime/knowledge/docs/ui/components/organisms/vc-app/vc-app.docs.md +49 -26
- package/runtime/knowledge/docs/ui/components/organisms/vc-auth-layout/vc-auth-layout.docs.md +82 -28
- package/runtime/knowledge/docs/ui/components/organisms/vc-blade/vc-blade.docs.md +120 -75
- package/runtime/knowledge/docs/ui/components/organisms/vc-data-table/composables/table-composables.docs.md +30 -44
- package/runtime/knowledge/docs/ui/components/organisms/vc-data-table/vc-data-table.docs.md +536 -365
- package/runtime/knowledge/docs/ui/components/organisms/vc-dynamic-property/vc-dynamic-property.docs.md +35 -52
- package/runtime/knowledge/docs/ui/components/organisms/vc-gallery/vc-gallery.docs.md +33 -62
- package/runtime/knowledge/docs/ui/components/organisms/vc-image-upload/vc-image-upload.docs.md +17 -23
- package/runtime/knowledge/docs/ui/components/organisms/vc-popup/vc-popup.docs.md +109 -68
- package/runtime/knowledge/docs/ui/components/organisms/vc-sidebar/vc-sidebar.docs.md +82 -44
- package/runtime/knowledge/docs/ui/composables/ui-composables.docs.md +8 -8
- package/runtime/knowledge/docs/ui/composables/useDataTablePagination.docs.md +164 -0
- package/runtime/knowledge/docs/ui/composables/useDataTableSort.docs.md +34 -26
- package/runtime/knowledge/docs/ui/composables/useTableSelection.docs.md +48 -40
- package/runtime/knowledge/docs/ui/composables/useTableSort.docs.md +30 -17
- package/runtime/knowledge/docs/ui/types/ui-types.docs.md +40 -29
- package/runtime/knowledge/examples/offers-module.md +15 -13
- package/runtime/knowledge/examples/team-module.md +82 -119
- package/runtime/knowledge/examples/videos-module.md +44 -17
- package/runtime/knowledge/index.md +22 -0
- package/runtime/knowledge/migration-prompts/blade-form-migration.md +255 -0
- package/runtime/knowledge/migration-prompts/blade-props-migration.md +194 -0
- package/runtime/knowledge/migration-prompts/datatable-migration.md +801 -0
- package/runtime/knowledge/migration-prompts/icon-migration.md +97 -0
- package/runtime/knowledge/migration-prompts/manual-migration-audit.md +117 -0
- package/runtime/knowledge/migration-prompts/notifications-migration.md +223 -0
- package/runtime/knowledge/migration-prompts/nswag-migration.md +244 -0
- package/runtime/knowledge/migration-prompts/use-assets-migration.md +164 -0
- package/runtime/knowledge/migration-prompts/use-data-table-pagination-migration.md +176 -0
- package/runtime/knowledge/migration-prompts/widgets-migration.md +178 -0
- package/runtime/knowledge/patterns/assets-management.md +20 -20
- package/runtime/knowledge/patterns/blade-navigation.md +7 -14
- package/runtime/knowledge/patterns/blade-widget.md +19 -17
- package/runtime/knowledge/patterns/child-blade-flow.md +19 -7
- package/runtime/knowledge/patterns/composable-details.md +20 -50
- package/runtime/knowledge/patterns/composable-list.md +43 -31
- package/runtime/knowledge/patterns/dashboard-widget.md +14 -16
- package/runtime/knowledge/patterns/datatable-pattern.md +521 -0
- package/runtime/knowledge/patterns/details-blade-pattern.md +78 -116
- package/runtime/knowledge/patterns/extension-points-usage.md +53 -44
- package/runtime/knowledge/patterns/form-validation.md +28 -64
- package/runtime/knowledge/patterns/list-blade-pattern.md +33 -21
- package/runtime/knowledge/patterns/module-structure.md +7 -1
- package/runtime/knowledge/patterns/multilanguage-fields.md +8 -14
- package/runtime/knowledge/patterns/notification-template.md +21 -14
- package/runtime/knowledge/patterns/signalr-notifications.md +30 -32
- package/runtime/knowledge/patterns/toolbar-pattern.md +18 -20
- package/runtime/vc-app.md +354 -49
- package/runtime/knowledge/docs/core/constants/constants.docs.md +0 -185
|
@@ -16,17 +16,26 @@ Uses `createSharedComposable` from VueUse, so all callers across the entire appl
|
|
|
16
16
|
|
|
17
17
|
```vue
|
|
18
18
|
<script setup lang="ts">
|
|
19
|
-
import { useUser } from
|
|
19
|
+
import { useUser } from "@vc-shell/framework";
|
|
20
20
|
|
|
21
21
|
const { user, isAuthenticated, isAdministrator, loading } = useUser();
|
|
22
22
|
</script>
|
|
23
23
|
|
|
24
24
|
<template>
|
|
25
|
-
<div
|
|
25
|
+
<div
|
|
26
|
+
v-if="loading"
|
|
27
|
+
class="tw-animate-pulse"
|
|
28
|
+
>
|
|
29
|
+
Loading user...
|
|
30
|
+
</div>
|
|
26
31
|
|
|
27
32
|
<div v-else-if="isAuthenticated">
|
|
28
33
|
<p>Welcome, {{ user?.userName }}</p>
|
|
29
|
-
<span
|
|
34
|
+
<span
|
|
35
|
+
v-if="isAdministrator"
|
|
36
|
+
class="tw-badge tw-badge-primary"
|
|
37
|
+
>Admin</span
|
|
38
|
+
>
|
|
30
39
|
</div>
|
|
31
40
|
|
|
32
41
|
<div v-else>
|
|
@@ -43,15 +52,15 @@ None.
|
|
|
43
52
|
|
|
44
53
|
### Returns
|
|
45
54
|
|
|
46
|
-
| Property
|
|
47
|
-
|
|
48
|
-
| `user`
|
|
49
|
-
| `loading`
|
|
50
|
-
| `isAuthenticated` | `ComputedRef<boolean>`
|
|
51
|
-
| `isAdministrator` | `ComputedRef<boolean \| undefined>`
|
|
52
|
-
| `loadUser`
|
|
53
|
-
| `signOut`
|
|
54
|
-
| `getAccessToken`
|
|
55
|
+
| Property | Type | Description |
|
|
56
|
+
| ----------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
57
|
+
| `user` | `ComputedRef<UserDetail \| undefined>` | Current user details (userName, email, id, etc.), or `undefined` if not authenticated. |
|
|
58
|
+
| `loading` | `ComputedRef<boolean>` | Whether a user operation (loadUser, signOut) is in progress. |
|
|
59
|
+
| `isAuthenticated` | `ComputedRef<boolean>` | Whether a user session is active. Derived from `user.userName != null`. |
|
|
60
|
+
| `isAdministrator` | `ComputedRef<boolean \| undefined>` | Whether the current user has admin privileges. `undefined` if user is not loaded. |
|
|
61
|
+
| `loadUser` | `() => Promise<UserDetail>` | Loads/reloads user info from the server. Deduplicates concurrent calls -- if two components call `loadUser()` at the same time, only one API request is made. |
|
|
62
|
+
| `signOut` | `() => Promise<void>` | Signs out the current user, clears auth data from localStorage, and resets the user state. Handles both standard and external (SSO) sign-out flows. |
|
|
63
|
+
| `getAccessToken` | `() => Promise<string \| null>` | Returns the current OAuth access token. Automatically refreshes using the refresh token if the access token has expired or will expire within 60 seconds. Returns `null` if no auth data exists. |
|
|
55
64
|
|
|
56
65
|
## How It Works
|
|
57
66
|
|
|
@@ -63,14 +72,16 @@ The composable delegates to `_createInternalUserLogic()`, which manages:
|
|
|
63
72
|
|
|
64
73
|
3. **Token refresh**: `getAccessToken()` checks the expiration time with a 60-second buffer. If the token is expired or about to expire and a refresh token is available, it makes a `POST /connect/token` request with `grant_type: refresh_token`.
|
|
65
74
|
|
|
66
|
-
4. **Shared state**: `createSharedComposable` from VueUse
|
|
75
|
+
4. **Shared state**: `createSharedComposable` from VueUse wraps `_createInternalUserLogic` so every call to `useUser()` or `useUserManagement()` reads from the same singleton internal logic. The `user` ref lives inside that singleton. App bootstrap registers `useUserManagement()` outside any component scope, which pins the singleton for the app lifetime — transient component mount/unmount cycles don't discard it.
|
|
67
76
|
|
|
68
77
|
## Recipe: Route Guard Based on Authentication
|
|
69
78
|
|
|
70
79
|
```typescript
|
|
71
|
-
import { useUser } from
|
|
80
|
+
import { useUser } from "@vc-shell/framework";
|
|
72
81
|
|
|
73
|
-
const router = createRouter({
|
|
82
|
+
const router = createRouter({
|
|
83
|
+
/* ... */
|
|
84
|
+
});
|
|
74
85
|
|
|
75
86
|
router.beforeEach(async (to) => {
|
|
76
87
|
const { isAuthenticated, loadUser } = useUser();
|
|
@@ -85,7 +96,7 @@ router.beforeEach(async (to) => {
|
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
if (to.meta.requiresAuth && !isAuthenticated.value) {
|
|
88
|
-
return { name:
|
|
99
|
+
return { name: "SignIn" };
|
|
89
100
|
}
|
|
90
101
|
});
|
|
91
102
|
```
|
|
@@ -95,25 +106,33 @@ router.beforeEach(async (to) => {
|
|
|
95
106
|
```vue
|
|
96
107
|
<template>
|
|
97
108
|
<div class="tw-flex tw-items-center tw-gap-2">
|
|
98
|
-
<VcAvatar
|
|
109
|
+
<VcAvatar
|
|
110
|
+
:name="user?.userName"
|
|
111
|
+
size="sm"
|
|
112
|
+
/>
|
|
99
113
|
<div>
|
|
100
114
|
<p class="tw-text-sm tw-font-medium">{{ user?.userName }}</p>
|
|
101
115
|
<p class="tw-text-xs tw-text-gray-500">{{ user?.email }}</p>
|
|
102
116
|
</div>
|
|
103
|
-
<VcButton
|
|
117
|
+
<VcButton
|
|
118
|
+
size="sm"
|
|
119
|
+
variant="ghost"
|
|
120
|
+
@click="handleSignOut"
|
|
121
|
+
>Sign Out</VcButton
|
|
122
|
+
>
|
|
104
123
|
</div>
|
|
105
124
|
</template>
|
|
106
125
|
|
|
107
126
|
<script setup lang="ts">
|
|
108
|
-
import { useUser } from
|
|
109
|
-
import { useRouter } from
|
|
127
|
+
import { useUser } from "@vc-shell/framework";
|
|
128
|
+
import { useRouter } from "vue-router";
|
|
110
129
|
|
|
111
130
|
const { user, signOut } = useUser();
|
|
112
131
|
const router = useRouter();
|
|
113
132
|
|
|
114
133
|
async function handleSignOut() {
|
|
115
134
|
await signOut();
|
|
116
|
-
router.push({ name:
|
|
135
|
+
router.push({ name: "SignIn" });
|
|
117
136
|
}
|
|
118
137
|
</script>
|
|
119
138
|
```
|
|
@@ -121,7 +140,7 @@ async function handleSignOut() {
|
|
|
121
140
|
## Recipe: Adding Access Token to Custom API Calls
|
|
122
141
|
|
|
123
142
|
```typescript
|
|
124
|
-
import { useUser } from
|
|
143
|
+
import { useUser } from "@vc-shell/framework";
|
|
125
144
|
|
|
126
145
|
const { getAccessToken } = useUser();
|
|
127
146
|
|
|
@@ -130,7 +149,7 @@ async function fetchCustomEndpoint(url: string) {
|
|
|
130
149
|
|
|
131
150
|
const response = await fetch(url, {
|
|
132
151
|
headers: {
|
|
133
|
-
Authorization: token ? `Bearer ${token}` :
|
|
152
|
+
Authorization: token ? `Bearer ${token}` : "",
|
|
134
153
|
},
|
|
135
154
|
});
|
|
136
155
|
|
|
@@ -140,7 +159,7 @@ async function fetchCustomEndpoint(url: string) {
|
|
|
140
159
|
|
|
141
160
|
## Tips
|
|
142
161
|
|
|
143
|
-
- **`loadUser` deduplicates concurrent calls.** If
|
|
162
|
+
- **`loadUser` deduplicates concurrent calls across composables.** If multiple blades call `loadUser()` simultaneously — or if `useUser()` and `useUserManagement()` are both used in the same flow — only one API request is made. `useUser` and `useUserManagement` share the singleton internal logic, so request deduplication works across both.
|
|
144
163
|
- **Token refresh is transparent.** `getAccessToken()` handles expiration checking and refresh internally. You never need to manually check token expiration or call a refresh endpoint.
|
|
145
164
|
- **`isAuthenticated` checks `userName`, not the token.** A user is considered authenticated if `user.value?.userName` is not null. This means the user could have an expired token but still appear "authenticated" until the next API call fails.
|
|
146
165
|
- **`signOut` clears localStorage.** After sign-out, the `vc_auth_data` key is removed from localStorage. Any cached tokens are gone permanently.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Shared composable exposing the full user management API: sign-in, sign-out, password reset, token validation, and login type discovery. This composable extends the same internal logic as `useUser` but surfaces the administrative and authentication-flow methods that `useUser` intentionally hides. The separation ensures that regular blades only have access to read-only user state, while auth pages and admin screens get the full API.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Both `useUser()` and `useUserManagement()` read from the same singleton internal logic, so everything is shared: the `user` ref, `authData`, the `loading` ref, in-flight request deduplication (`loadUser`), and the underlying `SecurityClient` instance. The two composables are thin selectors over a single source of truth — picking a narrow read-only slice or the full management slice.
|
|
6
6
|
|
|
7
7
|
## When to Use
|
|
8
8
|
|
|
@@ -17,29 +17,45 @@ Uses `createSharedComposable` from VueUse, so all callers share the same state i
|
|
|
17
17
|
|
|
18
18
|
```vue
|
|
19
19
|
<script setup lang="ts">
|
|
20
|
-
import { useUserManagement } from
|
|
21
|
-
import { ref } from
|
|
20
|
+
import { useUserManagement } from "@vc-shell/framework";
|
|
21
|
+
import { ref } from "vue";
|
|
22
22
|
|
|
23
23
|
const { signIn, loading, isAuthenticated } = useUserManagement();
|
|
24
|
-
const username = ref(
|
|
25
|
-
const password = ref(
|
|
26
|
-
const errorMessage = ref(
|
|
24
|
+
const username = ref("");
|
|
25
|
+
const password = ref("");
|
|
26
|
+
const errorMessage = ref("");
|
|
27
27
|
|
|
28
28
|
async function handleLogin() {
|
|
29
|
-
errorMessage.value =
|
|
29
|
+
errorMessage.value = "";
|
|
30
30
|
const result = await signIn(username.value, password.value);
|
|
31
31
|
if (!result.succeeded) {
|
|
32
|
-
errorMessage.value = result.error ??
|
|
32
|
+
errorMessage.value = result.error ?? "Login failed";
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
</script>
|
|
36
36
|
|
|
37
37
|
<template>
|
|
38
38
|
<form @submit.prevent="handleLogin">
|
|
39
|
-
<VcInput
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
<VcInput
|
|
40
|
+
v-model="username"
|
|
41
|
+
label="Username"
|
|
42
|
+
/>
|
|
43
|
+
<VcInput
|
|
44
|
+
v-model="password"
|
|
45
|
+
label="Password"
|
|
46
|
+
type="password"
|
|
47
|
+
/>
|
|
48
|
+
<p
|
|
49
|
+
v-if="errorMessage"
|
|
50
|
+
class="tw-text-red-500"
|
|
51
|
+
>
|
|
52
|
+
{{ errorMessage }}
|
|
53
|
+
</p>
|
|
54
|
+
<VcButton
|
|
55
|
+
type="submit"
|
|
56
|
+
:loading="loading"
|
|
57
|
+
>Sign In</VcButton
|
|
58
|
+
>
|
|
43
59
|
</form>
|
|
44
60
|
</template>
|
|
45
61
|
```
|
|
@@ -52,25 +68,25 @@ None.
|
|
|
52
68
|
|
|
53
69
|
### Returns
|
|
54
70
|
|
|
55
|
-
| Property
|
|
56
|
-
|
|
57
|
-
| `user`
|
|
58
|
-
| `loading`
|
|
59
|
-
| `isAdministrator`
|
|
60
|
-
| `isAuthenticated`
|
|
61
|
-
| `signIn`
|
|
62
|
-
| `signOut`
|
|
63
|
-
| `loadUser`
|
|
64
|
-
| `validateToken`
|
|
65
|
-
| `validatePassword`
|
|
66
|
-
| `resetPasswordByToken` | `(userId: string, password: string, token: string) => Promise<SecurityResult>`
|
|
67
|
-
| `requestPasswordReset` | `(loginOrEmail: string) => Promise<RequestPasswordResult>`
|
|
68
|
-
| `changeUserPassword`
|
|
69
|
-
| `getLoginType`
|
|
71
|
+
| Property | Type | Description |
|
|
72
|
+
| ---------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
73
|
+
| `user` | `ComputedRef<UserDetail \| undefined>` | Current user details. |
|
|
74
|
+
| `loading` | `ComputedRef<boolean>` | Whether any operation is in progress. |
|
|
75
|
+
| `isAdministrator` | `ComputedRef<boolean \| undefined>` | Admin status of the current user. |
|
|
76
|
+
| `isAuthenticated` | `ComputedRef<boolean>` | Whether user session is active. |
|
|
77
|
+
| `signIn` | `(username: string, password: string) => Promise<SignInResult>` | Authenticates with username/password. Performs cookie-based login, obtains an OAuth token with offline_access scope, and loads user details. Returns `{ succeeded: true }` on success or `{ succeeded: false, error: string }` on failure. |
|
|
78
|
+
| `signOut` | `() => Promise<void>` | Clears session, auth data, and localStorage. Handles both standard and external SSO sign-out. |
|
|
79
|
+
| `loadUser` | `() => Promise<UserDetail>` | Loads/reloads user info from the server. Deduplicates concurrent calls. |
|
|
80
|
+
| `validateToken` | `(userId: string, token: string) => Promise<boolean>` | Validates a password-reset token. Returns `true` if valid. |
|
|
81
|
+
| `validatePassword` | `(password: string) => Promise<IdentityResult>` | Validates a password against the platform's password policy (length, complexity, etc.). |
|
|
82
|
+
| `resetPasswordByToken` | `(userId: string, password: string, token: string) => Promise<SecurityResult>` | Resets a user's password using a reset token. Returns `{ succeeded: true }` on success. |
|
|
83
|
+
| `requestPasswordReset` | `(loginOrEmail: string) => Promise<RequestPasswordResult>` | Sends a password-reset email to the user. Returns `{ succeeded: true }` on success. |
|
|
84
|
+
| `changeUserPassword` | `(oldPassword: string, newPassword: string) => Promise<SecurityResult \| undefined>` | Changes the current user's password. Requires the old password for verification. |
|
|
85
|
+
| `getLoginType` | `() => Promise<LoginType[]>` | Returns available login types (password-based, external SSO providers, etc.). Used to render the appropriate sign-in UI. |
|
|
70
86
|
|
|
71
87
|
## How It Works
|
|
72
88
|
|
|
73
|
-
The composable
|
|
89
|
+
The composable reads from the singleton internal user logic (instantiated lazily on first call to `useUser()` or `useUserManagement()`). The `signIn` flow has three phases:
|
|
74
90
|
|
|
75
91
|
1. **Cookie login**: Calls `securityClient.login()` to establish a server-side session cookie.
|
|
76
92
|
2. **OAuth token**: Fetches an OAuth token via `POST /connect/token` with `grant_type: password` and `scope: offline_access`. The refresh token enables automatic token renewal.
|
|
@@ -82,9 +98,9 @@ Token data is stored in localStorage under `vc_auth_data` and survives page relo
|
|
|
82
98
|
|
|
83
99
|
```vue
|
|
84
100
|
<script setup lang="ts">
|
|
85
|
-
import { useUserManagement } from
|
|
86
|
-
import { ref } from
|
|
87
|
-
import { useRoute, useRouter } from
|
|
101
|
+
import { useUserManagement } from "@vc-shell/framework";
|
|
102
|
+
import { ref } from "vue";
|
|
103
|
+
import { useRoute, useRouter } from "vue-router";
|
|
88
104
|
|
|
89
105
|
const { validateToken, validatePassword, resetPasswordByToken, loading } = useUserManagement();
|
|
90
106
|
const route = useRoute();
|
|
@@ -92,39 +108,39 @@ const router = useRouter();
|
|
|
92
108
|
|
|
93
109
|
const userId = route.query.userId as string;
|
|
94
110
|
const token = route.query.token as string;
|
|
95
|
-
const newPassword = ref(
|
|
96
|
-
const confirmPassword = ref(
|
|
97
|
-
const error = ref(
|
|
111
|
+
const newPassword = ref("");
|
|
112
|
+
const confirmPassword = ref("");
|
|
113
|
+
const error = ref("");
|
|
98
114
|
const tokenValid = ref(false);
|
|
99
115
|
|
|
100
116
|
// Validate the token on mount
|
|
101
117
|
onMounted(async () => {
|
|
102
118
|
tokenValid.value = await validateToken(userId, token);
|
|
103
119
|
if (!tokenValid.value) {
|
|
104
|
-
error.value =
|
|
120
|
+
error.value = "This reset link has expired or is invalid.";
|
|
105
121
|
}
|
|
106
122
|
});
|
|
107
123
|
|
|
108
124
|
async function handleReset() {
|
|
109
|
-
error.value =
|
|
125
|
+
error.value = "";
|
|
110
126
|
|
|
111
127
|
if (newPassword.value !== confirmPassword.value) {
|
|
112
|
-
error.value =
|
|
128
|
+
error.value = "Passwords do not match.";
|
|
113
129
|
return;
|
|
114
130
|
}
|
|
115
131
|
|
|
116
132
|
// Validate against password policy
|
|
117
133
|
const validation = await validatePassword(newPassword.value);
|
|
118
134
|
if (!validation.succeeded) {
|
|
119
|
-
error.value = validation.errors?.join(
|
|
135
|
+
error.value = validation.errors?.join(", ") ?? "Password does not meet requirements.";
|
|
120
136
|
return;
|
|
121
137
|
}
|
|
122
138
|
|
|
123
139
|
const result = await resetPasswordByToken(userId, newPassword.value, token);
|
|
124
140
|
if (result.succeeded) {
|
|
125
|
-
router.push({ name:
|
|
141
|
+
router.push({ name: "SignIn", query: { message: "Password reset successfully" } });
|
|
126
142
|
} else {
|
|
127
|
-
error.value = result.errors?.join(
|
|
143
|
+
error.value = result.errors?.join(", ") ?? "Reset failed.";
|
|
128
144
|
}
|
|
129
145
|
}
|
|
130
146
|
</script>
|
|
@@ -134,8 +150,8 @@ async function handleReset() {
|
|
|
134
150
|
|
|
135
151
|
```vue
|
|
136
152
|
<script setup lang="ts">
|
|
137
|
-
import { useUserManagement } from
|
|
138
|
-
import { ref, onMounted } from
|
|
153
|
+
import { useUserManagement } from "@vc-shell/framework";
|
|
154
|
+
import { ref, onMounted } from "vue";
|
|
139
155
|
|
|
140
156
|
const { getLoginType, signIn } = useUserManagement();
|
|
141
157
|
const loginTypes = ref<LoginType[]>([]);
|
|
@@ -144,20 +160,24 @@ const showPasswordForm = ref(false);
|
|
|
144
160
|
onMounted(async () => {
|
|
145
161
|
loginTypes.value = await getLoginType();
|
|
146
162
|
// Show password form only if password auth is available
|
|
147
|
-
showPasswordForm.value = loginTypes.value.some((t) => t.loginProvider ===
|
|
163
|
+
showPasswordForm.value = loginTypes.value.some((t) => t.loginProvider === "password");
|
|
148
164
|
});
|
|
149
165
|
</script>
|
|
150
166
|
|
|
151
167
|
<template>
|
|
152
168
|
<div>
|
|
153
|
-
<form
|
|
169
|
+
<form
|
|
170
|
+
v-if="showPasswordForm"
|
|
171
|
+
@submit.prevent="handlePasswordLogin"
|
|
172
|
+
>
|
|
154
173
|
<!-- standard username/password form -->
|
|
155
174
|
</form>
|
|
156
175
|
|
|
157
|
-
<div
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
176
|
+
<div
|
|
177
|
+
v-for="provider in loginTypes.filter((t) => t.loginProvider !== 'password')"
|
|
178
|
+
:key="provider.loginProvider"
|
|
179
|
+
>
|
|
180
|
+
<VcButton @click="redirectToSSO(provider)"> Sign in with {{ provider.loginProvider }} </VcButton>
|
|
161
181
|
</div>
|
|
162
182
|
</div>
|
|
163
183
|
</template>
|
|
@@ -12,16 +12,16 @@ Registers Core Web Vitals observers (FCP, LCP, CLS, INP, TTFB) and reports metri
|
|
|
12
12
|
## Quick Start
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
|
-
import { useWebVitals } from
|
|
15
|
+
import { useWebVitals } from "@vc-shell/framework";
|
|
16
16
|
|
|
17
17
|
// Option 1: Default behavior -- logs to console.warn in dev mode, silent in production
|
|
18
18
|
useWebVitals();
|
|
19
19
|
|
|
20
20
|
// Option 2: Custom callback for production analytics
|
|
21
21
|
useWebVitals((metric) => {
|
|
22
|
-
analytics.track(
|
|
23
|
-
name: metric.name,
|
|
24
|
-
value: metric.value,
|
|
22
|
+
analytics.track("web-vital", {
|
|
23
|
+
name: metric.name, // "FCP", "LCP", "CLS", "INP", or "TTFB"
|
|
24
|
+
value: metric.value, // milliseconds (or unitless for CLS)
|
|
25
25
|
rating: metric.rating, // "good", "needs-improvement", or "poor"
|
|
26
26
|
});
|
|
27
27
|
});
|
|
@@ -32,7 +32,7 @@ Call this once after `app.mount()` in your application entry point:
|
|
|
32
32
|
```typescript
|
|
33
33
|
// main.ts
|
|
34
34
|
const app = createApp(App);
|
|
35
|
-
app.mount(
|
|
35
|
+
app.mount("#app");
|
|
36
36
|
|
|
37
37
|
// Register Web Vitals observers after mount
|
|
38
38
|
useWebVitals();
|
|
@@ -42,17 +42,17 @@ useWebVitals();
|
|
|
42
42
|
|
|
43
43
|
### Parameters
|
|
44
44
|
|
|
45
|
-
| Parameter
|
|
46
|
-
|
|
47
|
-
| `callback` | `(metric: IWebVitalsMetric) => void` | No
|
|
45
|
+
| Parameter | Type | Required | Description |
|
|
46
|
+
| ---------- | ------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------- |
|
|
47
|
+
| `callback` | `(metric: IWebVitalsMetric) => void` | No | Custom handler for each metric. Defaults to `console.warn` formatted output in dev mode; no-op in production. |
|
|
48
48
|
|
|
49
49
|
### IWebVitalsMetric
|
|
50
50
|
|
|
51
|
-
| Field
|
|
52
|
-
|
|
53
|
-
| `name`
|
|
54
|
-
| `value`
|
|
55
|
-
| `rating` | `"good" \| "needs-improvement" \| "poor"` | Performance rating based on Google's thresholds (e.g., LCP < 2500ms = "good")
|
|
51
|
+
| Field | Type | Description |
|
|
52
|
+
| -------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
+
| `name` | `string` | Metric name: `"FCP"` (First Contentful Paint), `"LCP"` (Largest Contentful Paint), `"CLS"` (Cumulative Layout Shift), `"INP"` (Interaction to Next Paint), `"TTFB"` (Time to First Byte) |
|
|
54
|
+
| `value` | `number` | Metric value in milliseconds for all metrics except CLS, which is a unitless score |
|
|
55
|
+
| `rating` | `"good" \| "needs-improvement" \| "poor"` | Performance rating based on Google's thresholds (e.g., LCP < 2500ms = "good") |
|
|
56
56
|
|
|
57
57
|
### Returns
|
|
58
58
|
|
|
@@ -75,7 +75,7 @@ The default callback formats CLS values to 4 decimal places (since CLS is a dime
|
|
|
75
75
|
## Recipe: Sending Web Vitals to Application Insights
|
|
76
76
|
|
|
77
77
|
```typescript
|
|
78
|
-
import { useWebVitals, useAppInsights } from
|
|
78
|
+
import { useWebVitals, useAppInsights } from "@vc-shell/framework";
|
|
79
79
|
|
|
80
80
|
const { appInsights } = useAppInsights();
|
|
81
81
|
|
|
@@ -96,14 +96,14 @@ This sends each Web Vital as a custom metric to Application Insights, where you
|
|
|
96
96
|
## Recipe: Conditional Reporting Based on Rating
|
|
97
97
|
|
|
98
98
|
```typescript
|
|
99
|
-
import { useWebVitals } from
|
|
99
|
+
import { useWebVitals } from "@vc-shell/framework";
|
|
100
100
|
|
|
101
101
|
useWebVitals((metric) => {
|
|
102
102
|
// Only report poor metrics to reduce noise
|
|
103
|
-
if (metric.rating ===
|
|
104
|
-
fetch(
|
|
105
|
-
method:
|
|
106
|
-
headers: {
|
|
103
|
+
if (metric.rating === "poor") {
|
|
104
|
+
fetch("/api/telemetry/web-vitals", {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: { "Content-Type": "application/json" },
|
|
107
107
|
body: JSON.stringify({
|
|
108
108
|
metric: metric.name,
|
|
109
109
|
value: metric.value,
|
|
@@ -14,15 +14,15 @@ Also exports `provideWidgetService()` for framework-level initialization and `re
|
|
|
14
14
|
## Quick Start
|
|
15
15
|
|
|
16
16
|
```typescript
|
|
17
|
-
import { useWidgets } from
|
|
17
|
+
import { useWidgets } from "@vc-shell/framework";
|
|
18
18
|
|
|
19
19
|
const widgetService = useWidgets();
|
|
20
20
|
|
|
21
21
|
// Query all widgets registered for a specific blade
|
|
22
|
-
const widgets = widgetService.getWidgets(
|
|
22
|
+
const widgets = widgetService.getWidgets("order-details");
|
|
23
23
|
|
|
24
24
|
// Check if a widget is currently active (visible and expanded)
|
|
25
|
-
if (widgetService.isActiveWidget(
|
|
25
|
+
if (widgetService.isActiveWidget("order-status-widget")) {
|
|
26
26
|
// widget is currently in focus
|
|
27
27
|
}
|
|
28
28
|
```
|
|
@@ -35,29 +35,29 @@ None.
|
|
|
35
35
|
|
|
36
36
|
### Returns (`IWidgetService`)
|
|
37
37
|
|
|
38
|
-
| Property / Method
|
|
39
|
-
|
|
40
|
-
| `registerWidget`
|
|
41
|
-
| `unregisterWidget`
|
|
42
|
-
| `getWidgets`
|
|
43
|
-
| `clearBladeWidgets`
|
|
44
|
-
| `registeredWidgets`
|
|
45
|
-
| `isActiveWidget`
|
|
46
|
-
| `setActiveWidget`
|
|
47
|
-
| `updateActiveWidget`
|
|
48
|
-
| `isWidgetRegistered`
|
|
49
|
-
| `updateWidget`
|
|
50
|
-
| `resolveWidgetProps`
|
|
51
|
-
| `getExternalWidgetsForBlade` | `(bladeId: string) => IExternalWidgetRegistration[]` | Gets external widgets that target a specific blade (registered by other modules).
|
|
52
|
-
| `getAllExternalWidgets`
|
|
38
|
+
| Property / Method | Type | Description |
|
|
39
|
+
| ---------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
40
|
+
| `registerWidget` | `(widget: IWidget, bladeId: string) => void` | Registers a widget for a specific blade. The widget will appear in that blade's widget area. |
|
|
41
|
+
| `unregisterWidget` | `(widgetId: string, bladeId: string) => void` | Removes a widget from a blade by ID. |
|
|
42
|
+
| `getWidgets` | `(bladeId: string) => IWidget[]` | Returns all widgets registered for a specific blade. |
|
|
43
|
+
| `clearBladeWidgets` | `(bladeId: string) => void` | Removes all widgets for a blade (used during blade teardown). |
|
|
44
|
+
| `registeredWidgets` | `IWidgetRegistration[]` | All widget registrations across all blades. |
|
|
45
|
+
| `isActiveWidget` | `(id: string) => boolean` | Checks if a widget is currently active (selected/expanded). |
|
|
46
|
+
| `setActiveWidget` | `(args) => void` | Sets a widget as active with its exposed instance. |
|
|
47
|
+
| `updateActiveWidget` | `() => void` | Triggers the active widget's update function. Deprecated -- use headless widgets instead. |
|
|
48
|
+
| `isWidgetRegistered` | `(id: string) => boolean` | Checks if a widget ID exists in any blade's registry. |
|
|
49
|
+
| `updateWidget` | `(args) => void` | Updates properties of a registered widget (trigger, badge, etc.). |
|
|
50
|
+
| `resolveWidgetProps` | `(widget, bladeData) => Record<string, unknown>` | Resolves widget props from blade data. Deprecated. |
|
|
51
|
+
| `getExternalWidgetsForBlade` | `(bladeId: string) => IExternalWidgetRegistration[]` | Gets external widgets that target a specific blade (registered by other modules). |
|
|
52
|
+
| `getAllExternalWidgets` | `() => IExternalWidgetRegistration[]` | Gets all registered external widgets across all modules. |
|
|
53
53
|
|
|
54
54
|
### Additional Exports
|
|
55
55
|
|
|
56
|
-
| Export
|
|
57
|
-
|
|
58
|
-
| `provideWidgetService()`
|
|
59
|
-
| `registerWidget(widget, bladeId)`
|
|
60
|
-
| `registerExternalWidget(registration)` | Global pre-registration for cross-module widgets. These widgets are rendered in blades owned by other modules.
|
|
56
|
+
| Export | Description |
|
|
57
|
+
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
58
|
+
| `provideWidgetService()` | Creates and provides the widget service via Vue injection. Idempotent -- returns existing service if already provided. Cleans up via `onScopeDispose`. |
|
|
59
|
+
| `registerWidget(widget, bladeId)` | Global pre-registration. Widgets registered this way are picked up when `provideWidgetService()` creates the service. Use in module `install()`. |
|
|
60
|
+
| `registerExternalWidget(registration)` | Global pre-registration for cross-module widgets. These widgets are rendered in blades owned by other modules. |
|
|
61
61
|
|
|
62
62
|
## How It Works
|
|
63
63
|
|
|
@@ -75,20 +75,20 @@ The service tracks active widgets (which widget is currently expanded/focused) s
|
|
|
75
75
|
|
|
76
76
|
```typescript
|
|
77
77
|
// my-module/index.ts
|
|
78
|
-
import { registerWidget } from
|
|
79
|
-
import { markRaw } from
|
|
80
|
-
import OrderStatusWidget from
|
|
78
|
+
import { registerWidget } from "@vc-shell/framework";
|
|
79
|
+
import { markRaw } from "vue";
|
|
80
|
+
import OrderStatusWidget from "./widgets/OrderStatusWidget.vue";
|
|
81
81
|
|
|
82
82
|
export default {
|
|
83
83
|
install() {
|
|
84
84
|
registerWidget(
|
|
85
85
|
{
|
|
86
|
-
id:
|
|
87
|
-
title:
|
|
86
|
+
id: "order-status",
|
|
87
|
+
title: "Order Status",
|
|
88
88
|
component: markRaw(OrderStatusWidget),
|
|
89
|
-
icon:
|
|
89
|
+
icon: "fas fa-clipboard-check",
|
|
90
90
|
},
|
|
91
|
-
|
|
91
|
+
"order-details", // blade ID where this widget appears
|
|
92
92
|
);
|
|
93
93
|
},
|
|
94
94
|
};
|
|
@@ -100,18 +100,18 @@ A shipping module can add a widget to the order module's detail blade:
|
|
|
100
100
|
|
|
101
101
|
```typescript
|
|
102
102
|
// shipping-module/index.ts
|
|
103
|
-
import { registerExternalWidget } from
|
|
104
|
-
import { markRaw } from
|
|
105
|
-
import ShippingTracker from
|
|
103
|
+
import { registerExternalWidget } from "@vc-shell/framework";
|
|
104
|
+
import { markRaw } from "vue";
|
|
105
|
+
import ShippingTracker from "./widgets/ShippingTracker.vue";
|
|
106
106
|
|
|
107
107
|
export default {
|
|
108
108
|
install() {
|
|
109
109
|
registerExternalWidget({
|
|
110
|
-
widgetId:
|
|
111
|
-
bladeId:
|
|
110
|
+
widgetId: "shipping-tracker",
|
|
111
|
+
bladeId: "order-details", // target blade from another module
|
|
112
112
|
component: markRaw(ShippingTracker),
|
|
113
|
-
title:
|
|
114
|
-
icon:
|
|
113
|
+
title: "Shipping",
|
|
114
|
+
icon: "fas fa-truck",
|
|
115
115
|
});
|
|
116
116
|
},
|
|
117
117
|
};
|
|
@@ -121,21 +121,16 @@ export default {
|
|
|
121
121
|
|
|
122
122
|
```vue
|
|
123
123
|
<script setup lang="ts">
|
|
124
|
-
import { useWidgets } from
|
|
125
|
-
import { computed } from
|
|
124
|
+
import { useWidgets } from "@vc-shell/framework";
|
|
125
|
+
import { computed } from "vue";
|
|
126
126
|
|
|
127
127
|
const props = defineProps<{ bladeId: string }>();
|
|
128
128
|
const widgetService = useWidgets();
|
|
129
129
|
|
|
130
130
|
const bladeWidgets = computed(() => widgetService.getWidgets(props.bladeId));
|
|
131
|
-
const externalWidgets = computed(() =>
|
|
132
|
-
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
const allWidgets = computed(() => [
|
|
136
|
-
...bladeWidgets.value,
|
|
137
|
-
...externalWidgets.value,
|
|
138
|
-
]);
|
|
131
|
+
const externalWidgets = computed(() => widgetService.getExternalWidgetsForBlade(props.bladeId));
|
|
132
|
+
|
|
133
|
+
const allWidgets = computed(() => [...bladeWidgets.value, ...externalWidgets.value]);
|
|
139
134
|
</script>
|
|
140
135
|
```
|
|
141
136
|
|