@akanjs/cli 0.0.146 → 0.0.147

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 (28) hide show
  1. package/README.md +7 -26
  2. package/cjs/index.js +175 -18
  3. package/cjs/src/guidelines/cssRule/cssRule.instruction.md +1 -1
  4. package/cjs/src/guidelines/docPageRule/docPageRule.instruction.md +59 -52
  5. package/cjs/src/guidelines/modelConstant/modelConstant.instruction.md +335 -752
  6. package/cjs/src/guidelines/modelTemplate/modelTemplate.instruction.md +418 -391
  7. package/cjs/src/guidelines/modelUnit/modelUnit.instruction.md +0 -292
  8. package/cjs/src/guidelines/scalarModule/scalarModule.instruction.md +84 -0
  9. package/cjs/src/templates/app/main.js +1 -2
  10. package/esm/index.js +183 -26
  11. package/esm/src/guidelines/cssRule/cssRule.instruction.md +1 -1
  12. package/esm/src/guidelines/docPageRule/docPageRule.instruction.md +59 -52
  13. package/esm/src/guidelines/modelConstant/modelConstant.instruction.md +335 -752
  14. package/esm/src/guidelines/modelTemplate/modelTemplate.instruction.md +418 -391
  15. package/esm/src/guidelines/modelUnit/modelUnit.instruction.md +0 -292
  16. package/esm/src/guidelines/scalarModule/scalarModule.instruction.md +84 -0
  17. package/esm/src/templates/app/main.js +1 -2
  18. package/package.json +1 -1
  19. package/src/guideline/guideline.command.d.ts +3 -1
  20. package/src/guideline/guideline.prompt.d.ts +15 -1
  21. package/src/guideline/guideline.runner.d.ts +17 -3
  22. package/src/guideline/guideline.script.d.ts +8 -2
  23. package/src/guidelines/cssRule/cssRule.instruction.md +1 -1
  24. package/src/guidelines/docPageRule/docPageRule.instruction.md +59 -52
  25. package/src/guidelines/modelConstant/modelConstant.instruction.md +335 -752
  26. package/src/guidelines/modelTemplate/modelTemplate.instruction.md +418 -391
  27. package/src/guidelines/modelUnit/modelUnit.instruction.md +0 -292
  28. package/src/guidelines/scalarModule/scalarModule.instruction.md +84 -0
@@ -10,568 +10,595 @@ Model.Template.tsx files are client-side React components that define the form s
10
10
  4. **UI Consistency**: Ensure uniform form design across the application
11
11
  5. **Type Safety**: Enforce data types based on Model.Constant definitions
12
12
  6. **Internationalization**: Support multi-language form fields with dictionary integration
13
+ 7. **Component Reusability**: Enable form sections to be used in multiple contexts (pages, modals, utilities)
13
14
 
14
15
  ## File Location and Naming Convention
15
16
 
17
+ ### Standard Path
18
+
16
19
  ```
17
- libs/[domain]/lib/[model]/[Model].Template.tsx
20
+ {apps,libs}/*/lib/[model]/[Model].Template.tsx
18
21
  ```
19
22
 
20
- For example:
23
+ ### Examples:
21
24
 
22
- - `libs/shared/lib/admin/Admin.Template.tsx`
23
- - `apps/akamir/lib/rewardRequest/RewardRequest.Template.tsx`
25
+ - `libs/game/lib/map/Map.Template.tsx`
26
+ - `apps/lu/lib/feeling/Feeling.Template.tsx`
27
+ - `libs/shared/lib/user/User.Template.tsx`
24
28
 
25
- The file should export one or more named components, typically including:
29
+ ### Naming Rules:
26
30
 
27
- - `General`: Main form template used in most scenarios
28
- - `Advanced`: Optional complex fields for advanced forms
29
- - `Compact`: Simplified version for inline editing or mobile views
31
+ - PascalCase for model names (e.g., `Map.Template.tsx`)
32
+ - Place in the same directory as other model files (`[Model].store.ts`, `[Model].constant.ts`)
33
+ - Export named components (typically `General`, `Advanced`, `Compact`)
30
34
 
31
- ## Creating Model.Template.tsx Files
35
+ ## Basic Structure and Component Patterns
32
36
 
33
- ### Basic Structure
37
+ ### Minimum Structure
34
38
 
