wcz-layout 7.6.0 → 7.6.2

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.

Potentially problematic release.


This version of wcz-layout might be problematic. Click here for more details.

Files changed (34) hide show
  1. package/dist/{RouterListItemButton-CvfZk2zD.js → RouterListItemButton-DeaQB4ym.js} +1 -1
  2. package/dist/{RouterListItemButton-CvfZk2zD.js.map → RouterListItemButton-DeaQB4ym.js.map} +1 -1
  3. package/dist/components/core/Layout.d.ts +1 -1
  4. package/dist/components/core/navigation/NavigationList.d.ts +4 -4
  5. package/dist/components/core/navigation/NavigationListItem.d.ts +3 -3
  6. package/dist/components.js +2 -2
  7. package/dist/hooks.js +1 -1
  8. package/dist/index.js +617 -621
  9. package/dist/index.js.map +1 -1
  10. package/dist/lib/auth/msalClient.d.ts +8 -2
  11. package/dist/middleware.js +11 -11
  12. package/dist/models/Navigation.d.ts +23 -11
  13. package/dist/{queries-DzKY6YXz.js → queries-D-DV5lCw.js} +3 -3
  14. package/dist/{queries-DzKY6YXz.js.map → queries-D-DV5lCw.js.map} +1 -1
  15. package/dist/query.js +2 -2
  16. package/dist/{queryClient-uWNhcABg.js → queryClient-B__OEZ70.js} +1 -1
  17. package/dist/{queryClient-uWNhcABg.js.map → queryClient-B__OEZ70.js.map} +1 -1
  18. package/dist/{msalClient-BLrbVP5z.js → utils-C4oJ0tr5.js} +58 -47
  19. package/dist/utils-C4oJ0tr5.js.map +1 -0
  20. package/dist/utils.js +5 -5
  21. package/package.json +38 -6
  22. package/skills/api-routes/SKILL.md +251 -0
  23. package/skills/auth/SKILL.md +268 -0
  24. package/skills/data-grid/SKILL.md +229 -0
  25. package/skills/database-schema/SKILL.md +182 -0
  26. package/skills/dialogs-notifications/SKILL.md +241 -0
  27. package/skills/forms-validation/SKILL.md +331 -0
  28. package/skills/forms-validation/references/field-components.md +212 -0
  29. package/skills/layout-navigation/SKILL.md +259 -0
  30. package/skills/project-initialization/SKILL.md +181 -0
  31. package/skills/project-structure/SKILL.md +157 -0
  32. package/skills/tanstack-db-collections/SKILL.md +270 -0
  33. package/skills/ui-pages/SKILL.md +278 -0
  34. package/dist/msalClient-BLrbVP5z.js.map +0 -1
