feat(web): /fields two-pane screen (grouped list + create form) + nav (no stubs left)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import type { components } from "../api/schema";
|
||||
import { useFieldDefinitions } from "../api/queries";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
|
||||
type FieldDefinitionView = components["schemas"]["FieldDefinitionView"];
|
||||
|
||||
function labelText(labels: FieldDefinitionView["labels"], lang: string): string {
|
||||
return (
|
||||
labels.find((l) => l.lang === lang)?.label ??
|
||||
labels.find((l) => l.lang === "en")?.label ??
|
||||
labels[0]?.label ??
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
export function FieldList() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { data, isLoading, isError } = useFieldDefinitions();
|
||||
const lang = i18n.language.startsWith("sv") ? "sv" : "en";
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="space-y-2 p-3">
|
||||
{Array.from({ length: 6 }).map((_, i) => (
|
||||
<Skeleton key={i} className="h-9 w-full" />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) return <p className="p-4 text-sm text-red-600">{t("fields.loadError")}</p>;
|
||||
if (!data || data.length === 0)
|
||||
return <p className="p-4 text-sm text-neutral-500">{t("fields.empty")}</p>;
|
||||
|
||||
const groups = new Map<string, FieldDefinitionView[]>();
|
||||
|
||||
for (const def of data) {
|
||||
const key = def.group?.trim() ? def.group : t("fields.other");
|
||||
const bucket = groups.get(key) ?? [];
|
||||
|
||||
bucket.push(def);
|
||||
groups.set(key, bucket);
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className="overflow-auto">
|
||||
{[...groups.entries()].map(([group, defs]) => (
|
||||
<li key={group}>
|
||||
<div className="border-b bg-neutral-50 px-3 py-1 text-xs font-medium uppercase tracking-wide text-neutral-500">
|
||||
{group}
|
||||
</div>
|
||||
<ul>
|
||||
{defs.map((def) => (
|
||||
<li key={def.key} className="flex items-center gap-2 border-b px-3 py-2 text-sm">
|
||||
<span className="font-medium">{labelText(def.labels, lang)}</span>
|
||||
<span className="text-xs text-neutral-400">{def.key}</span>
|
||||
<span className="rounded bg-neutral-100 px-1.5 py-0.5 text-xs text-neutral-600">
|
||||
{t(`fields.types.${def.data_type}`)}
|
||||
</span>
|
||||
{def.required && <span className="text-xs text-red-600">*</span>}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user