35
39
  ```tsx
36
- // File: libs/[domain]/lib/[model]/[Model].Template.tsx
37
40
  "use client";
38
- import { Field } from "@akanjs/client";
39
- import { cnst } from "@[project]/constant";
41
+ import { Field } from "@shared/ui";
40
42
  import { st } from "@[project]/client";
41
43
  import { usePage } from "@[project]/lib/usePage";
42
44
 
43
45
  export const General = ({ id }: { id?: string }) => {
44
- const form = st.use.[model]Form(); // e.g., st.use.adminForm()
46
+ const form = st.use.[model]Form();
45
47
  const { l } = usePage();
46
48
 
47
49
  return (
48
- <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
50
+ <div className="grid grid-cols-1 gap-4">
49
51
  <Field.Text
50
- label={l.field.name}
51
- value={form.name}
52
- onChange={(v) => st.do.setNameOn[Model](v)}
53
- required
54
- maxLength={30}
55
- />
56
-
57
- <Field.Select
58
- label={l.field.status}
59
- value={form.status}
60
- options={cnst.[Model]Status.options}
61
- onChange={(v) => st.do.setStatusOn[Model](v)}
52
+ label={l.field("model", "fieldName")}
53
+ value={form.fieldName}
54
+ onChange={(v) => st.do.setFieldNameOn[Model](v)}
62
55
  />
63
-
64
- {/* Add other fields */}
65
56
  </div>
66
57
  );
67
58
  };
68
59
  ```
69
60
 
70
- ### Integration with Store State
71
-
72
- Model.Template components connect to Zustand store slices that follow a consistent pattern:
61
+ ### Component Organization Patterns
73
62
 
74
63
  ```tsx
75
- // Form state (defined in model.store.ts)
76
- interface [Model]State {
77
- [model]Form: cnst.[Model]Input;
78
- // other state properties...
79
- }
64
+ // Single-file pattern
65
+ export const General = () => { /* main form */ }
66
+ export const Advanced = () => { /* advanced fields */ }
67
+
68
+ // Multi-file pattern (recommended)
69
+ [Model].Template/
70
+ ├─ General.tsx
71
+ ├─ Sections/
72
+ │ ├─ BasicInfo.tsx
73
+ │ ├─ ContactDetails.tsx
74
+ ```
80
75
 
81
- // Form actions (defined in model.store.ts)
82
- interface [Model]Action {
83
- setNameOn[Model]: (name: string) => void;
84
- setStatusOn[Model]: (status: cnst.[Model]Status) => void;
85
- // other actions...
86
- }
76
+ ## Integration with Store State Management
87
77
 
88
- // In Template component
78
+ ### Connecting to Zustand Store
79
+
80
+ ```tsx
81
+ // Access form state
89
82
  const form = st.use.[model]Form();
90
- const { setNameOn[Model] } = st.do;
83
+
84
+ // Update fields
85
+ <Field.Text
86
+ value={form.name}
87
+ onChange={st.do.setNameOn[Model]}
88
+ />
89
+ ```
90
+
91
+ ### Store Action Patterns
92
+
93
+ ```tsx
94
+ // Simple field update
95
+ st.do.setNameOn[Model](value);
96
+
97
+ // Nested field update
98
+ st.do.writeOn[Model](["address", "city"], value);
99
+
100
+ // List operations
101
+ st.do.addItemOn[Model](newItem);
102
+ st.do.removeItemOn[Model](index);
91
103
  ```
92
104
 
93
- ### Field Types and Components
105
+ ### Initialization and Cleanup
94
106
 
95
- Akan.js provides a comprehensive set of form field components in the `Field` namespace:
107
+ ```tsx
108
+ useEffect(() => {
109
+ if (id) {
110
+ // Load existing data
111
+ void st.do.load[Model](id);
112
+ }
113
+
114
+ return () => {
115
+ // Reset form on unmount
116
+ st.do.reset[Model]Form();
117
+ };
118
+ }, [id]);
119
+ ```
120
+
121
+ ## Field Types and Form Components
122
+
123
+ ### Core Field Components
124
+
125
+ | Component | Usage | Example |
126
+ | -------------------- | --------------------- | ------------------------------------------- |
127
+ | `Field.Text` | Text input | `<Field.Text value={form.name} />` |
128
+ | `Field.TextArea` | Multi-line text | `<Field.TextArea value={form.desc} />` |
129
+ | `Field.Number` | Numeric input | `<Field.Number value={form.age} />` |
130
+ | `Field.Select` | Dropdown selection | `<Field.Select options={statusOptions} />` |
131
+ | `Field.ToggleSelect` | Toggleable options | `<Field.ToggleSelect items={types} />` |
132
+ | `Field.Tags` | Tag input | `<Field.Tags value={form.tags} />` |
133
+ | `Field.Img` | Image upload | `<Field.Img sliceName="model" />` |
134
+ | `Field.Date` | Date picker | `<Field.Date value={form.dueDate} />` |
135
+ | `Field.Parent` | Relationship selector | `<Field.Parent sliceName="relatedModel" />` |
136
+ | `Field.Slate` | Rich text editor | `<Field.Slate valuePath="content" />` |
137
+
138
+ ### Complex Field Example
96
139
 
