1
2// Standard path pattern
3{apps,libs}/*/lib/[model]/[Model].Template.tsx
4
5// Examples:
6libs/game/lib/map/Map.Template.tsx
7apps/lu/lib/feeling/Feeling.Template.tsx
8
1
2// Single-file pattern
3export const General = () => { /* main form */ }
4export const Advanced = () => { /* advanced fields */ }
5
6// Multi-file pattern (recommended)
7[Model].Template/
8├─ General.tsx
9├─ Sections/
10│ ├─ BasicInfo.tsx
11│ ├─ ContactDetails.tsx
12
1
2useEffect(() => {
3 if (id) {
4 // Load existing data
5 void st.do.load[Model](id);
6 }
7
8 return () => {
9 // Reset form on unmount
10 st.do.reset[Model]Form();
11 };
12}, [id]);
13
field | Description | Example |
---|---|---|
Text | Standard text input field |
|
TextArea | Multi-line text input |
|
Select | Dropdown selection input |
|
ToggleSelect | Toggleable options selector |
|
Img | Image upload component |
|
Parent | Relationship selector |
|
Standard text input field
1<Field.Text />
Multi-line text input
1<Field.TextArea />
Dropdown selection input
1<Field.Select />
Toggleable options selector
1<Field.ToggleSelect />
Image upload component
1<Field.Img sliceName='model' />
Relationship selector
1<Field.Parent sliceName='relatedModel' />
1
2<Field.List
3 label={l.field("map", "spawnPositions")}
4 value={form.spawnPositions}
5 onAdd={() => st.do.addSpawnPosition(defaultPosition)}
6 renderItem={(position, idx) => (
7 <div className="flex gap-4">
8 <Field.Text
9 value={position.key}
10 onChange={(v) => st.do.writeOnMap(["spawnPositions", idx, "key"], v)}
11 />
12 <Field.DoubleNumber
13 value={position.position}
14 onChange={(v) => st.do.writeOnMap(["spawnPositions", idx, "position"], v)}
15 />
16 </div>
17 )}
18/>
19
1
2<Field.Email
3 value={form.email}
4 validate={(v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)}
5 errorMessage="Invalid email format"
6 required
7/>
8
Option | Type | Default | Description | Example |
---|---|---|---|---|
required | boolean | false | Makes field mandatory |
|
min/max | number | - | Number range constraints |
|
validate | function | - | Custom validation function |
|
errorMessage | string | - | Custom error message |
|
Makes field mandatory
1required
Number range constraints
1min={0} max={100}
Custom validation function
1validate={(v) => isValidEmail(v)}
Custom error message
1errorMessage="Invalid format"
1
2<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
3 <Field.Text label="First Name" />
4 <Field.Text label="Last Name" />
5 <Field.Email label="Email" className="md:col-span-2" />
6</div>
7
1
2{form.type === "business" && (
3 <section className="mt-6 p-4 bg-base-200 rounded-lg">
4 <h3 className="text-lg font-semibold mb-3">Business Details</h3>
5 <Field.Text label="Company Name" />
6 <Field.Text label="Tax ID" />
7 </section>
8)}
9
1
2// Simple field update
3st.do.setNameOn[Model](value);
4
5// Nested field update
6st.do.writeOn[Model](["address", "city"], value);
7
8// List operations
9st.do.addItemOn[Model](newItem);
10st.do.removeItemOn[Model](index);
11
1
2const FormSection = React.memo(({ fields }) => {
3 // Component implementation
4});
5
6// Debounced inputs
7import { useDebounce } from "@akanjs/hooks";
8const [input, setInput] = useState("");
9const debouncedInput = useDebounce(input, 300);
10
1
2// Proper labeling
3<label htmlFor="email-field">Email</label>
4<input id="email-field" type="email" />
5
6// Error messaging
7<div role="alert" aria-live="polite">
8 {error && <p className="text-error">{error}</p>}
9</div>
10
1
2// AddressSection.tsx
3export const AddressSection = () => (
4 <div className="space-y-3">
5 <Field.Text label="Street" />
6 <Field.Text label="City" />
7 <Field.Text label="Zip Code" />
8 </div>
9);
10
1
2const handleSubmit = async () => {
3 try {
4 // Validate form
5 const errors = validateForm(form);
6 if (Object.keys(errors).length) throw errors;
7
8 // Submit data
9 if (id) await st.do.updateModel(id, form);
10 else await st.do.createModel(form);
11
12 // Redirect on success
13 router.push("/success");
14 } catch (error) {
15 // Handle API errors
16 st.do.setFormErrors(error);
17 }
18};
19
issue | Description | Example |
---|---|---|
Form Not Updating | Verify Zustand store actions are properly connected |
|
Type Mismatches | Ensure constant types match form state |
|
Missing Translations | Verify dictionary keys and scopes |
|
Verify Zustand store actions are properly connected
1st.do.setNameOn[Model]
Ensure constant types match form state
1ProjectInput vs ProjectForm
Verify dictionary keys and scopes
1l.field("model", "name")