wcz-layout 8.1.0 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/components.js +2 -2
  2. package/dist/components.js.map +1 -1
  3. package/dist/data/client.d.ts +1 -1
  4. package/dist/data/server.d.ts +1 -1
  5. package/dist/models.d.ts +4 -4
  6. package/dist/{peoplesoft-CFgBFvG-.d.ts → peoplesoft-kaMETchL.d.ts} +5 -5
  7. package/package.json +154 -154
  8. package/skills/client-db/SKILL.md +111 -0
  9. package/skills/db-schema/SKILL.md +69 -0
  10. package/skills/forms/SKILL.md +85 -0
  11. package/skills/routes/SKILL.md +127 -0
  12. package/skills/server/SKILL.md +114 -0
  13. package/skills/start/SKILL.md +46 -0
  14. package/skills/start/steps/01-git-setup.md +10 -0
  15. package/skills/start/steps/02-dependency-check.md +11 -0
  16. package/skills/start/steps/03-env-setup.md +16 -0
  17. package/skills/start/steps/04-project-name-setup.md +14 -0
  18. package/skills/start/steps/05-database-setup.md +37 -0
  19. package/skills/start/steps/06-entra-setup.md +59 -0
  20. package/skills/start/steps/07-vault-setup.md +56 -0
  21. package/skills/start/steps/08-generate-favicon.md +15 -0
  22. package/skills/start/steps/09-commit.md +15 -0
  23. package/skills/tables/SKILL.md +184 -0
  24. package/skills/api-routes/SKILL.md +0 -251
  25. package/skills/auth/SKILL.md +0 -268
  26. package/skills/data-grid/SKILL.md +0 -229
  27. package/skills/database-schema/SKILL.md +0 -182
  28. package/skills/dialogs-notifications/SKILL.md +0 -241
  29. package/skills/forms-validation/SKILL.md +0 -331
  30. package/skills/forms-validation/references/field-components.md +0 -212
  31. package/skills/layout-navigation/SKILL.md +0 -259
  32. package/skills/project-initialization/SKILL.md +0 -181
  33. package/skills/project-structure/SKILL.md +0 -157
  34. package/skills/tanstack-db-collections/SKILL.md +0 -270
  35. package/skills/template-init.md +0 -146
  36. package/skills/ui-pages/SKILL.md +0 -278