97
140
  ```tsx
98
- <Field.Text /> // Text input
99
- <Field.Textarea /> // Multi-line text
100
- <Field.Number /> // Numeric input
101
- <Field.Select /> // Dropdown selection
102
- <Field.Checkbox /> // Toggle input
103
- <Field.Date /> // Date picker
104
- <Field.File /> // File upload
105
- <Field.Image /> // Image upload with preview
106
- <Field.Radio /> // Radio button group
107
- <Field.Switch /> // Toggle switch
108
- <Field.Tags /> // Tag input
109
- <Field.Time /> // Time picker
110
- <Field.Color /> // Color picker
111
- <Field.Autocomplete /> // Text input with suggestions
112
- <Field.MultiSelect /> // Multi-value selection
113
- <Field.RichText /> // Rich text editor
114
- <Field.CodeEditor /> // Code editor
115
- <Field.Location /> // Map location picker
116
- <Field.Slider /> // Numeric range slider
117
- <Field.Password /> // Password input with visibility toggle
141
+ <Field.List
142
+ label={l.field("map", "spawnPositions")}
143
+ value={form.spawnPositions}
144
+ onAdd={() => st.do.addSpawnPosition(defaultPosition)}
145
+ renderItem={(position, idx) => (
146
+ <div className="flex gap-4">
147
+ <Field.Text value={position.key} onChange={(v) => st.do.writeOnMap(["spawnPositions", idx, "key"], v)} />
148
+ <Field.DoubleNumber
149
+ value={position.position}
150
+ onChange={(v) => st.do.writeOnMap(["spawnPositions", idx, "position"], v)}
151
+ />
152
+ </div>
153
+ )}
154
+ />
118
155
  ```
119
156
 
120
- ### Form Validation
157
+ ## Form Validation Approaches
121
158
 
122
- Field components support validation through props:
159
+ ### Field-Level Validation
123
160
 
124
161
  ```tsx
125
- <Field.Text
126
- label="Email"
162
+ <Field.Email
127
163
  value={form.email}
128
- onChange={(v) => st.do.setEmailOnUser(v)}
129
164
  validate={(v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)}
130
165
  errorMessage="Invalid email format"
131
166
  required
132
- maxLength={50}
133
167
  />
134
168
  ```
135
169
 
136
- Common validation options:
170
+ ### Custom Validation Functions
171
+
172
+ ```tsx
173
+ // Phone validation
174
+ <Field.Phone
175
+ value={form.phone}
176
+ validate={(v) => isPhoneNumber(v)}
177
+ />
178
+
179
+ // Custom business rule
180
+ <Field.Number
181
+ value={form.quantity}
182
+ validate={(v) => v > 0 ? true : "Must be positive"}
183
+ />
184
+ ```
185
+
186
+ ### Validation Props
137
187
 
138
- - `required`: Makes the field required
139
- - `maxLength`: Maximum character length
140
- - `minLength`: Minimum character length
141
- - `min`/`max`: Number range limits
142
- - `pattern`: Regex pattern for validation
143
- - `validate`: Custom validation function
144
- - `errorMessage`: Custom error message
188
+ | Prop | Description | Example |
189
+ | ----------------------- | -------------------- | ------------------------------- |
190
+ | `required` | Mandatory field | `required` |
191
+ | `min`/`max` | Number range | `min={0} max={100}` |
192
+ | `minLength`/`maxLength` | Text length | `minLength={2}` |
193
+ | `validate` | Custom validation fn | `validate={(v) => isValid(v)}` |
194
+ | `errorMessage` | Custom error text | `errorMessage="Invalid format"` |
145
195
 
146
- ### Internationalization
196
+ ## Internationalization with usePage
147
197
 
148
- Templates should use the `usePage()` hook to access localized strings:
198
+ ### Dictionary Integration
149
199
 
150
200
  ```tsx
151
201
  const { l } = usePage();
152
202
 
203
+ // Basic field
204
+ <Field.Text label={l.field("model", "name")} />
205
+
206
+ // With description
153
207
  <Field.Text
154
- label={l.field.email}
155
- placeholder={l.desc.enterEmail}
156
- // ...
157
- />;
208
+ label={l.field("model", "email")}
209
+ desc={l.desc("model", "email")}
210
+ />
211
+
212
+ // Dynamic content
213
+ <div>{l(`profileVerify.enum-type-${type}`)}</div>
158
214
  ```
159
215
 
160
- ## Form Layout Patterns
216
+ ### Dictionary Structure
161
217
 
