@vc-shell/vc-app-skill 2.0.0-alpha.31 → 2.0.0-alpha.33

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 (41) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +1 -1
  3. package/runtime/VERSION +1 -1
  4. package/runtime/agents/migration-agent.md +83 -0
  5. package/runtime/knowledge/docs/_BUILD_HASH.md +1 -1
  6. package/runtime/knowledge/docs/core/composables/bladeContext/index.docs.md +111 -0
  7. package/runtime/knowledge/docs/core/composables/useBladeForm/useBladeForm.docs.md +167 -0
  8. package/runtime/knowledge/docs/core/composables/useBladeWidgets/index.docs.md +305 -0
  9. package/runtime/knowledge/docs/core/composables/useMenuExpanded/index.docs.md +83 -0
  10. package/runtime/knowledge/docs/core/utilities/thumbnail/thumbnail.docs.md +116 -0
  11. package/runtime/knowledge/docs/shell/components/change-password-button/change-password-button.docs.md +1 -1
  12. package/runtime/knowledge/docs/ui/components/atoms/vc-card/vc-card.docs.md +4 -0
  13. package/runtime/knowledge/docs/ui/components/molecules/vc-accordion/vc-accordion.docs.md +4 -0
  14. package/runtime/knowledge/docs/ui/components/molecules/vc-checkbox/vc-checkbox.docs.md +5 -0
  15. package/runtime/knowledge/docs/ui/components/molecules/vc-checkbox-group/vc-checkbox-group.docs.md +5 -0
  16. package/runtime/knowledge/docs/ui/components/molecules/vc-color-input/vc-color-input.docs.md +5 -0
  17. package/runtime/knowledge/docs/ui/components/molecules/vc-date-picker/vc-date-picker.docs.md +7 -0
  18. package/runtime/knowledge/docs/ui/components/molecules/vc-editor/vc-editor.docs.md +5 -0
  19. package/runtime/knowledge/docs/ui/components/molecules/vc-field/vc-field.docs.md +5 -0
  20. package/runtime/knowledge/docs/ui/components/molecules/vc-file-upload/vc-file-upload.docs.md +5 -0
  21. package/runtime/knowledge/docs/ui/components/molecules/vc-input/vc-input.docs.md +7 -0
  22. package/runtime/knowledge/docs/ui/components/molecules/vc-input-currency/vc-input-currency.docs.md +7 -0
  23. package/runtime/knowledge/docs/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.docs.md +7 -0
  24. package/runtime/knowledge/docs/ui/components/molecules/vc-multivalue/vc-multivalue.docs.md +7 -0
  25. package/runtime/knowledge/docs/ui/components/molecules/vc-radio-button/vc-radio-button.docs.md +5 -0
  26. package/runtime/knowledge/docs/ui/components/molecules/vc-radio-group/vc-radio-group.docs.md +5 -0
  27. package/runtime/knowledge/docs/ui/components/molecules/vc-rating/vc-rating.docs.md +5 -0
  28. package/runtime/knowledge/docs/ui/components/molecules/vc-select/vc-select.docs.md +7 -0
  29. package/runtime/knowledge/docs/ui/components/molecules/vc-slider/vc-slider.docs.md +5 -0
  30. package/runtime/knowledge/docs/ui/components/molecules/vc-switch/vc-switch.docs.md +5 -0
  31. package/runtime/knowledge/docs/ui/components/molecules/vc-textarea/vc-textarea.docs.md +7 -0
  32. package/runtime/knowledge/docs/ui/components/organisms/vc-blade/vc-blade.docs.md +30 -0
  33. package/runtime/knowledge/docs/ui/components/organisms/vc-data-table/vc-data-table.docs.md +28 -0
  34. package/runtime/knowledge/migration-prompts/blade-form-migration.md +246 -0
  35. package/runtime/knowledge/migration-prompts/blade-props-migration.md +195 -0
  36. package/runtime/knowledge/migration-prompts/notifications-migration.md +218 -0
  37. package/runtime/knowledge/migration-prompts/nswag-migration.md +248 -0
  38. package/runtime/knowledge/migration-prompts/widgets-migration.md +157 -0
  39. package/runtime/vc-app.md +126 -0
  40. package/runtime/knowledge/docs/core/constants/constants.docs.md +0 -185
  41. /package/runtime/knowledge/docs/shell/{pages → auth}/ChangePasswordPage/change-password-page.docs.md +0 -0