package/package.json CHANGED
@@ -1,154 +1,154 @@
1
- {
2
- "name": "wcz-layout",
3
- "version": "8.1.0",
4
- "private": false,
5
- "keywords": [
6
- "tanstack-intent"
7
- ],
8
- "files": [
9
- "dist",
10
- "skills",
11
- "!skills/_artifacts"
12
- ],
13
- "type": "module",
14
- "sideEffects": false,
15
- "types": "./dist/index.d.ts",
16
- "typesVersions": {
17
- "*": {
18
- "components": [
19
- "./dist/components.d.ts"
20
- ],
21
- "hooks": [
22
- "./dist/hooks.d.ts"
23
- ],
24
- "middleware": [
25
- "./dist/middleware.d.ts"
26
- ],
27
- "models": [
28
- "./dist/models.d.ts"
29
- ],
30
- "data": [
31
- "./dist/data.d.ts"
32
- ],
33
- "data/client": [
34
- "./dist/data/client.d.ts"
35
- ],
36
- "data/server": [
37
- "./dist/data/server.d.ts"
38
- ],
39
- "utils": [
40
- "./dist/utils.d.ts"
41
- ],
42
- "vite": [
43
- "./dist/vite.d.ts"
44
- ]
45
- }
46
- },
47
- "exports": {
48
- ".": {
49
- "types": "./dist/index.d.ts",
50
- "import": "./dist/index.js"
51
- },
52
- "./components": {
53
- "types": "./dist/components.d.ts",
54
- "import": "./dist/components.js"
55
- },
56
- "./hooks": {
57
- "types": "./dist/hooks.d.ts",
58
- "import": "./dist/hooks.js"
59
- },
60
- "./middleware": {
61
- "types": "./dist/middleware.d.ts",
62
- "import": "./dist/middleware.js"
63
- },
64
- "./models": {
65
- "types": "./dist/models.d.ts",
66
- "import": "./dist/models.js"
67
- },
68
- "./data": {
69
- "types": "./dist/data.d.ts",
70
- "import": "./dist/data.js"
71
- },
72
- "./data/client": {
73
- "types": "./dist/data/client.d.ts",
74
- "import": "./dist/data/client.js"
75
- },
76
- "./data/server": {
77
- "types": "./dist/data/server.d.ts",
78
- "import": "./dist/data/server.js"
79
- },
80
- "./utils": {
81
- "types": "./dist/utils.d.ts",
82
- "import": "./dist/utils.js"
83
- },
84
- "./vite": {
85
- "types": "./dist/vite.d.ts",
86
- "import": "./dist/vite.js"
87
- }
88
- },
89
- "scripts": {
90
- "dev": "vp dev",
91
- "vp:install": "vp install",
92
- "vp:update": "vp update",
93
- "ncu:install": "npx npm-check-updates -i",
94
- "lint:package": "publint",
95
- "build": "vp pack",
96
- "prepublishOnly": "npm run build"
97
- },
98
- "dependencies": {
99
- "@azure/msal-browser": "^5.9.0",
100
- "@azure/msal-node": "^5.1.5",
101
- "@azure/msal-react": "^5.3.2",
102
- "@t3-oss/env-core": "^0.13.11",
103
- "file-saver": "^2.0.5",
104
- "i18next": "^26.0.8",
105
- "i18next-browser-languagedetector": "^8.2.1",
106
- "jose": "^6.2.3",
107
- "react-dropzone": "^15.0.0",
108
- "react-i18next": "^17.0.6",
109
- "react-intersection-observer": "^10.0.3",
110
- "react-number-format": "^5.4.5",
111
- "tus-js-client": "^4.3.1",
112
- "uuidv7": "^1.2.1"
113
- },
114
- "devDependencies": {
115
- "@rolldown/plugin-babel": "^0.2.3",
116
- "@tanstack/intent": "^0.0.40",
117
- "@types/file-saver": "^2.0.7",
118
- "@types/node": "^24.10.13",
119
- "@types/react": "^19.2.14",
120
- "@types/react-dom": "^19.2.3",
121
- "@vitejs/plugin-react": "^6.0.1",
122
- "babel-plugin-react-compiler": "^1.0.0",
123
- "nitro": "npm:nitro-nightly@latest",
124
- "publint": "^0.3.18",
125
- "typescript": "^6.0.3",
126
- "vite-plugin-checker": "^0.13.0"
127
- },
128
- "peerDependencies": {
129
- "@emotion/react": "11.x",
130
- "@emotion/styled": "11.x",
131
- "@mui/icons-material": "9.x",
132
- "@mui/material": "9.x",
133
- "@mui/x-data-grid-premium": "9.x",
134
- "@mui/x-date-pickers-pro": "9.x",
135
- "@tanstack/query-db-collection": "1.x",
136
- "@tanstack/react-db": "0.x",
137
- "@tanstack/react-form": "1.x",
138
- "@tanstack/react-query": "5.x",
139
- "@tanstack/react-router": "1.x",
140
- "@tanstack/react-router-ssr-query": "1.x",
141
- "@tanstack/react-start": "1.x",
142
- "axios": "1.x",
143
- "dayjs": "1.x",
144
- "react": "19.x",
145
- "react-dom": "19.x",
146
- "vite": "npm:@voidzero-dev/vite-plus-core@latest",
147
- "vite-plus": "latest",
148
- "zod": "4.x"
149
- },
150
- "overrides": {
151
- "vite": "npm:@voidzero-dev/vite-plus-core@latest"
152
- },
153
- "packageManager": "npm@11.13.0"
154
- }
1
+ {
2
+ "name": "wcz-layout",
3
+ "version": "8.2.0",
4
+ "private": false,
5
+ "keywords": [
6
+ "tanstack-intent"
7
+ ],
8
+ "files": [
9
+ "dist",
10
+ "skills",
11
+ "!skills/_artifacts"
12
+ ],
13
+ "type": "module",
14
+ "sideEffects": false,
15
+ "types": "./dist/index.d.ts",
16
+ "typesVersions": {
17
+ "*": {
18
+ "components": [
19
+ "./dist/components.d.ts"
20
+ ],
21
+ "hooks": [
22
+ "./dist/hooks.d.ts"
23
+ ],
24
+ "middleware": [
25
+ "./dist/middleware.d.ts"
26
+ ],
27
+ "models": [
28
+ "./dist/models.d.ts"
29
+ ],
30
+ "data": [
31
+ "./dist/data.d.ts"
32
+ ],
33
+ "data/client": [
34
+ "./dist/data/client.d.ts"
35
+ ],
36
+ "data/server": [
37
+ "./dist/data/server.d.ts"
38
+ ],
39
+ "utils": [
40
+ "./dist/utils.d.ts"
41
+ ],
42
+ "vite": [
43
+ "./dist/vite.d.ts"
44
+ ]
45
+ }
46
+ },
47
+ "exports": {
48
+ ".": {
49
+ "types": "./dist/index.d.ts",
50
+ "import": "./dist/index.js"
51
+ },
52
+ "./components": {
53
+ "types": "./dist/components.d.ts",
54
+ "import": "./dist/components.js"
55
+ },
56
+ "./hooks": {
57
+ "types": "./dist/hooks.d.ts",
58
+ "import": "./dist/hooks.js"
59
+ },
60
+ "./middleware": {
61
+ "types": "./dist/middleware.d.ts",
62
+ "import": "./dist/middleware.js"
63
+ },
64
+ "./models": {
65
+ "types": "./dist/models.d.ts",
66
+ "import": "./dist/models.js"
67
+ },
68
+ "./data": {
69
+ "types": "./dist/data.d.ts",
70
+ "import": "./dist/data.js"
71
+ },
72
+ "./data/client": {
73
+ "types": "./dist/data/client.d.ts",
74
+ "import": "./dist/data/client.js"
75
+ },
76
+ "./data/server": {
77
+ "types": "./dist/data/server.d.ts",
78
+ "import": "./dist/data/server.js"
79
+ },
80
+ "./utils": {
81
+ "types": "./dist/utils.d.ts",
82
+ "import": "./dist/utils.js"
83
+ },
84
+ "./vite": {
85
+ "types": "./dist/vite.d.ts",
86
+ "import": "./dist/vite.js"
87
+ }
88
+ },
89
+ "scripts": {
90
+ "dev": "vp dev",
91
+ "vp:install": "vp install",
92
+ "vp:update": "vp update",
93
+ "ncu:install": "npx npm-check-updates -i",
94
+ "lint:package": "publint",
95
+ "build": "vp pack",
96
+ "prepublishOnly": "npm run build"
97
+ },
98
+ "dependencies": {
99
+ "@azure/msal-browser": "^5.10.1",
100
+ "@azure/msal-node": "^5.2.1",
101
+ "@azure/msal-react": "^5.4.1",
102
+ "@t3-oss/env-core": "^0.13.11",
103
+ "file-saver": "^2.0.5",
104
+ "i18next": "^26.1.0",
105
+ "i18next-browser-languagedetector": "^8.2.1",
106
+ "jose": "^6.2.3",
107
+ "react-dropzone": "^15.0.0",
108
+ "react-i18next": "^17.0.7",
109
+ "react-intersection-observer": "^10.0.3",
110
+ "react-number-format": "^5.4.5",
111
+ "tus-js-client": "^4.3.1",
112
+ "uuidv7": "^1.2.1"
113
+ },
114
+ "devDependencies": {
115
+ "@rolldown/plugin-babel": "^0.2.3",
116
+ "@tanstack/intent": "^0.0.41",
117
+ "@types/file-saver": "^2.0.7",
118
+ "@types/node": "^24.10.13",
119
+ "@types/react": "^19.2.14",
120
+ "@types/react-dom": "^19.2.3",
121
+ "@vitejs/plugin-react": "^6.0.1",
122
+ "babel-plugin-react-compiler": "^1.0.0",
123
+ "nitro": "npm:nitro-nightly@latest",
124
+ "publint": "^0.3.20",
125
+ "typescript": "^6.0.3",
126
+ "vite-plugin-checker": "^0.13.0"
127
+ },
128
+ "peerDependencies": {
129
+ "@emotion/react": "11.x",
130
+ "@emotion/styled": "11.x",
131
+ "@mui/icons-material": "9.x",
132
+ "@mui/material": "9.x",
133
+ "@mui/x-data-grid-premium": "9.x",
134
+ "@mui/x-date-pickers-pro": "9.x",
135
+ "@tanstack/query-db-collection": "1.x",
136
+ "@tanstack/react-db": "0.x",
137
+ "@tanstack/react-form": "1.x",
138
+ "@tanstack/react-query": "5.x",
139
+ "@tanstack/react-router": "1.x",
140
+ "@tanstack/react-router-ssr-query": "1.x",
141
+ "@tanstack/react-start": "1.x",
142
+ "axios": "1.x",
143
+ "dayjs": "1.x",
144
+ "react": "19.x",
145
+ "react-dom": "19.x",
146
+ "vite": "npm:@voidzero-dev/vite-plus-core@latest",
147
+ "vite-plus": "latest",
148
+ "zod": "4.x"
149
+ },
150
+ "overrides": {
151
+ "vite": "npm:@voidzero-dev/vite-plus-core@latest"
152
+ },
153
+ "packageManager": "npm@11.14.1"
154
+ }
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: client-db
3
+ description: Querying data on client by creating reactive TanStack DB collections.
4
+ ---
5
+
6
+ ## Rules
7
+
8
+ - Use `eager` syncMode for top-level collections, `on-demand` for child/relational data.
9
+ - Use `useLiveQuery` on list pages (provides `isLoading` for DataGrid), `useLiveSuspenseQuery` on detail/edit pages or components (wrap in `<Suspense>` with loading fallback).
10
+ - For mutations use `createOptimisticAction` — mirror the server mutation in `onMutate`, then call the server function and `collection.utils.refetch()` in `mutationFn`.
11
+ - Use `meta.loadSubsetOptions` for relational subset loading.
12
+ - Use Axios with auth interceptor `getAccessToken` but only for public REST APIs; use default `api` as a scope key.
13
+
14
+ ## File Placement
15
+
16
+ ```
17
+ src/db-collections/ — DB collections
18
+ src/server/actions/ — server functions (select/update/insert/delete)
19
+ src/lib/schemas/ — Zod schemas
20
+ src/lib/auth/scopes.ts — API scope keys
21
+ wcz-layout/data — shared data utilities and server functions from npm package
22
+ wcz-layout/utils — shared utils from npm package
23
+ ```
24
+
25
+ ## Examples
26
+
27
+ ```ts
28
+ // imports
29
+ import { queryClient } from "wcz-layout/data";
30
+ import { getAccessToken } from "wcz-layout/utils";
31
+
32
+ // src/db-collections/library.ts
33
+ export const api = axios.create({
34
+ baseURL: "/api/libraries",
35
+ });
36
+
37
+ api.interceptors.request.use(async (config) => {
38
+ const accessToken = await getAccessToken("api");
39
+ config.headers.set("Authorization", `Bearer ${accessToken}`);
40
+ return config;
41
+ });
42
+
43
+ export const librariesCollection = createCollection(
44
+ queryCollectionOptions({
45
+ queryKey: ["libraries"],
46
+ queryFn: () => selectLibraries(),
47
+ getKey: ({ id }) => id,
48
+ schema: LibrarySchema,
49
+ queryClient: queryClient,
50
+ syncMode: "eager",
51
+ }),
52
+ );
53
+
54
+ // src/db-collections/book.ts
55
+ export const bookCollection = createCollection(
56
+ queryCollectionOptions({
57
+ queryKey: ["books"],
58
+ queryFn: ({ meta }) => selectBooks({ data: meta?.loadSubsetOptions }),
59
+ getKey: ({ id }) => id,
60
+ schema: BookSchema,
61
+ queryClient: queryClient,
62
+ syncMode: "on-demand",
63
+ }),
64
+ );
65
+
66
+ // src/routes/libraries/edit/$id.tsx
67
+ const { data, isLoading } = useLiveQuery((q) =>
68
+ q.from({ library: libraryCollection }).orderBy(({ library }) => library.name, "asc"),
69
+ );
70
+
71
+ const { data } = useLiveQuery((q) =>
72
+ q
73
+ .from({ library: libraryCollection })
74
+ .where(({ library }) => eq(library.id, id))
75
+ .findOne()
76
+ .select(({ library }) => ({
77
+ ...library,
78
+ books: toArray(
79
+ q
80
+ .from({ book: bookCollection })
81
+ .where(({ book }) => eq(book.libraryId, library.id))
82
+ .orderBy(({ book }) => book.title, "asc")
83
+ .select(({ book }) => ({
84
+ id: book.id,
85
+ title: book.title,
86
+ })),
87
+ ),
88
+ })),
89
+ );
90
+
91
+ const handleOnSubmit = createOptimisticAction<Library>({
92
+ onMutate: (formValues) => {
93
+ libraryCollection.update(id, (prev) => Object.assign(prev, formValues));
94
+ },
95
+ mutationFn: async (formValues) => {
96
+ await updateLibrary({ data: formValues });
97
+ await libraryCollection.utils.refetch();
98
+ },
99
+ });
100
+
101
+ try {
102
+ const transaction = handleOnSubmit(formValues);
103
+ await transaction.isPersisted.promise;
104
+ } catch (error) {
105
+ if (error instanceof Error) alert(error.message);
106
+ }
107
+ ```
108
+
109
+ ## Next Step (ask user after completion)
110
+
111
+ - Create a new Route
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: db-schema
3
+ description: Define PostgreSQL database schemas with Drizzle ORM.
4
+ ---
5
+
6
+ ## Rules
7
+
8
+ - Always use `uuid` for primary keys.
9
+ - Define enums with `pgEnum`; they can be shared across schema files.
10
+ - Omit column name strings; Drizzle auto-maps camelCase to snake_case via `casing: "snake_case"`.
11
+ - Use `onDelete: "cascade"` on foreign keys for child/dependent tables.
12
+ - Define relations into `relations.ts` file.
13
+ - Derive form schemas with `.extend()` from the base schema; use `.transform()` to set parent IDs on children.
14
+ - Always add `withTimezone: true` to timestamp columns.
15
+ - Generate Zod schemas with `createSelectSchema`; override fields with stricter rules (trim, min/max) where needed.
16
+ - Don't auto-generate migrations.
17
+
18
+ ## File Placement
19
+
20
+ ```
21
+ src/server/db/migrations/ — migrations
22
+ src/server/db/schemas/ — tables, enums, relations
23
+ src/lib/schemas/ — Zod schemas
24
+ wcz-layout/utils — shared utils from npm package
25
+ ```
26
+
27
+ ## Examples
28
+
29
+ ```ts
30
+ // imports
31
+ import { t } from "wcz-layout/utils";
32
+
33
+ // src/server/db/schemas/book.ts
34
+ export const bookTable = pgTable("books", {
35
+ id: uuid().primaryKey(),
36
+ title: text().notNull(),
37
+ libraryId: uuid()
38
+ .notNull()
39
+ .references(() => libraryTable.id, { onDelete: "cascade" }),
40
+ });
41
+
42
+ // src/server/db/schemas/relations.ts
43
+ export const relations = defineRelations(
44
+ { libraryTable, bookTable },
45
+ ({ one, many, libraryTable: library, bookTable: book }) => ({
46
+ libraryTable: {
47
+ books: many.bookTable(),
48
+ },
49
+ bookTable: {
50
+ library: one.libraryTable({ from: book.libraryId, to: library.id }),
51
+ },
52
+ }),
53
+ );
54
+
55
+ // src/lib/schemas/book.ts
56
+ export const BookSchema = createSelectSchema(bookTable, {
57
+ title: (schema) =>
58
+ schema
59
+ .trim()
60
+ .min(1, t("Validation.Required"))
61
+ .max(255, t("Validation.MaxLength", { length: 255 })),
62
+ });
63
+
64
+ export type Book = z.infer<typeof BookSchema>;
65
+ ```
66
+
67
+ ## Next Step (ask user after completion)
68
+
69
+ - Create REST API routes or server functions with TanStack Start/Router to query these tables.
@@ -0,0 +1,85 @@
1
+ ---
2
+ name: forms
3
+ description: Best practices for building forms including validation and field types.
4
+ ---
5
+
6
+ ## Rules
7
+
8
+ - Always use `useLayoutForm` with pre-defined components.
9
+ - Define `width` for all form fields based on the expected content length.
10
+ - Derive Zod schemas from Drizzle table schemas using `createSelectSchema` with `t()` for validation messages.
11
+
12
+ ## File Placement
13
+
14
+ ```
15
+ src/routes/features/-components — Form components scoped to a feature
16
+ src/lib/schemas/ — Zod schemas
17
+ wcz-layout/hooks — shared hooks from npm package
18
+ ```
19
+
20
+ ## Examples
21
+
22
+ ```ts
23
+ // Imports
24
+ import { useLayoutForm } from "wcz-layout/hooks";
25
+ import { LibrarySchema } from "~/lib/schemas/library";
26
+
27
+ // Form Component
28
+ interface FormProps {
29
+ defaultValues: Library;
30
+ onSubmit: (value: Library) => Promise<void>;
31
+ }
32
+
33
+ export const Form: FC<FormProps> = ({ defaultValues, onSubmit }) => {
34
+ const { t } = useTranslation();
35
+
36
+ const form = useLayoutForm({
37
+ defaultValues,
38
+ validators: { onChange: LibrarySchema },
39
+ onSubmit: async ({ value, formApi }) => {
40
+ await onSubmit(value);
41
+ formApi.reset();
42
+ },
43
+ });
44
+
45
+ return (
46
+ <form
47
+ onSubmit={(event) => {
48
+ event.preventDefault();
49
+ event.stopPropagation();
50
+ form.handleSubmit();
51
+ }}
52
+ >
53
+ {/* some styling */}
54
+ <form.AppField name="name">
55
+ {(field) => <field.TextField label={t("Library.Name")} required sx={{ width: 420 }} />}
56
+ </form.AppField>
57
+ {/* some styling */}
58
+ <form.AppForm>
59
+ <form.SubmitButton variant="contained">{t("Submit")}</form.SubmitButton>
60
+ </form.AppForm>
61
+ </form>
62
+ );
63
+ };
64
+
65
+ // Autocomplete
66
+ <field.Autocomplete
67
+ options={options}
68
+ sx={{ width: 250 }}
69
+ autoHighlight
70
+ autoSelect
71
+ autoComplete
72
+ loading={isLoading}
73
+ textFieldProps={{
74
+ label: t("Customer"),
75
+ required: true,
76
+ }}
77
+ />
78
+
79
+ // Checkbox
80
+ <field.Checkbox label={t("IsThisTrue")} />
81
+ ```
82
+
83
+ ## Field Types
84
+
85
+ `Autocomplete` · `Checkbox` · `DatePicker` · `DateRangePicker` · `DateTimePicker` · `DateTimeRangePicker` · `NumberField` · `RadioGroup` · `Slider` · `SubmitButton` · `Switch` · `TextField` · `TimePicker` · `TimeRangePicker`