162
- ### Basic Grid Layout
218
+ ```tsx
219
+ // Example dictionary
220
+ export const dictionary = {
221
+ field: {
222
+ model: {
223
+ name: { en: "Name", ko: "이름" },
224
+ email: { en: "Email", ko: "이메일" },
225
+ },
226
+ },
227
+ desc: {
228
+ model: {
229
+ email: { en: "Your contact email", ko: "연락처 이메일" },
230
+ },
231
+ },
232
+ };
233
+ ```
234
+
235
+ ## Form Layout Patterns and Best Practices
236
+
237
+ ### Responsive Grid Layout
163
238
 
164
239
  ```tsx
165
240
  <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
166
- <Field.Text label="First Name" /* ... */ />
167
- <Field.Text label="Last Name" /* ... */ />
168
- <Field.Email label="Email" className="md:col-span-2" /* ... */ />
241
+ <Field.Text label="First Name" />
242
+ <Field.Text label="Last Name" />
243
+ <Field.Email label="Email" className="md:col-span-2" />
169
244
  </div>
170
245
  ```
171
246
 
172
- ### Grouped Fields
247
+ ### Section Grouping
173
248
 
174
249
  ```tsx
175
- <div className="space-y-6">
176
- <fieldset className="rounded-md border p-4">
177
- <legend className="text-lg font-medium">{l.section.personalInfo}</legend>
178
- <div className="grid grid-cols-1 gap-4 md:grid-cols-2">{/* Personal info fields */}</div>
179
- </fieldset>
180
-
181
- <fieldset className="rounded-md border p-4">
182
- <legend className="text-lg font-medium">{l.section.contactInfo}</legend>
183
- <div className="grid grid-cols-1 gap-4 md:grid-cols-2">{/* Contact info fields */}</div>
184
- </fieldset>
185
- </div>
250
+ <section className="mb-6">
251
+ <h3 className="mb-3 text-xl font-semibold">Personal Information</h3>
252
+ <div className="space-y-4">
253
+ <Field.Text label="Full Name" />
254
+ <Field.Date label="Birth Date" />
255
+ </div>
256
+ </section>
186
257
  ```
187
258
 
188
- ### Conditional Fields
259
+ ### Conditional Sections
189
260
 
190
261
  ```tsx
191
262
  {
192
263
  form.type === "business" && (
193
- <>
194
- <Field.Text label="Company Name" /* ... */ />
195
- <Field.Text label="Tax ID" /* ... */ />
196
- </>
264
+ <section>
265
+ <h3>Business Details</h3>
266
+ <Field.Text label="Company Name" />
267
+ <Field.Text label="Tax ID" />
268
+ </section>
197
269
  );
198
270
  }
199
271
  ```
200
272
 
201
- ### Nested Components
273
+ ### Best Practices
202
274
 
203
- Break complex forms into nested components for better organization:
204
-
205
- ```tsx
206
- export const General = ({ id }: { id?: string }) => {
207
- // Main form logic
208
- return (
209
- <div className="space-y-6">
210
- <BasicInfo />
211
- <ContactDetails />
212
- <Preferences />
213
- </div>
214
- );
215
- };
216
-
217
- const BasicInfo = () => {
218
- const form = st.use.userForm();
219
- // Component implementation
220
- };
221
- ```
275
+ 1. Use consistent spacing (`gap-4`, `mb-4`)
276
+ 2. Group related fields visually
277
+ 3. Place important fields above the fold
278
+ 4. Use appropriate field types for data
279
+ 5. Provide clear validation feedback
280
+ 6. Optimize for mobile-first experiences
222
281
 
223
- ## Using Model.Template in Pages
282
+ ## Using Templates in Pages, Utilities, and Zones
224
283
 
225
- ### Creation Page
284
+ ### In Page Components
226
285
 
227
286
  ```tsx
228
- // apps/app-name/app/[lang]/[model]/new/page.tsx
229
- import { Model } from "@shared/ui";
230
- import { [Model] } from "@[project]/client";
231
-
232
- export default function NewModelPage() {
233
- return (
234
- <Model.Edit
235
- sliceName="[model]"
236
- type="form"
237
- onSubmit="/[model]"
238
- >
239
- <[Model].Template.General />
240
- </Model.Edit>
241
- );
242
- }
287
+ // Creation page
288
+ <Model.Edit sliceName="model" type="form">
289
+ <Model.Template.General />
290
+ </Model.Edit>
291
+
292
+ // Edit page
293
+ <Model.Edit modelId={params.id} sliceName="model">
294
+ <Model.Template.General id={params.id} />
295
+ </Model.Edit>
243
296
  ```
244
297
 