@@ -0,0 +1,241 @@
1
+ ---
2
+ name: dialogs-notifications
3
+ description: >
4
+ Use useDialogs for alert/confirm/custom dialogs and useNotification for
5
+ snackbar messages. Promise-based dialog API with DialogProps type for
6
+ custom dialogs. Notification severity (success, error, warning, info)
7
+ with configurable autoHideDuration. Activate when showing confirmations,
8
+ alerts, custom dialogs, or toast notifications.
9
+ type: core
10
+ library: wcz-layout
11
+ library_version: "7.6.1"
12
+ sources:
13
+ - "wcz-layout:src/hooks/DialogsHooks.tsx"
14
+ - "wcz-layout:src/providers/DialogsProvider.tsx"
15
+ - "wcz-layout:src/contexts/NotificationContext.tsx"
16
+ ---
17
+
18
+ # Dialogs & Notifications
19
+
20
+ ## Setup
21
+
22
+ Both hooks are available inside the `LayoutProvider` tree (which includes `DialogsProvider` and `NotificationProvider`):
23
+
24
+ ```typescript
25
+ import { useDialogs, useNotification } from "wcz-layout/hooks";
26
+ ```
27
+
28
+ ## Core Patterns
29
+
30
+ ### Confirmation dialog before deletion
31
+
32
+ ```typescript
33
+ import { useDialogs, useNotification } from "wcz-layout/hooks";
34
+ import { todoCollection } from "~/lib/db/collections/todoCollection";
35
+ import type { Todo } from "~/schemas/todo";
36
+
37
+ function DeleteButton({ todo }: { todo: Todo }) {
38
+ const { confirm } = useDialogs();
39
+ const { notify } = useNotification();
40
+
41
+ const handleDelete = async () => {
42
+ const confirmed = await confirm("Delete this item?");
43
+ if (confirmed) {
44
+ todoCollection.delete(todo);
45
+ notify("Item deleted", { severity: "success" });
46
+ }
47
+ };
48
+
49
+ return <Button onClick={handleDelete}>Delete</Button>;
50
+ }
51
+ ```
52
+
53
+ ### Alert dialog
54
+
55
+ ```typescript
56
+ const { alert } = useDialogs();
57
+
58
+ await alert("Operation completed successfully", { title: "Success" });
59
+ // Resolves when user clicks OK
60
+ ```
61
+
62
+ ### Custom dialog
63
+
64
+ Define the dialog component with `DialogProps`:
65
+
66
+ ```typescript
67
+ import type { DialogProps } from "wcz-layout/hooks";
68
+ import {
69
+ Dialog,
70
+ DialogTitle,
71
+ DialogContent,
72
+ DialogActions,
73
+ Button,
74
+ TextField,
75
+ } from "@mui/material";
76
+ import { useState } from "react";
77
+
78
+ interface NotePayload {
79
+ title: string;
80
+ }
81
+
82
+ function NoteDialog({ payload, open, onClose }: DialogProps<NotePayload, string>) {
83
+ const [note, setNote] = useState("");
84
+
85
+ return (
86
+ <Dialog open={open} onClose={() => onClose("")} maxWidth="sm" fullWidth>
87
+ <DialogTitle>{payload.title}</DialogTitle>
88
+ <DialogContent>
89
+ <TextField
90
+ autoFocus
91
+ fullWidth
92
+ label="Note"
93
+ value={note}
94
+ onChange={(e) => setNote(e.target.value)}
95
+ />
96
+ </DialogContent>
97
+ <DialogActions>
98
+ <Button onClick={() => onClose("")}>Cancel</Button>
99
+ <Button onClick={() => onClose(note)} variant="contained">
100
+ Save
101
+ </Button>
102
+ </DialogActions>
103
+ </Dialog>
104
+ );
105
+ }
106
+ ```
107
+
108
+ Open the custom dialog and await its result:
109
+
110
+ ```typescript
111
+ const { open } = useDialogs();
112
+
113
+ const note = await open(NoteDialog, { title: "Add a note" });
114
+ if (note) {
115
+ // user entered a note
116
+ }
117
+ ```
118
+
119
+ ### Dialog API reference
120
+
121
+ ```typescript
122
+ interface DialogHook {
123
+ alert(message: string, options?: { title?: string }): Promise<void>;
124
+ confirm(message: string, options?: { title?: string; cancelText?: string }): Promise<boolean>;
125
+ open<TPayload, TResult>(
126
+ Component: ComponentType<DialogProps<TPayload, TResult>>,
127
+ payload?: TPayload,
128
+ options?: DialogOptions,
129
+ ): Promise<TResult>;
130
+ close<TResult>(dialogPromise: Promise<TResult>, result: TResult): Promise<TResult>;
131
+ }
132
+ ```
133
+
134
+ ### Notifications
135
+
136
+ ```typescript
137
+ const { notify } = useNotification();
138
+
139
+ notify("Changes saved", { severity: "success" });
140
+ notify("Something went wrong", { severity: "error" });
141
+ notify("Check your input", { severity: "warning" });
142
+ notify("New version available", { severity: "info" });
143
+
144
+ // Custom auto-hide duration (default: 5000ms)
145
+ notify("Processing...", { severity: "info", autoHideDuration: 10000 });
146
+ ```
147
+
148
+ ### After form submission
149
+
150
+ ```typescript
151
+ const form = useLayoutForm({
152
+ defaultValues: { name: "" } as Todo,
153
+ validators: { onChange: TodoSchema },
154
+ onSubmit: ({ value }) => {
155
+ todoCollection.insert(value);
156
+ notify("Todo created", { severity: "success" });
157
+ },
158
+ });
159
+ ```
160
+
161
+ ## Common Mistakes
162
+
163
+ ### HIGH Using window.confirm instead of useDialogs
164
+
165
+ Wrong:
166
+
167
+ ```typescript
168
+ if (window.confirm("Delete this item?")) {
169
+ todoCollection.delete(todo);
170
+ }
171
+ ```
172
+
173
+ Correct:
174
+
175
+ ```typescript
176
+ const { confirm } = useDialogs();
177
+
178
+ const confirmed = await confirm("Delete this item?");
179
+ if (confirmed) {
180
+ todoCollection.delete(todo);
181
+ }
182
+ ```
183
+
184
+ `window.confirm` is a blocking browser dialog that ignores MUI theming and i18n. `useDialogs().confirm` provides themed, async/await MUI dialogs.
185
+
186
+ Source: wcz-layout:src/hooks/DialogsHooks.tsx
187
+
188
+ ### CRITICAL Custom dialog not calling onClose
189
+
190
+ Wrong:
191
+
192
+ ```typescript
193
+ function MyDialog({ payload, open }: DialogProps<string>) {
194
+ return (
195
+ <Dialog open={open}>
196
+ <Button onClick={() => {}}>Done</Button>
197
+ </Dialog>
198
+ );
199
+ }
200
+ ```
201
+
202
+ Correct:
203
+
204
+ ```typescript
205
+ function MyDialog({ payload, open, onClose }: DialogProps<string>) {
206
+ return (
207
+ <Dialog open={open} onClose={() => onClose()}>
208
+ <Button onClick={() => onClose()}>Done</Button>
209
+ </Dialog>
210
+ );
211
+ }
212
+ ```
213
+
214
+ `open()` returns a `Promise` that resolves when `onClose(result)` is called. Forgetting `onClose` leaves the promise hanging permanently and the dialog stuck open. Always wire both the `Dialog` `onClose` prop and button click handlers.
215
+
216
+ Source: wcz-layout:src/providers/DialogsProvider.tsx
217
+
218
+ ### HIGH Using useDialogs outside LayoutProvider tree
219
+
220
+ Wrong:
221
+
222
+ ```typescript
223
+ // Component rendered outside LayoutProvider
224
+ function StandaloneComponent() {
225
+ const { alert } = useDialogs(); // context has unsafe default — crashes on call
226
+ }
227
+ ```
228
+
229
+ Correct:
230
+
231
+ Ensure all components using `useDialogs` or `useNotification` are rendered inside the `LayoutProvider` component tree (which wraps `DialogsProvider` and `NotificationProvider`).
232
+
233
+ `DialogsContext` uses an unsafe cast default value. Accessing the context outside the provider doesn't throw on `useContext`, but crashes when `alert`, `confirm`, or `open` are invoked.
234
+
235
+ Source: wcz-layout:src/contexts/DialogsContext.ts
236
+
237
+ ---
238
+
239
+ See also:
240
+
241
+ - skills/forms-validation/SKILL.md — Form submissions typically show notifications or confirmations.
@@ -0,0 +1,331 @@
1
+ ---
2
+ name: forms-validation
3
+ description: >
4
+ Build forms with useLayoutForm hook (primary) and withLayoutForm for
5
+ composable sub-forms. 13 pre-registered MUI field components via
6
+ form.AppField: TextField, NumberField, Autocomplete, Checkbox, Switch,
7
+ RadioGroup, Slider, DatePicker, DateRangePicker, TimePicker,
8
+ TimeRangePicker, DateTimePicker, DateTimeRangePicker. SubmitButton in
9
+ form.AppForm. Zod onChange validators. FormOmittedProps type. Activate
10
+ when creating or modifying forms with validation.
11
+ type: core
12
+ library: wcz-layout
13
+ library_version: "7.6.1"
14
+ sources:
15
+ - "wcz-layout:src/hooks/FormHooks.ts"
16
+ - "wcz-layout:src/components/form/"
17
+ - "wcz-layout:src/lib/utils.ts"
18
+ references:
19
+ - "references/field-components.md"
20
+ ---
21
+
22
+ # Forms & Validation
23
+
24
+ ## Setup
25
+
26
+ Import the form hook and Zod schema:
27
+
28
+ ```typescript
29
+ import { useLayoutForm } from "wcz-layout/hooks";
30
+ import { TodoSchema } from "~/schemas/todo";
31
+ ```
32
+
33
+ ## Core Patterns
34
+
35
+ ### Basic form with useLayoutForm
36
+
37
+ ```typescript
38
+ import { useLayoutForm } from "wcz-layout/hooks";
39
+ import { TodoSchema } from "~/schemas/todo";
40
+ import type { Todo } from "~/schemas/todo";
41
+ import { todoCollection } from "~/lib/db/collections/todoCollection";
42
+ import { uuidv7 } from "uuidv7";
43
+
44
+ function TodoForm() {
45
+ const form = useLayoutForm({
46
+ defaultValues: {
47
+ id: uuidv7(),
48
+ name: "",
49
+ description: "",
50
+ isCompleted: false,
51
+ } as Todo,
52
+ validators: {
53
+ onChange: TodoSchema,
54
+ },
55
+ onSubmit: ({ value }) => {
56
+ todoCollection.insert(value);
57
+ },
58
+ });
59
+
60
+ return (
61
+ <form.AppForm>
62
+ <form.AppField
63
+ name="name"
64
+ children={(field) => <field.TextField label="Name" required />}
65
+ />
66
+ <form.AppField
67
+ name="description"
68
+ children={(field) => <field.TextField label="Description" multiline rows={3} />}
69
+ />
70
+ <form.AppField
71
+ name="isCompleted"
72
+ children={(field) => <field.Checkbox label="Completed" />}
73
+ />
74
+ <form.SubmitButton />
75
+ </form.AppForm>
76
+ );
77
+ }
78
+ ```
79
+
80
+ ### Available field components
81
+
82
+ All 13 field components are accessed through `field.*` inside `form.AppField`:
83
+
84
+ | Component | MUI base | Use case |
85
+ | --------------------------- | --------------------------- | ----------------------------- |
86
+ | `field.TextField` | TextField | Text input, multiline |
87
+ | `field.NumberField` | TextField (number) | Numeric input |
88
+ | `field.Autocomplete` | Autocomplete | Search/select with options |
89
+ | `field.Checkbox` | FormControlLabel + Checkbox | Boolean toggle |
90
+ | `field.Switch` | FormControlLabel + Switch | Boolean toggle (switch) |
91
+ | `field.RadioGroup` | RadioGroup | Single selection from options |
92
+ | `field.Slider` | Slider | Range/value slider |
93
+ | `field.DatePicker` | DatePicker | Date only |
94
+ | `field.DateRangePicker` | DateRangePicker | Date range |
95
+ | `field.TimePicker` | TimePicker | Time only |
96
+ | `field.TimeRangePicker` | TimeRangePicker | Time range |
97
+ | `field.DateTimePicker` | DateTimePicker | Date + time |
98
+ | `field.DateTimeRangePicker` | DateTimeRangePicker | Date + time range |
99
+
100
+ See references/field-components.md for detailed prop surfaces.
101
+
102
+ ### Edit form with existing data
103
+
104
+ ```typescript
105
+ function TodoEditForm({ todo }: { todo: Todo }) {
106
+ const form = useLayoutForm({
107
+ defaultValues: todo,
108
+ validators: {
109
+ onChange: TodoSchema,
110
+ },
111
+ onSubmit: ({ value }) => {
112
+ todoCollection.update(value);
113
+ },
114
+ });
115
+
116
+ return (
117
+ <form.AppForm>
118
+ <form.AppField
119
+ name="name"
120
+ children={(field) => <field.TextField label="Name" required />}
121
+ />
122
+ <form.SubmitButton />
123
+ </form.AppForm>
124
+ );
125
+ }
126
+ ```
127
+
128
+ ### Sub-form composition with withLayoutForm
129
+
130
+ Use `withLayoutForm` when splitting a large form into reusable sub-form components:
131
+
132
+ ```typescript
133
+ import { withLayoutForm } from "wcz-layout/hooks";
134
+ import { TodoSchema } from "~/schemas/todo";
135
+
136
+ const TodoDetailsSubForm = withLayoutForm({
137
+ defaultValues: { name: "", description: "" },
138
+ render: ({ form }) => (
139
+ <>
140
+ <form.AppField
141
+ name="name"
142
+ children={(field) => <field.TextField label="Name" required />}
143
+ />
144
+ <form.AppField
145
+ name="description"
146
+ children={(field) => <field.TextField label="Description" multiline />}
147
+ />
148
+ </>
149
+ ),
150
+ });
151
+ ```
152
+
153
+ ### Autocomplete with API data
154
+
155
+ ```typescript
156
+ <form.AppField
157
+ name="assigneeId"
158
+ children={(field) => (
159
+ <field.Autocomplete
160
+ label="Assignee"
161
+ options={users}
162
+ getOptionLabel={(user) => user.displayName}
163
+ isOptionEqualToValue={(option, value) => option.id === value.id}
164
+ />
165
+ )}
166
+ />
167
+ ```
168
+
169
+ ### Date pickers
170
+
171
+ ```typescript
172
+ <form.AppField
173
+ name="dueDate"
174
+ children={(field) => <field.DatePicker label="Due Date" />}
175
+ />
176
+
177
+ <form.AppField
178
+ name="dateRange"
179
+ children={(field) => (
180
+ <field.DateRangePicker
181
+ localeText={{ start: "Start Date", end: "End Date" }}
182
+ />
183
+ )}
184
+ />
185
+ ```
186
+
187
+ ## Common Mistakes
188
+
189
+ ### CRITICAL Using raw MUI TextField instead of form.AppField
190
+
191
+ Wrong:
192
+
193
+ ```typescript
194
+ <TextField
195
+ name="title"
196
+ value={title}
197
+ onChange={(e) => setTitle(e.target.value)}
198
+ />
199
+ ```
200
+
201
+ Correct:
202
+
203
+ ```typescript
204
+ <form.AppField
205
+ name="title"
206
+ children={(field) => <field.TextField label="Title" />}
207
+ />
208
+ ```
209
+
210
+ `useLayoutForm` pre-registers all field components. Using raw MUI inputs bypasses TanStack Form state management, validation, and error display.
211
+
212
+ Source: wcz-layout:src/hooks/FormHooks.ts
213
+
214
+ ### HIGH Passing name/value/onChange to AppField components
215
+
216
+ Wrong:
217
+
218
+ ```typescript
219
+ <form.AppField
220
+ name="title"
221
+ children={(field) => (
222
+ <field.TextField
223
+ label="Title"
224
+ name="title"
225
+ value={field.state.value}
226
+ onChange={(e) => field.handleChange(e.target.value)}
227
+ />
228
+ )}
229
+ />
230
+ ```
231
+
232
+ Correct:
233
+
234
+ ```typescript
235
+ <form.AppField
236
+ name="title"
237
+ children={(field) => <field.TextField label="Title" />}
238
+ />
239
+ ```
240
+
241
+ `FormOmittedProps` explicitly strips `name`, `value`, `onChange`, `onBlur`, `error`, `helperText`, `renderInput`, `type`, and `aria-label`. These are managed internally by `useFieldContext`. Passing them causes conflicts or is silently ignored.
242
+
243
+ Source: wcz-layout:src/lib/utils.ts
244
+
245
+ ### HIGH Not wrapping SubmitButton in form.AppForm
246
+
247
+ Wrong:
248
+
249
+ ```typescript
250
+ <div>
251
+ <form.AppField name="name" children={(field) => <field.TextField label="Name" />} />
252
+ <form.SubmitButton /> {/* Outside AppForm — crashes */}
253
+ </div>
254
+ ```
255
+
256
+ Correct:
257
+
258
+ ```typescript
259
+ <form.AppForm>
260
+ <form.AppField name="name" children={(field) => <field.TextField label="Name" />} />
261
+ <form.SubmitButton />
262
+ </form.AppForm>
263
+ ```
264
+
265
+ `SubmitButton` uses `useFormContext()` to read `canSubmit` and `isSubmitting` state. It must be rendered inside `form.AppForm`.
266
+
267
+ Source: wcz-layout:src/components/form/FormSubmitButton.tsx
268
+
269
+ ### HIGH Using useMemo or useCallback in form components
270
+
271
+ Wrong:
272
+
273
+ ```typescript
274
+ const handleSubmit = useCallback(() => form.handleSubmit(), [form]);
275
+ ```
276
+
277
+ Correct:
278
+
279
+ ```typescript
280
+ const handleSubmit = () => form.handleSubmit();
281
+ ```
282
+
283
+ React Compiler handles memoization. Manual `useMemo` / `useCallback` is forbidden per project conventions.
284
+
285
+ Source: copilot-instructions.md
286
+
287
+ Cross-skill: See also skills/ui-pages/SKILL.md § Common Mistakes
288
+
289
+ ### MEDIUM FormRadioGroup numeric values become strings
290
+
291
+ Wrong:
292
+
293
+ ```typescript
294
+ <field.RadioGroup
295
+ options={[
296
+ { label: "Low", value: 1 },
297
+ { label: "High", value: 2 },
298
+ ]}
299
+ />
300
+ // field.state.value is "1" not 1
301
+ ```
302
+
303
+ Correct:
304
+
305
+ ```typescript
306
+ <field.RadioGroup
307
+ options={[
308
+ { label: "Low", value: "1" },
309
+ { label: "High", value: "2" },
310
+ ]}
311
+ />
312
+ // Use string values consistently, or convert in onSubmit
313
+ ```
314
+
315
+ Radio group `onChange` always returns `event.target.value` as a string. Numeric option values round-trip as strings unless explicitly converted.
316
+
317
+ Source: wcz-layout:src/components/form/FormRadioGroup.tsx
318
+
319
+ ### HIGH Tension: Type safety vs. rapid prototyping
320
+
321
+ Quick forms using `useState` + manual validation bypass the enforced pattern. Always use `useLayoutForm` + Zod schema derived from Drizzle — even for simple forms. The boilerplate pays off in type safety and consistency.
322
+
323
+ See also: skills/database-schema/SKILL.md § Common Mistakes
324
+
325
+ ---
326
+
327
+ See also:
328
+
329
+ - skills/database-schema/SKILL.md — Zod schemas used as form validators.
330
+ - skills/dialogs-notifications/SKILL.md — Form submissions typically show notifications.
331
+ - skills/ui-pages/SKILL.md — Create/edit pages embed forms.