Files
silo/web/src/hooks/useFormDescriptor.ts
forbes-0023 4edaa35c49 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)
2026-02-11 10:14:00 -06:00

38 lines
1.2 KiB
TypeScript

import { useState, useEffect } from "react";
import { get } from "../api/client";
import type { FormDescriptor } from "../api/types";
// Module-level cache to avoid refetching across mounts.
let cached: FormDescriptor | null = null;
export function useFormDescriptor(schemaName = "kindred-rd") {
const [descriptor, setDescriptor] = useState<FormDescriptor | null>(cached);
const [loading, setLoading] = useState(cached === null);
useEffect(() => {
if (cached) return;
get<FormDescriptor>(`/api/schemas/${encodeURIComponent(schemaName)}/form`)
.then((desc) => {
cached = desc;
setDescriptor(desc);
})
.catch(() => {})
.finally(() => setLoading(false));
}, [schemaName]);
// Derive flat categories map from the category_picker stages
const categories: Record<string, string> = {};
if (descriptor?.category_picker) {
const subcatStage = descriptor.category_picker.stages.find(
(s) => s.values_by_domain,
);
if (subcatStage?.values_by_domain) {
for (const domainVals of Object.values(subcatStage.values_by_domain)) {
Object.assign(categories, domainVals);
}
}
}
return { descriptor, categories, loading };
}