245
- ### Editing Page
298
+ ### In Utility Components
246
299
 
247
300
  ```tsx
248
- // apps/app-name/app/[lang]/[model]/[id]/edit/page.tsx
249
- import { Model } from "@shared/ui";
250
- import { [Model] } from "@[project]/client";
251
-
252
- export default function EditModelPage({ params }) {
253
- return (
254
- <Model.Edit
255
- modelId={params.id}
256
- sliceName="[model]"
257
- renderTitle={l => l.title.edit[model]}
258
- >
259
- <[Model].Template.General id={params.id} />
260
- </Model.Edit>
261
- );
262
- }
301
+ // Modal-based editing
302
+ <Modal.Open opens={`edit-${id}`}>
303
+ <button>Edit</button>
304
+ </Modal.Open>
305
+
306
+ <Modal.Window name={`edit-${id}`}>
307
+ <Model.Edit modelId={id} sliceName="model">
308
+ <Model.Template.Compact />
309
+ </Model.Edit>
310
+ </Modal.Window>
263
311
  ```
264
312
 
265
- ## Using Model.Template in Utility Components
266
-
267
- ### In Modal Dialogs
313
+ ### In Zone Components
268
314
 
269
315
  ```tsx
270
- // libs/[domain]/lib/[model]/[Model].Util.tsx
271
- import { Modal, Model } from "@util/ui";
272
- import { [Model] } from "@[project]/client";
316
+ // List view with create form
317
+ <Data.ListContainer
318
+ renderTemplate={() => <Model.Template.General />}
319
+ />
273
320
 
274
- export const EditButton = ({ id }: { id: string }) => (
275
- <>
276
- <Modal.Open opens={`edit-${id}`}>
277
- <button>Edit</button>
278
- </Modal.Open>
279
-
280
- <Modal.Window name={`edit-${id}`}>
281
- <Model.Edit
282
- modelId={id}
283
- sliceName="[model]"
284
- >
285
- <[Model].Template.General id={id} />
286
- </Model.Edit>
287
- </Modal.Window>
288
- </>
289
- );
321
+ // Inline editor
322
+ <Zone.InlineEdit>
323
+ <Model.Template.Compact />
324
+ </Zone.InlineEdit>
290
325
  ```
291
326
 
292
- ### In Custom Forms
327
+ ## State Management and Lifecycle Methods
328
+
329
+ ### Lifecycle Hooks
293
330
 
294
331
  ```tsx
295
- // libs/[domain]/lib/[model]/[Model].Util.tsx
296
- import { st } from "@[project]/client";
297
- import { [Model] } from "@[project]/client";
332
+ useEffect(() => {
333
+ // Initialize form data
334
+ if (id) void st.do.loadModel(id);
298
335
 
299
- export const CustomFormSection = () => {
300
- const form = st.use.[model]Form();
336
+ // Initialize related data
337
+ void st.do.initRelatedData();
301
338
 
302
- return (
303
- <div>
304
- <h3>Advanced Settings</h3>
305
- <[Model].Template.Advanced />
306
- <button onClick={st.do.submit[model]}>Save</button>
307
- </div>
308
- );
309
- };
339
+ return () => {
340
+ // Cleanup on unmount
341
+ st.do.resetModelForm();
342
+ };
343
+ }, [id]);
310
344
  ```
311
345
 
312
- ## Using Model.Template in Zone Components
346
+ ### Form State Flow
313
347
 
314
- ### List View Creation
348
+ 1. **Initialization**: Load data when component mounts
349
+ 2. **User Interaction**: Update state via store actions
350
+ 3. **Validation**: Validate on change/blur/submit
351
+ 4. **Submission**: Process form data
352
+ 5. **Cleanup**: Reset state on unmount
315
353
 
316
- ```tsx
317
- // libs/[domain]/lib/[model]/[Model].Zone.tsx
318
- import { Data } from "@shared/ui";
319
- import { [Model] } from "@[project]/client";
320
-
321
- export const AdminZone = () => (
322
- <Data.ListContainer
323
- renderTemplate={() => <[Model].Template.General />}
324
- // ...other props
325
- />
326
- );
327
- ```
354
+ ## Performance Optimization Techniques
328
355
 
329
- ### Inline Editing
356
+ ### Memoization
330
357
 
331
358
  ```tsx
332
- // libs/[domain]/lib/[model]/[Model].Zone.tsx
333
- import { Model } from "@shared/ui";
334
- import { [Model] } from "@[project]/client";
335
-
336
- export const InlineEditor = ({ id }: { id: string }) => (
337
- <Model.EditInline
338
- modelId={id}
339
- sliceName="[model]"
340
- >
341
- <[Model].Template.Compact id={id} />
342
- </Model.EditInline>
343
- );
359
+ const FormSection = React.memo(({ fields }) => {
360
+ // Component implementation
361
+ });
344
362
  ```