@@ -0,0 +1,218 @@
1
+ ---
2
+ name: notifications-migration
3
+ description: AI transformation rules for notification template migration to new defineAppModule config.
4
+ ---
5
+
6
+ # Notifications Migration: useNotifications → useBladeNotifications
7
+
8
+ Migrate from `createAppModule(pages, locales, notificationTemplates)` to `defineAppModule({ notifications })`, and from `useNotifications()` to `useBladeNotifications()`.
9
+
10
+ ## RULE 1: Move Notification Directory
11
+
12
+ Move `components/notifications/` to `notifications/` at the module root. Remove the barrel `index.ts` that re-exports all templates.
13
+
14
+ **BEFORE:**
15
+
16
+ ```
17
+ my-module/
18
+ components/
19
+ notifications/
20
+ index.ts ← barrel: export { default as MyEvent } from "./MyEvent.vue"
21
+ MyEvent.vue
22
+ pages/
23
+ index.ts
24
+ ```
25
+
26
+ **AFTER:**
27
+
28
+ ```
29
+ my-module/
30
+ notifications/
31
+ MyEvent.vue ← no barrel index.ts needed
32
+ pages/
33
+ index.ts
34
+ ```
35
+
36
+ ## RULE 2: Update Notification Template
37
+
38
+ Remove `defineProps`, remove `defineOptions({ inheritAttrs: false })`, and use `useNotificationContext()` composable instead of props.
39
+
40
+ **BEFORE:**
41
+
42
+ ```vue
43
+ <template>
44
+ <NotificationTemplate
45
+ :color="color"
46
+ :title="notification.title"
47
+ :icon="icon"
48
+ :notification="notification"
49
+ @click="onClick"
50
+ >
51
+ <VcHint>{{ notification.description }}</VcHint>
52
+ </NotificationTemplate>
53
+ </template>
54
+
55
+ <script lang="ts" setup>
56
+ import { PushNotification, NotificationTemplate } from "@vc-shell/framework";
57
+
58
+ export interface Props {
59
+ notification: PushNotification;
60
+ }
61
+
62
+ defineProps<Props>();
63
+ defineOptions({ inheritAttrs: false });
64
+
65
+ const color = computed(() => "var(--primary-500)");
66
+ const icon = "lucide-bell";
67
+
68
+ function onClick() {
69
+ // handle click
70
+ }
71
+ </script>
72
+ ```
73
+
74
+ **AFTER:**
75
+
76
+ ```vue
77
+ <template>
78
+ <NotificationTemplate
79
+ :color="color"
80
+ :title="notification.title"
81
+ :icon="icon"
82
+ :notification="notification"
83
+ @click="onClick"
84
+ >
85
+ <VcHint>{{ notification.description }}</VcHint>
86
+ </NotificationTemplate>
87
+ </template>
88
+
89
+ <script lang="ts" setup>
90
+ import { PushNotification, useBlade, NotificationTemplate, useNotificationContext } from "@vc-shell/framework";
91
+ import { computed } from "vue";
92
+
93
+ interface IMyNotification extends PushNotification {
94
+ customField?: string;
95
+ }
96
+
97
+ const notificationRef = useNotificationContext<IMyNotification>();
98
+ const notification = computed(() => notificationRef.value);
99
+
100
+ const color = computed(() => "var(--primary-500)");
101
+ const icon = "lucide-bell";
102
+
103
+ function onClick() {
104
+ // handle click
105
+ }
106
+ </script>
107
+ ```
108
+
109
+ ## RULE 3: Update Module index.ts
110
+
111
+ Replace `createAppModule(pages, locales, notificationTemplates)` with `defineAppModule({ notifications })`. Import each notification template individually instead of using a barrel `import *`.
112
+
113
+ **BEFORE:**
114
+
115
+ ```typescript
116
+ import * as pages from "./pages";
117
+ import * as locales from "./locales";
118
+ import * as notificationTemplates from "./components/notifications";
119
+
120
+ export default createAppModule(pages, locales, notificationTemplates);
121
+ ```
122
+
123
+ **AFTER:**
124
+
125
+ ```typescript
126
+ import * as pages from "./pages";
127
+ import * as locales from "./locales";
128
+ import MyCreatedEvent from "./notifications/MyCreatedEvent.vue";
129
+ import MyDeletedEvent from "./notifications/MyDeletedEvent.vue";
130
+ import { defineAppModule } from "@vc-shell/framework";
131
+
132
+ export default defineAppModule({
133
+ blades: pages,
134
+ locales,
135
+ notifications: {
136
+ MyCreatedDomainEvent: {
137
+ template: MyCreatedEvent,
138
+ toast: { mode: "auto" },
139
+ },
140
+ MyDeletedDomainEvent: {
141
+ template: MyDeletedEvent,
142
+ toast: { mode: "auto", severity: "warning" },
143
+ },
144
+ },
145
+ });
146
+ ```
147
+
148
+ Toast options:
149
+ - `mode`: `"auto"` (show and auto-dismiss), `"progress"` (show with progress bar), `"silent"` (no toast)
150
+ - `severity`: `"info"` (default), `"warning"`, `"error"`, `"critical"`
151
+
152
+ ## RULE 4: Replace useNotifications in Blades
153
+
154
+ Replace `useNotifications()` with `useBladeNotifications()`. Remove `setNotificationHandler`, `moduleNotifications` watch, and `notifyType` from `defineOptions`.
155
+
156
+ **BEFORE:**
157
+
158
+ ```typescript
159
+ defineOptions({
160
+ name: "MyBlade",
161
+ notifyType: "MyDomainEvent",
162
+ });
163
+
164
+ const { markAsRead, setNotificationHandler, moduleNotifications } = useNotifications("MyDomainEvent");
165
+
166
+ setNotificationHandler((message) => {
167
+ if (message.title) {
168
+ notification.success(message.title, {
169
+ onClose() {
170
+ markAsRead(message);
171
+ },
172
+ });
173
+ }
174
+ });
175
+
176
+ // or watch pattern:
177
+ watch(moduleNotifications, (newVal) => {
178
+ // manual toast management
179
+ }, { deep: true });
180
+ ```
181
+
182
+ **AFTER:**
183
+
184
+ ```typescript
185
+ defineOptions({
186
+ name: "MyBlade",
187
+ // notifyType removed
188
+ });
189
+
190
+ const { messages } = useBladeNotifications({
191
+ types: ["MyDomainEvent"],
192
+ onMessage: () => reloadData(),
193
+ });
194
+ ```
195
+
196
+ For progress notifications:
197
+
198
+ ```typescript
199
+ // Module config: notifications: { MyProgressEvent: { toast: { mode: "progress" } } }
200
+ useBladeNotifications({
201
+ types: ["MyProgressEvent"],
202
+ filter: (msg) => msg.entityId === param.value,
203
+ onMessage: (msg) => refreshData(msg),
204
+ });
205
+ ```
206
+
207
+ ## Verification
208
+
209
+ After migration:
210
+
211
+ 1. Run `npx tsc --noEmit` to verify no TypeScript errors
212
+ 2. Confirm notification templates render correctly in the notification panel
213
+ 3. Confirm toast notifications appear for configured event types
214
+ 4. Confirm `useBladeNotifications` triggers `onMessage` callback when events arrive
215
+ 5. Confirm `notifyType` is removed from all `defineOptions` calls
216
+ 6. Confirm `components/notifications/` directory is removed and `notifications/` exists at module root
217
+ 7. Confirm no remaining imports of `useNotifications`, `setNotificationHandler`, or `moduleNotifications`
218
+ 8. Confirm no remaining `defineProps` or `defineOptions({ inheritAttrs: false })` in notification templates
@@ -0,0 +1,248 @@
1
+ ---
2
+ name: nswag-migration
3
+ description: AI transformation rules for NSwag class→interface DTO migration.
4
+ ---
5
+
6
+ # NSwag API Client Migration: Class to Interface
7
+
8
+ You are tasked with fixing TypeScript errors caused by migrating NSwag-generated API clients from class-based to interface-based data models.
9
+
10
+ **Context:**
11
+ - API Client CLASSES (e.g., `*Client` classes) remain unchanged — they still have constructors
12
+ - DATA MODEL types (DTOs, Commands, Queries, etc.) are now INTERFACES instead of classes
13
+ - Interfaces cannot be instantiated with `new` — use object literals with type assertions instead
14
+
15
+ ## RULE 1: Import Name Changes (Remove "I" Prefix)
16
+
17
+ **Error:** `error TS2724: has no exported member named 'IXxx'. Did you mean 'Xxx'?`
18
+
19
+ **Fix:** Remove the "I" prefix from all data model interface imports.
20
+
21
+ ```typescript
22
+ // BEFORE
23
+ import { IOffer, ISeller, ISearchQuery } from "@your-api-package";
24
+
25
+ // AFTER
26
+ import { Offer, Seller, SearchQuery } from "@your-api-package";
27
+ ```
28
+
29
+ Also update all type annotations in the file:
30
+
31
+ ```typescript
32
+ // BEFORE
33
+ const item = ref<IOffer>(...);
34
+ function process(data: IOffer): IOffer { ... }
35
+
36
+ // AFTER
37
+ const item = ref<Offer>(...);
38
+ function process(data: Offer): Offer { ... }
39
+ ```
40
+
41
+ ## RULE 2: Class Instantiation → Object Literals
42
+
43
+ **Error:** `error TS2693: 'Xxx' only refers to a type, but is being used as a value here`
44
+
45
+ **Fix:** Replace `new TypeName({...})` with `{...} as TypeName` or typed variable declaration.
46
+
47
+ **Pattern A — Command/Query objects:**
48
+
49
+ ```typescript
50
+ // BEFORE
51
+ const command = new CreateCommand({
52
+ id: "123",
53
+ name: "Test",
54
+ details: new Details({ value: 100 }),
55
+ });
56
+
57
+ // AFTER
58
+ const command: CreateCommand = {
59
+ id: "123",
60
+ name: "Test",
61
+ details: { value: 100 } as Details,
62
+ };
63
+ ```
64
+
65
+ **Pattern B — Search/Query criteria:**
66
+
67
+ ```typescript
68
+ // BEFORE
69
+ const criteria = new SearchQuery({
70
+ take: 20,
71
+ skip: 0,
72
+ sort: "name:asc",
73
+ });
74
+
75
+ // AFTER
76
+ const criteria: SearchQuery = {
77
+ take: 20,
78
+ skip: 0,
79
+ sort: "name:asc",
80
+ };
81
+ ```
82
+
83
+ **Pattern C — Empty/default objects:**
84
+
85
+ ```typescript
86
+ // BEFORE
87
+ const item = ref<IEntity>(new Entity());
88
+
89
+ // AFTER
90
+ const item = ref<Entity>({} as Entity);
91
+ ```
92
+
93
+ **Pattern D — Nested objects:**
94
+
95
+ ```typescript
96
+ // BEFORE
97
+ const data = ref(new Parent({ child: new Child() }));
98
+
99
+ // AFTER
100
+ const data = ref<Parent>({
101
+ child: {} as Child,
102
+ } as Parent);
103
+ ```
104
+
105
+ **Pattern E — With Vue reactive():**
106
+
107
+ ```typescript
108
+ // BEFORE
109
+ const item = ref<Order>(reactive(new Order()));
110
+
111
+ // AFTER
112
+ const item = ref<Order>(reactive({} as Order));
113
+ ```
114
+
115
+ ## RULE 3: Factory Functions for Framework Callbacks
116
+
117
+ **Error:** `error TS2693: 'PropertyValue' only refers to a type, but is being used as a value here`
118
+
119
+ When a framework function expects a constructor function (callable with `new`), create factory functions:
120
+
121
+ ```typescript
122
+ // BEFORE — Framework expects constructor
123
+ const { loadItems } = useFrameworkHook<Type1, Type2>(
124
+ fetchFunction,
125
+ ItemClass,
126
+ OtherClass,
127
+ );
128
+
129
+ // AFTER — Create factory functions
130
+ const createItem = (data?: Partial<ItemType>): ItemType =>
131
+ ({ ...data } as ItemType);
132
+
133
+ const createOther = (data?: Partial<OtherType>): OtherType =>
134
+ ({ ...data } as OtherType);
135
+
136
+ const { loadItems } = useFrameworkHook<Type1, Type2>(
137
+ fetchFunction,
138
+ createItem,
139
+ createOther,
140
+ );
141
+ ```
142
+
143
+ ## RULE 4: Image Import Conflict with Global DOM Type
144
+
145
+ **Error:** `error TS2866: Import 'Image' conflicts with global value used in this file`
146
+
147
+ **Fix:** Use type-only import or rename:
148
+
149
+ ```typescript
150
+ // BEFORE
151
+ import { Image, Entity } from "@your-api-package";
152
+
153
+ // AFTER — Option 1: Type-only import
154
+ import { type Image, Entity } from "@your-api-package";
155
+
156
+ // AFTER — Option 2: Rename import
157
+ import { Image as ApiImage, Entity } from "@your-api-package";
158
+ ```
159
+
160
+ ## RULE 5: Implicit Any Types
161
+
162
+ **Error:** `error TS7006: Parameter 'x' implicitly has an 'any' type`
163
+
164
+ **Fix:** Add explicit type annotations to callback parameters:
165
+
166
+ ```typescript
167
+ // BEFORE
168
+ items.map((x) => x.id)
169
+ items.filter((item) => item.active)
170
+ data.forEach((d) => process(d))
171
+
172
+ // AFTER
173
+ items.map((x: ItemType) => x.id)
174
+ items.filter((item: ItemType) => item.active)
175
+ data.forEach((d: DataType) => process(d))
176
+ ```
177
+
178
+ ## RULE 6: Type Mismatch in Function Arguments
179
+
180
+ **Error:** `error TS2345: Argument of type 'X' is not assignable to parameter of type 'Y'`
181
+
182
+ Check if the function signature expects a different type (often an ID instead of a full object):
183
+
184
+ ```typescript
185
+ // BEFORE
186
+ someFunction(asset) // asset is full object
187
+
188
+ // AFTER
189
+ someFunction(asset.id) // pass the id property
190
+ ```
191
+
192
+ ## Important Notes
193
+
194
+ 1. **API Client Classes are NOT affected** — Do NOT modify client classes like `*Client`. They remain classes with constructors:
195
+ ```typescript
196
+ const { getApiClient } = useApiClient(MyApiClient); // No change needed
197
+ ```
198
+
199
+ 2. **Spread operators work with interfaces:**
200
+ ```typescript
201
+ const updated = { ...existingItem, name: "New Name" }; // Works fine
202
+ ```
203
+
204
+ 3. **Required properties must be provided** when using type assertions:
205
+ ```typescript
206
+ const details: ItemDetails = {
207
+ sku: item.sku || "",
208
+ ...otherProps,
209
+ };
210
+ ```
211
+
212
+ 4. **Generic type parameters need updating too:**
213
+ ```typescript
214
+ // BEFORE
215
+ const { action } = useAsync<ISearchQuery>(...)
216
+
217
+ // AFTER
218
+ const { action } = useAsync<SearchQuery>(...)
219
+ ```
220
+
221
+ 5. **Interfaces with all optional properties** can use empty object:
222
+ ```typescript
223
+ const query: SearchQuery = {}; // OK if all properties are optional
224
+ ```
225
+
226
+ 6. **Interfaces with required properties** need those properties:
227
+ ```typescript
228
+ const item: Item = { id: "default" }; // Must provide 'id'
229
+ ```
230
+
231
+ ## Quick Reference Table
232
+
233
+ | Error Code | Pattern | Fix |
234
+ |------------|---------|-----|
235
+ | TS2724 | `IXxx` not found | Remove "I" prefix from import |
236
+ | TS2693 | Type used as value | Replace `new Type({})` with `{} as Type` |
237
+ | TS2866 | Import conflicts with global | Use `type` import or rename |
238
+ | TS7006 | Implicit any | Add explicit type annotation |
239
+ | TS2345 | Type not assignable | Check if property (like `.id`) should be passed |
240
+
241
+ ## Verification
242
+
243
+ After making changes:
244
+
245
+ 1. Run TypeScript compiler: `npx tsc --noEmit` or `npx vue-tsc --noEmit`
246
+ 2. Verify no new errors were introduced
247
+ 3. Build the project to ensure everything compiles
248
+ 4. Test affected functionality if possible
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: widgets-migration
3
+ description: AI transformation rules for useWidgets→useBladeWidgets migration.
4
+ ---
5
+
6
+ # Widget Migration: useWidgets → useBladeWidgets
7
+
8
+ Migrate blade widgets from the imperative `useWidgets()` + `registerWidget()` API to the declarative `useBladeWidgets()` composable. The new API eliminates manual registration/cleanup and removes the need for per-widget `.vue` components when they only render a standard sidebar item.
9
+
10
+ ## RULE 1: Create Widget Composable
11
+
12
+ For each blade that calls `registerWidget()`, create a `widgets/useXxxWidgets.ts` composable.
13
+
14
+ **BEFORE:**
15
+
16
+ ```typescript
17
+ // In XxxDetails.vue <script setup>
18
+ import { useWidgets, useBlade } from "@vc-shell/framework";
19
+ import type { BladeInstance } from "@vc-shell/framework";
20
+ import { onMounted, onUnmounted } from "vue";
21
+ import OffersWidget from "../widgets/OffersWidget.vue";
22
+
23
+ const { registerWidget, clearBladeWidgets } = useWidgets();
24
+ const { openBlade } = useBlade();
25
+
26
+ onMounted(() => {
27
+ registerWidget(
28
+ {
29
+ id: "OffersWidget",
30
+ component: OffersWidget,
31
+ props: {
32
+ count: offersCount,
33
+ onClick: () => openBlade({ name: "OffersList", options: { productId: entity.value.id } }),
34
+ },
35
+ },
36
+ bladeContext.id,
37
+ );
38
+ });
39
+
40
+ onUnmounted(() => {
41
+ clearBladeWidgets(bladeContext.id);
42
+ });
43
+ ```
44
+
45
+ **AFTER:**
46
+
47
+ ```typescript
48
+ // widgets/useProductWidgets.ts
49
+ import { useBladeWidgets, useBlade } from "@vc-shell/framework";
50
+ import type { UseBladeWidgetsReturn } from "@vc-shell/framework";
51
+ import { computed, type Ref } from "vue";
52
+
53
+ interface UseProductWidgetsOptions {
54
+ item: Ref<Product | undefined>;
55
+ isVisible: Ref<boolean> | boolean;
56
+ offersCount: Ref<number>;
57
+ }
58
+
59
+ export function useProductWidgets(options: UseProductWidgetsOptions): UseBladeWidgetsReturn {
60
+ const { item, isVisible, offersCount } = options;
61
+ const { openBlade } = useBlade();
62
+
63
+ return useBladeWidgets([
64
+ {
65
+ id: "OffersWidget",
66
+ icon: "lucide-tag",
67
+ title: "PRODUCTS.WIDGETS.OFFERS.TITLE",
68
+ badge: computed(() => offersCount.value),
69
+ isVisible,
70
+ onClick: () =>
71
+ openBlade({
72
+ name: "OffersList",
73
+ options: { productId: item.value?.id },
74
+ }),
75
+ onRefresh: async () => {
76
+ // load offers count logic here
77
+ },
78
+ },
79
+ ]);
80
+ }
81
+ ```
82
+
83
+ ## RULE 2: Replace Usage in Blade Page
84
+
85
+ Import the new composable and destructure `{ refreshAll }`.
86
+
87
+ **BEFORE:**
88
+
89
+ ```typescript
90
+ // XxxDetails.vue <script setup>
91
+ import { useWidgets } from "@vc-shell/framework";
92
+ import OffersWidget from "../widgets/OffersWidget.vue";
93
+ import AssociationsWidget from "../widgets/AssociationsWidget.vue";
94
+
95
+ const { registerWidget, clearBladeWidgets } = useWidgets();
96
+
97
+ onMounted(() => {
98
+ registerWidget({ id: "OffersWidget", component: OffersWidget, props: { ... } }, bladeId);
99
+ registerWidget({ id: "AssociationsWidget", component: AssociationsWidget, props: { ... } }, bladeId);
100
+ });
101
+
102
+ onUnmounted(() => {
103
+ clearBladeWidgets(bladeId);
104
+ });
105
+ ```
106
+
107
+ **AFTER:**
108
+
109
+ ```typescript
110
+ // XxxDetails.vue <script setup>
111
+ import { useProductWidgets } from "../widgets/useProductWidgets";
112
+
113
+ const isExisting = computed(() => !!param.value);
114
+
115
+ const { refreshAll } = useProductWidgets({
116
+ item: entity,
117
+ isVisible: isExisting,
118
+ offersCount,
119
+ });
120
+ ```
121
+
122
+ ## RULE 3: Remove Widget .vue Components
123
+
124
+ Delete widget `.vue` files that only render a standard sidebar item (icon + title + badge + click handler). These are replaced by the declarative config in `useBladeWidgets()`.
125
+
126
+ **Keep** the `.vue` component only if it has custom rendering beyond the standard widget layout.
127
+
128
+ **Delete:**
129
+ - `widgets/OffersWidget.vue` (if it only shows icon, title, badge, click)
130
+ - `widgets/AssociationsWidget.vue` (same)
131
+
132
+ **Keep:**
133
+ - `widgets/CustomChartWidget.vue` (has custom chart rendering)
134
+
135
+ ## RULE 4: Remove Old Imports
136
+
137
+ Remove these imports that are no longer needed:
138
+
139
+ ```typescript
140
+ // REMOVE all of these:
141
+ import { useWidgets } from "@vc-shell/framework";
142
+ import type { BladeInstance } from "@vc-shell/framework"; // if only used for widgets
143
+ import { registerWidget, unregisterWidget, clearBladeWidgets } from "...";
144
+ import { onUnmounted } from "vue"; // if only used for widget cleanup
145
+ import OffersWidget from "../widgets/OffersWidget.vue"; // deleted component
146
+ ```
147
+
148
+ ## Verification
149
+
150
+ After migration:
151
+
152
+ 1. Run `npx tsc --noEmit` to verify no TypeScript errors
153
+ 2. Confirm widgets appear in the blade sidebar
154
+ 3. Confirm badge counts update reactively
155
+ 4. Confirm clicking a widget opens the correct child blade
156
+ 5. Confirm widgets are hidden when `isVisible` is false (e.g., on "create new" blades)
157
+ 6. Confirm no console errors about widget registration/cleanup on blade close