@vc-shell/vc-app-skill 2.0.0-alpha.32 → 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.
- package/CHANGELOG.md +15 -0
- package/package.json +1 -1
- package/runtime/VERSION +1 -1
- package/runtime/agents/migration-agent.md +83 -0
- package/runtime/knowledge/docs/_BUILD_HASH.md +1 -1
- package/runtime/knowledge/docs/core/composables/useBladeForm/useBladeForm.docs.md +55 -1
- package/runtime/knowledge/docs/shell/auth/ChangePasswordPage/change-password-page.docs.md +102 -0
- package/runtime/knowledge/docs/ui/components/atoms/vc-card/vc-card.docs.md +4 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-accordion/vc-accordion.docs.md +4 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-checkbox/vc-checkbox.docs.md +5 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-checkbox-group/vc-checkbox-group.docs.md +5 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-color-input/vc-color-input.docs.md +5 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-date-picker/vc-date-picker.docs.md +7 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-editor/vc-editor.docs.md +5 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-field/vc-field.docs.md +5 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-file-upload/vc-file-upload.docs.md +5 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-input/vc-input.docs.md +7 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-input-currency/vc-input-currency.docs.md +7 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.docs.md +7 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-multivalue/vc-multivalue.docs.md +7 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-radio-button/vc-radio-button.docs.md +5 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-radio-group/vc-radio-group.docs.md +5 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-rating/vc-rating.docs.md +5 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-select/vc-select.docs.md +7 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-slider/vc-slider.docs.md +5 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-switch/vc-switch.docs.md +5 -0
- package/runtime/knowledge/docs/ui/components/molecules/vc-textarea/vc-textarea.docs.md +7 -0
- package/runtime/knowledge/docs/ui/components/organisms/vc-blade/vc-blade.docs.md +30 -0
- package/runtime/knowledge/docs/ui/components/organisms/vc-data-table/vc-data-table.docs.md +28 -0
- package/runtime/knowledge/migration-prompts/blade-form-migration.md +246 -0
- package/runtime/knowledge/migration-prompts/blade-props-migration.md +195 -0
- package/runtime/knowledge/migration-prompts/notifications-migration.md +218 -0
- package/runtime/knowledge/migration-prompts/nswag-migration.md +248 -0
- package/runtime/knowledge/migration-prompts/widgets-migration.md +157 -0
- package/runtime/vc-app.md +126 -0
- package/runtime/knowledge/docs/core/constants/constants.docs.md +0 -185
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blade-form-migration
|
|
3
|
+
description: AI transformation rules for useForm + onBeforeClose → useBladeForm.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Blade Form Migration: useForm → useBladeForm
|
|
7
|
+
|
|
8
|
+
Replace manual wiring of `useForm()` (vee-validate) + `useModificationTracker()` + `useBeforeUnload()` + `onBeforeClose()` with the unified `useBladeForm()` composable.
|
|
9
|
+
|
|
10
|
+
## RULE 1: Replace useForm + onBeforeClose with useBladeForm
|
|
11
|
+
|
|
12
|
+
**BEFORE:**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { useForm } from "vee-validate";
|
|
16
|
+
import { useBeforeUnload, useModificationTracker, useBlade, usePopup } from "@vc-shell/framework";
|
|
17
|
+
|
|
18
|
+
const { item, loading, load, save } = useMyDetails();
|
|
19
|
+
const { onBeforeClose } = useBlade();
|
|
20
|
+
const { showConfirmation } = usePopup();
|
|
21
|
+
|
|
22
|
+
const { errorBag, setFieldError, meta: formMeta } = useForm({
|
|
23
|
+
initialValues: item,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const { currentValue, resetModificationState, isModified } = useModificationTracker(item);
|
|
27
|
+
|
|
28
|
+
useBeforeUnload(computed(() => isModified.value));
|
|
29
|
+
|
|
30
|
+
onBeforeClose(async () => {
|
|
31
|
+
if (isModified.value) {
|
|
32
|
+
return showConfirmation(t("MY_MODULE.ALERTS.CLOSE_CONFIRMATION"));
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const canSave = computed(
|
|
38
|
+
() => isModified.value && formMeta.value.valid && !loading.value,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
async function handleSave() {
|
|
42
|
+
await save(item.value);
|
|
43
|
+
resetModificationState();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function handleCancel() {
|
|
47
|
+
item.value = JSON.parse(JSON.stringify(currentValue.value));
|
|
48
|
+
resetModificationState();
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**AFTER:**
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { useBladeForm } from "@vc-shell/framework";
|
|
56
|
+
|
|
57
|
+
const { item, loading, load, save } = useMyDetails();
|
|
58
|
+
|
|
59
|
+
const { canSave, isModified, setBaseline, revert, setFieldError, errorBag } = useBladeForm({
|
|
60
|
+
data: item,
|
|
61
|
+
closeConfirmMessage: () => t("MY_MODULE.ALERTS.CLOSE_CONFIRMATION"),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
watch(item, () => {
|
|
65
|
+
if (item.value) setBaseline();
|
|
66
|
+
}, { once: true });
|
|
67
|
+
|
|
68
|
+
async function handleSave() {
|
|
69
|
+
await save(item.value);
|
|
70
|
+
setBaseline();
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## RULE 2: Remove onBeforeClose Entirely
|
|
75
|
+
|
|
76
|
+
`useBladeForm` handles the unsaved-changes guard automatically via `autoBeforeClose` (defaults to `true`).
|
|
77
|
+
|
|
78
|
+
**BEFORE:**
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const { onBeforeClose } = useBlade();
|
|
82
|
+
|
|
83
|
+
onBeforeClose(async () => {
|
|
84
|
+
if (formMeta.value.dirty) {
|
|
85
|
+
return confirm("You have unsaved changes. Discard?");
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**AFTER:**
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// Remove entirely. useBladeForm handles this.
|
|
95
|
+
// If you need a custom message, pass closeConfirmMessage option:
|
|
96
|
+
const form = useBladeForm({
|
|
97
|
+
data: item,
|
|
98
|
+
closeConfirmMessage: () => t("MY_MODULE.ALERTS.CLOSE_CONFIRMATION"),
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## RULE 3: Update Toolbar Disabled Logic
|
|
103
|
+
|
|
104
|
+
**BEFORE:**
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
const canSave = computed(
|
|
108
|
+
() => !meta.value.valid || !isModified.value,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// or in toolbar items:
|
|
112
|
+
{
|
|
113
|
+
id: "save",
|
|
114
|
+
disabled: computed(() => !formMeta.value.valid || !isModified.value),
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**AFTER:**
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// canSave is returned directly from useBladeForm:
|
|
122
|
+
const { canSave } = useBladeForm({ data: item });
|
|
123
|
+
|
|
124
|
+
// In toolbar items:
|
|
125
|
+
{
|
|
126
|
+
id: "save",
|
|
127
|
+
disabled: computed(() => !canSave.value),
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
For additional disabled conditions (e.g., async validation in progress), use `canSaveOverride`:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
const { canSave } = useBladeForm({
|
|
135
|
+
data: item,
|
|
136
|
+
canSaveOverride: computed(() => !isSkuValidating.value && !loading.value),
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## RULE 4: Keep Field from vee-validate
|
|
141
|
+
|
|
142
|
+
Only remove `useForm` from vee-validate imports. Keep `Field`, `ErrorMessage`, and other template-level components — they still work with `useBladeForm` which uses vee-validate internally.
|
|
143
|
+
|
|
144
|
+
**BEFORE:**
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { useForm, Field, ErrorMessage } from "vee-validate";
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**AFTER:**
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { Field, ErrorMessage } from "vee-validate";
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## RULE 5: Remove useBeforeUnload / useModificationTracker
|
|
157
|
+
|
|
158
|
+
`useBladeForm` handles both browser unload guards and modification tracking internally.
|
|
159
|
+
|
|
160
|
+
**BEFORE:**
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { useBeforeUnload, useModificationTracker } from "@vc-shell/framework";
|
|
164
|
+
|
|
165
|
+
const { currentValue, resetModificationState, isModified } = useModificationTracker(item);
|
|
166
|
+
useBeforeUnload(computed(() => isModified.value));
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**AFTER:**
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// Remove entirely. useBladeForm provides:
|
|
173
|
+
// - isModified (deep comparison against baseline)
|
|
174
|
+
// - setBaseline() (replaces resetModificationState)
|
|
175
|
+
// - revert() (replaces manual JSON.parse(JSON.stringify(currentValue.value)))
|
|
176
|
+
// - Browser unload guard (automatic)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Also remove `useModificationTracker` from data composables if present:
|
|
180
|
+
|
|
181
|
+
**BEFORE (data composable):**
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
export function useMyDetails() {
|
|
185
|
+
const item = ref<MyItem>();
|
|
186
|
+
const { currentValue, resetModificationState, isModified } = useModificationTracker(item);
|
|
187
|
+
|
|
188
|
+
async function load(id: string) {
|
|
189
|
+
item.value = await api.get(id);
|
|
190
|
+
resetModificationState();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return { item, currentValue, resetModificationState, isModified, load, save };
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**AFTER (data composable):**
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
export function useMyDetails() {
|
|
201
|
+
const item = ref<MyItem>();
|
|
202
|
+
|
|
203
|
+
async function load(id: string) {
|
|
204
|
+
item.value = await api.get(id);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return { item, load, save };
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Template Changes
|
|
212
|
+
|
|
213
|
+
Remove `:modified` prop from `<VcBlade>` — it is auto-detected via provide/inject.
|
|
214
|
+
|
|
215
|
+
**BEFORE:**
|
|
216
|
+
|
|
217
|
+
```vue
|
|
218
|
+
<template>
|
|
219
|
+
<VcBlade :modified="isModified" :closable="true">
|
|
220
|
+
<!-- content -->
|
|
221
|
+
</VcBlade>
|
|
222
|
+
</template>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**AFTER:**
|
|
226
|
+
|
|
227
|
+
```vue
|
|
228
|
+
<template>
|
|
229
|
+
<VcBlade :closable="true">
|
|
230
|
+
<!-- content — no :modified prop needed -->
|
|
231
|
+
</VcBlade>
|
|
232
|
+
</template>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Verification
|
|
236
|
+
|
|
237
|
+
After migration:
|
|
238
|
+
|
|
239
|
+
1. Run `npx tsc --noEmit` to verify no TypeScript errors
|
|
240
|
+
2. Confirm the blade shows a modified indicator when data changes
|
|
241
|
+
3. Confirm closing a modified blade shows the confirmation dialog
|
|
242
|
+
4. Confirm the save button is disabled when form is clean or invalid
|
|
243
|
+
5. Confirm `setBaseline()` is called after initial load and after save
|
|
244
|
+
6. Confirm cancel/revert restores data to the last baseline
|
|
245
|
+
7. Confirm `useModificationTracker` is removed from data composables
|
|
246
|
+
8. Confirm no stale imports of `useForm`, `useBeforeUnload`, `useModificationTracker`, or `onBeforeClose`
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blade-props-migration
|
|
3
|
+
description: AI transformation rules for reusable blade components skipped by CLI migrator.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Blade Props Migration: Boilerplate Removal
|
|
7
|
+
|
|
8
|
+
Remove blade prop/emit boilerplate from blade pages. VcBlade is now context-aware — it reads `expanded` and `closable` from the BladeDescriptor automatically via provide/inject. Use `useBlade()` for `param`, `options`, `closeSelf`, and `callParent`.
|
|
9
|
+
|
|
10
|
+
## RULE 1: Remove Blade Prop Forwarding from Template
|
|
11
|
+
|
|
12
|
+
Remove `:expanded`, `:closable`, `@close`, `@expand`, `@collapse` from the `<VcBlade>` tag.
|
|
13
|
+
|
|
14
|
+
**BEFORE:**
|
|
15
|
+
|
|
16
|
+
```vue
|
|
17
|
+
<template>
|
|
18
|
+
<VcBlade
|
|
19
|
+
:title="bladeTitle"
|
|
20
|
+
:expanded="expanded"
|
|
21
|
+
:closable="closable"
|
|
22
|
+
:toolbar-items="bladeToolbar"
|
|
23
|
+
@close="$emit('close:blade')"
|
|
24
|
+
@expand="$emit('expand:blade')"
|
|
25
|
+
@collapse="$emit('collapse:blade')"
|
|
26
|
+
>
|
|
27
|
+
<VcDataTable ... />
|
|
28
|
+
</VcBlade>
|
|
29
|
+
</template>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**AFTER:**
|
|
33
|
+
|
|
34
|
+
```vue
|
|
35
|
+
<template>
|
|
36
|
+
<VcBlade
|
|
37
|
+
:title="bladeTitle"
|
|
38
|
+
:toolbar-items="bladeToolbar"
|
|
39
|
+
>
|
|
40
|
+
<VcDataTable ... />
|
|
41
|
+
</VcBlade>
|
|
42
|
+
</template>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## RULE 2: Remove Props and Emits Interfaces
|
|
46
|
+
|
|
47
|
+
Remove `expanded`, `closable`, `param`, `options` from Props interface. Remove blade events from Emits. Use `useBlade()` to access `param` and `options`.
|
|
48
|
+
|
|
49
|
+
**BEFORE:**
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
export interface Props {
|
|
53
|
+
expanded?: boolean;
|
|
54
|
+
closable?: boolean;
|
|
55
|
+
param?: string;
|
|
56
|
+
options?: { sellerProduct?: SellerProduct };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface Emits {
|
|
60
|
+
(event: "parent:call", args: IParentCallArgs): void;
|
|
61
|
+
(event: "close:blade"): void;
|
|
62
|
+
(event: "collapse:blade"): void;
|
|
63
|
+
(event: "expand:blade"): void;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
67
|
+
expanded: true,
|
|
68
|
+
closable: true,
|
|
69
|
+
});
|
|
70
|
+
const emit = defineEmits<Emits>();
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**AFTER:**
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { useBlade } from "@vc-shell/framework";
|
|
77
|
+
|
|
78
|
+
const { param, options, openBlade, closeSelf, callParent } =
|
|
79
|
+
useBlade<{ sellerProduct?: SellerProduct }>();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
If the Props interface has other non-blade props, keep only those:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Keep non-blade props
|
|
86
|
+
export interface Props {
|
|
87
|
+
config?: DetailConfig;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const props = defineProps<Props>();
|
|
91
|
+
|
|
92
|
+
// Blade concerns via composable
|
|
93
|
+
const { param, options, closeSelf, callParent } = useBlade<{ orderId: string }>();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## RULE 3: Replace emit("parent:call") with callParent()
|
|
97
|
+
|
|
98
|
+
**BEFORE:**
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const emit = defineEmits<Emits>();
|
|
102
|
+
|
|
103
|
+
const reload = async () => {
|
|
104
|
+
await loadOffers({ ... });
|
|
105
|
+
emit("parent:call", { method: "reload" });
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
function onItemSelect(item: Product) {
|
|
109
|
+
emit("parent:call", { method: "onItemClick", args: { data: item } });
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**AFTER:**
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const { callParent } = useBlade();
|
|
117
|
+
|
|
118
|
+
const reload = async () => {
|
|
119
|
+
await loadOffers({ ... });
|
|
120
|
+
callParent("reload");
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
function onItemSelect(item: Product) {
|
|
124
|
+
callParent("onItemClick", { data: item });
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## RULE 4: Replace emit("close:blade") with closeSelf()
|
|
129
|
+
|
|
130
|
+
**BEFORE:**
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const emit = defineEmits<Emits>();
|
|
134
|
+
|
|
135
|
+
function handleClose() {
|
|
136
|
+
emit("close:blade");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// or in template:
|
|
140
|
+
// @click="$emit('close:blade')"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**AFTER:**
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
const { closeSelf } = useBlade();
|
|
147
|
+
|
|
148
|
+
function handleClose() {
|
|
149
|
+
closeSelf();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// or in template:
|
|
153
|
+
// @click="closeSelf()"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Wrapper Components
|
|
157
|
+
|
|
158
|
+
If a wrapper component forwards blade props to a base component, remove the forwarding. The base component should call `useBlade()` directly.
|
|
159
|
+
|
|
160
|
+
**BEFORE:**
|
|
161
|
+
|
|
162
|
+
```vue
|
|
163
|
+
<template>
|
|
164
|
+
<ProductDetailsBase
|
|
165
|
+
:expanded="expanded"
|
|
166
|
+
:closable="closable"
|
|
167
|
+
:param="param"
|
|
168
|
+
:options="options"
|
|
169
|
+
@parent:call="$emit('parent:call', $event)"
|
|
170
|
+
@close:blade="$emit('close:blade')"
|
|
171
|
+
@expand:blade="$emit('expand:blade')"
|
|
172
|
+
@collapse:blade="$emit('collapse:blade')"
|
|
173
|
+
/>
|
|
174
|
+
</template>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**AFTER:**
|
|
178
|
+
|
|
179
|
+
```vue
|
|
180
|
+
<template>
|
|
181
|
+
<ProductDetailsBase />
|
|
182
|
+
</template>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Verification
|
|
186
|
+
|
|
187
|
+
After migration:
|
|
188
|
+
|
|
189
|
+
1. Run `npx tsc --noEmit` to verify no TypeScript errors
|
|
190
|
+
2. Confirm blades open and close correctly
|
|
191
|
+
3. Confirm `param.value` is accessible in script (use `param` without `.value` in templates)
|
|
192
|
+
4. Confirm `options.value` provides typed options from parent
|
|
193
|
+
5. Confirm `callParent()` triggers the parent's exposed method
|
|
194
|
+
6. Confirm no remaining `emit("close:blade")` or `emit("parent:call", ...)` calls
|
|
195
|
+
7. Confirm no remaining `:expanded`, `:closable`, `@close`, `@expand`, `@collapse` on `<VcBlade>`
|
|
@@ -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
|