345
363
 
346
- ## Best Practices
347
-
348
- ### 1. Component Organization
349
-
350
- Split complex templates into logical sections:
364
+ ### Debounced Inputs
351
365
 
352
366
  ```tsx
353
- // Recommended structure
354
- [Model].Template.tsx
355
- ├─ General.tsx // Main form
356
- ├─ Advanced.tsx // Advanced fields
357
- ├─ Compact.tsx // Simplified form
358
- └─ Sections/ // Form sections
359
- ├─ Basic.tsx
360
- └─ Metadata.tsx
361
- ```
367
+ import { useDebounce } from "@akanjs/hooks";
362
368
 
363
- ### 2. State Management
369
+ const [input, setInput] = useState("");
370
+ const debouncedInput = useDebounce(input, 300);
364
371
 
365
- - Use `st.use.[model]Form()` for accessing form state
366
- - Use `st.do.set[Field]On[Model]()` for updating form fields
367
- - Perform form initialization in useEffect:
368
-
369
- ```tsx
370
372
  useEffect(() => {
371
- if (id) {
372
- void sig.[model].get[Model](id).then(data => {
373
- st.do.set[Model]Form(data.[model]);
374
- });
375
- }
376
-
377
- // Reset form on unmount
378
- return () => st.do.reset[Model]Form();
379
- }, [id]);
373
+ st.do.search(debouncedInput);
374
+ }, [debouncedInput]);
380
375
  ```
381
376
 
382
- ### 3. Validation Approaches
383
-
384
- - **Field-level validation**: Use the `validate` prop on field components
385
- - **Form-level validation**: Use custom validation logic before submission
386
- - **Server-side validation**: Always validate in the service layer as well
377
+ ### Lazy Loading
387
378
 
388
379
  ```tsx
389
- // Form-level validation example
390
- const handleSubmit = () => {
391
- const errors = validateForm(form);
392
- if (Object.keys(errors).length > 0) {
393
- st.do.setFormErrors(errors);
394
- return;
395
- }
380
+ const HeavyEditor = React.lazy(() => import("./HeavyEditor"));
396
381
 
397
- st.do.submit[Model]();
398
- };
382
+ <Suspense fallback={<Loader />}>
383
+ <HeavyEditor />
384
+ </Suspense>;
399
385
  ```
400
386
 
401
- ### 4. Performance Optimization
387
+ ## Accessibility Considerations
402
388
 
403
- - Memoize components with `React.memo` for complex forms
404
- - Use controlled components consistently
405
- - Debounce input changes for expensive operations:
389
+ ### Accessible Form Patterns
406
390
 
407
391
  ```tsx
408
- import { useDebounce } from "@[project]/lib/hooks";
409
-
410
- const debouncedValue = useDebounce(inputValue, 300);
392
+ // Proper labeling
393
+ <label htmlFor="email-field">Email</label>
394
+ <input id="email-field" type="email" />
395
+
396
+ // ARIA attributes
397
+ <div
398
+ role="alert"
399
+ aria-live="polite"
400
+ >
401
+ {error && <p>{error}</p>}
402
+ </div>
411
403
 
412
- useEffect(() => {
413
- // Perform expensive operation with debouncedValue
414
- }, [debouncedValue]);
404
+ // Keyboard navigation
405
+ <button onKeyDown={handleKeyNavigation}>Submit</button>
415
406
  ```
416
407
 
417
- ### 5. Accessibility
408
+ ### Accessibility Requirements
418
409
 
419
- - Associate labels with inputs using `htmlFor`/`id`
420
- - Provide ARIA attributes for custom components
421
- - Ensure keyboard navigation works properly
422
- - Use semantic HTML elements
410
+ 1. Associate all inputs with labels
411
+ 2. Provide ARIA attributes for custom components
412
+ 3. Ensure proper focus management
413
+ 4. Support keyboard navigation
414
+ 5. Provide error messages as live regions
415
+ 6. Use sufficient color contrast
423
416
 
424
- ```tsx
425
- <label htmlFor="name-field" className="block text-sm font-medium">
426
- Name
427
- </label>
428
- <input
429
- id="name-field"
430
- type="text"
431
- aria-describedby="name-description"
432
- // ...
433
- />
434
- <p id="name-description" className="text-xs text-gray-500">
435
- Enter your full name as it appears on your ID
436
- </p>
437
- ```
417
+ ## Common Patterns and Reusable Components
438
418
 
439
- ## Common Patterns
440
-
441
- ### Conditional Fields
419
+ ### Reusable Form Sections
442
420
 
