feat: schema-driven form descriptor API and dynamic form rendering

- Add ui section to kindred-rd.yaml with category_picker (multi-stage),
  item_fields, field_groups, category_field_groups, and field_overrides
- Add UIConfig structs to Go schema parser with full YAML/JSON tags
- Add ValidateUI() to validate field references against property schemas
- Add ValuesByDomain() helper to auto-derive subcategory picker stages
- Implement GET /api/schemas/{name}/form endpoint that returns resolved
  form descriptor with field metadata, widget hints, and category picker
- Replace GET /api/schemas/{name}/properties route with /form
- Add FormDescriptor TypeScript types
- Create useFormDescriptor hook (replaces useCategories)
- Rewrite CreateItemPane to render all sections dynamically from descriptor
- Update CategoryPicker with multi-stage domain/subcategory selection
- Delete useCategories.ts (superseded by useFormDescriptor)
This commit is contained in:
2026-02-11 10:14:00 -06:00
parent b3c748ef10
commit 4edaa35c49
9 changed files with 1084 additions and 193 deletions

View File

@@ -248,6 +248,68 @@ export interface PropertyDef {
export type PropertySchema = Record<string, PropertyDef>;
// Form Descriptor (from GET /api/schemas/{name}/form)
export interface FormFieldDescriptor {
name: string;
type: string;
widget?: string;
label: string;
required?: boolean;
default?: unknown;
unit?: string;
description?: string;
options?: string[];
currency?: string;
derived_from_category?: Record<string, string>;
search_endpoint?: string;
}
export interface FormFieldGroup {
key: string;
label: string;
order: number;
fields: FormFieldDescriptor[];
}
export interface CategoryPickerStage {
name: string;
label: string;
values?: Record<string, string>;
values_by_domain?: Record<string, Record<string, string>>;
}
export interface CategoryPickerDescriptor {
style: string;
stages: CategoryPickerStage[];
}
export interface ItemFieldDef {
type: string;
widget: string;
label: string;
required?: boolean;
default?: unknown;
options?: string[];
derived_from_category?: Record<string, string>;
search_endpoint?: string;
}
export interface FieldOverride {
widget?: string;
currency?: string;
options?: string[];
}
export interface FormDescriptor {
schema_name: string;
format: string;
category_picker?: CategoryPickerDescriptor;
item_fields?: Record<string, ItemFieldDef>;
field_groups?: FormFieldGroup[];
category_field_groups?: Record<string, FormFieldGroup[]>;
field_overrides?: Record<string, FieldOverride>;
}
// API Token
export interface ApiToken {
id: string;