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