443
421
  ```tsx
444
- {
445
- form.type === "advanced" && (
446
- <Field.Text
447
- label="Advanced Option"
448
- value={form.advancedOption}
449
- onChange={(v) => st.do.setAdvancedOptionOn[Model](v)}
450
- />
451
- );
452
- }
422
+ // AddressSection.tsx
423
+ export const AddressSection = () => (
424
+ <>
425
+ <Field.Text label="Street" />
426
+ <Field.Text label="City" />
427
+ <Field.Text label="Zip Code" />
428
+ </>
429
+ );
430
+
431
+ // Usage
432
+ <AddressSection />;
453
433
  ```
454
434
 
455
- ### Field Groups
435
+ ### Dynamic Field Arrays
456
436
 
457
437
  ```tsx
458
- const ContactSection = () => {
459
- const form = st.use.[model]Form();
460
- const { l } = usePage();
461
-
462
- return (
463
- <fieldset className="border p-4">
464
- <legend>{l.section.contactInfo}</legend>
465
- <Field.Text label={l.field.email} /* ... */ />
466
- <Field.Text label={l.field.phone} /* ... */ />
467
- </fieldset>
468
- );
469
- };
438
+ <Field.List
439
+ value={form.items}
440
+ onAdd={() => st.do.addItem(defaultItem)}
441
+ renderItem={(item, index) => (
442
+ <div key={index}>
443
+ <Field.Text value={item.name} />
444
+ <Field.Number value={item.quantity} />
445
+ </div>
446
+ )}
447
+ />
470
448
  ```
471
449
 
472
- ### Custom Field Components
450
+ ### Compound Components
473
451
 
474
452
  ```tsx
475
- const CustomField = ({ label, value, onChange }) => {
476
- const handleChange = (e) => {
477
- onChange(e.target.value);
478
- };
479
-
480
- return (
481
- <div className="custom-field">
482
- <label>{label}</label>
483
- <input value={value} onChange={handleChange} className="special-input" />
484
- </div>
485
- );
486
- };
453
+ // DockerRegistryField.tsx
454
+ const DockerRegistry = ({ value, onChange }) => (
455
+ <>
456
+ <Field.Text label="URI" value={value.uri} />
457
+ <Field.Text label="Username" value={value.username} />
458
+ <Field.Text label="Password" value={value.password} />
459
+ </>
460
+ );
487
461
  ```
488
462
 
489
463
  ## Form State Integration with Database Models
490
464
 
491
- Templates connect to state that directly corresponds to the model structure defined in `[model].constant.ts`:
465
+ ### Type Alignment
492
466
 
493
467
  ```tsx
494
- // In [model].constant.ts
495
- @Model.Input("[Model]Input")
496
- export class [Model]Input {
468
+ // Model.constant.ts
469
+ @Model.Input("ProjectInput")
470
+ export class ProjectInput {
497
471
  @Field.Prop(() => String)
498
472
  name: string;
499
-
500
- @Field.Prop(() => String)
501
- description: string;
502
473
  }
503
474
 
504
- // In [Model].Template.tsx
505
- const form = st.use.[model]Form(); // Type: [Model]Input
475
+ // Project.Template.tsx
476
+ const form = st.use.projectForm(); // Type: ProjectInput
506
477
 
507
- <Field.Text
508
- value={form.name}
509
- onChange={(v) => st.do.setNameOn[Model](v)}
510
- />
478
+ <Field.Text value={form.name} onChange={st.do.setNameOnProject} />;
511
479
  ```
512
480
 
513
- ## Troubleshooting
514
-
515
- ### Form Not Updating
481
+ ### Data Transformation
516
482
 
517
- - Verify Zustand store actions are properly connected
518
- - Check for conflicting state updates in parent components
519
- - Ensure `onChange` handlers are updating the correct store slice
520
- - Verify that the form state is being properly initialized
483
+ ```tsx
484
+ // Before submission
485
+ const handleSubmit = () => {
486
+ const dataToSend = transformFormData(form);
487
+ st.do.createProject(dataToSend);
488
+ };
521
489
 
522
- ### Validation Issues
490
+ // After load
491
+ useEffect(() => {
492
+ if (data) {
493
+ const formData = transformAPIData(data);
494
+ st.do.setProjectForm(formData);
495
+ }
496
+ }, [data]);
497
+ ```
523
498
 
524
- - Confirm validation regex patterns match server-side rules
525
- - Check field requirements in `[model].constant.ts`
526
- - Verify error messages are properly displayed
527
- - Test edge cases like empty strings and special characters
499
+ ## Troubleshooting Common Issues
528
500
 
529
- ### Performance Problems
501
+ ### Common Problems and Solutions
530
502
 
531
- - Profile re-renders with React DevTools
532
- - Implement memoization on complex components
533
- - Consider virtualizing large forms
534
- - Check for unnecessary re-renders
503
+ | Issue | Solution |
504
+ | ----------------------- | ---------------------------------------------- |
505
+ | Form not updating | Verify Zustand actions are connected correctly |
506
+ | Validation not working | Check validate prop and error handling |
507
+ | Performance issues | Memoize components, debounce inputs |
508
+ | Type mismatches | Ensure constant types match form state |
509
+ | Missing translations | Verify dictionary keys and scopes |
510
+ | Form reset issues | Check cleanup useEffect dependencies |
511
+ | API submission failures | Validate server-side requirements |
535
512
 
536
- ### Type Errors
513
+ ### Debugging Tips
537
514
 
538
- - Ensure `[model].constant.ts` types are up to date
539
- - Verify FormState interface matches `[Model]Input`
540
- - Check for correct import paths
541
- - Validate that prop types match the expected form field types
515
+ 1. Check Zustand store state with Redux DevTools
516
+ 2. Verify prop drilling in complex forms
517
+ 3. Test validation rules independently
518
+ 4. Check network requests for API errors
519
+ 5. Verify dictionary key paths with `l()` function
520
+ 6. Ensure all required fields have `required` prop
542
521
 
543
- ## Integration with API Calls
522
+ ## Integration with API Calls and Form Submission
544
523
 
545
- Templates should work with signals and stores to handle form submissions:
524
+ ### Submission Workflow
546
525
 
547
526
  ```tsx
548
- // In [model].store.ts
549
- createUser: async () => {
550
- const { userForm } = get();
527
+ const handleSubmit = async () => {
551
528
  try {
552
- const user = await sig.user.createUser({ data: userForm });
553
- set((state) => {
554
- state.users = [...state.users, user];
555
- state.userForm = {
556
- /* reset form */
557
- };
558
- });
559
- return user;
529
+ // Validate form
530
+ const errors = validateForm(form);
531
+ if (Object.keys(errors).length) throw errors;
532
+
533
+ // Submit data
534
+ if (id) {
535
+ await st.do.updateModel(id, form);
536
+ } else {
537
+ await st.do.createModel(form);
538
+ }
539
+
540
+ // Redirect on success
541
+ router.push("/success");
560
542
  } catch (error) {
561
- set((state) => {
562
- state.formErrors = error.errors || { general: error.message };
563
- });
564
- throw error;
543
+ // Handle API errors
544
+ st.do.setFormErrors(error);
565
545
  }
566
546
  };
567
547
 
568
548
  // In component
569
- const handleSubmit = async () => {
549
+ <Button onClick={handleSubmit}>Save</Button>;
550
+ ```
551
+
552
+ ### API Error Handling
553
+
554
+ ```tsx
555
+ // In store
556
+ createModel: async (form) => {
570
557
  try {
571
- await st.do.createUser();
572
- // Handle success (navigation, toast, etc.)
558
+ return await fetch.createModel(form);
573
559
  } catch (error) {
574
- // Error already handled in store
560
+ // Transform API errors to form errors
561
+ const formErrors = transformErrors(error);
562
+ set({ formErrors });
563
+ throw formErrors;
575
564
  }
576
565
  };
577
566
  ```
567
+
568
+ ### Success/Failure Feedback
569
+
570
+ ```tsx
571
+ // Using toast notifications
572
+ toast.success(l("model.createdSuccess"));
573
+
574
+ // Inline messages
575
+ {
576
+ submitError && <div className="text-error">{submitError}</div>;
577
+ }
578
+ ```
579
+
580
+ ## Additional Best Practices
581
+
582
+ ### Component Design Principles
583
+
584
+ 1. **Single Responsibility**: Each template should focus on one model
585
+ 2. **Reusability**: Design components to work in different contexts
586
+ 3. **Composition**: Build complex forms from simple components
587
+ 4. **Consistency**: Follow established patterns across all templates
588
+ 5. **Accessibility**: Meet WCAG 2.1 standards for all form elements
589
+
590
+ ### Performance Guidelines
591
+
592
+ 1. Avoid unnecessary state updates
593
+ 2. Use React.memo for expensive components
594
+ 3. Virtualize long lists of fields
595
+ 4. Debounce rapid state updates
596
+ 5. Lazy load heavy form sections
597
+
598
+ ### Testing Recommendations
599
+
600
+ 1. Unit test validation functions
601
+ 2. Test form submission flows
602
+ 3. Verify internationalization coverage
603
+ 4. Test accessibility with screen readers
604
+ 5. Perform cross-browser testing