metis-desk/modules/assets/src/web/catalog-relation-form.tsx
2026-10-08 14:14:01 +02:00

100 lines
3.3 KiB
TypeScript

import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@repo/shadcn-ui/components/button";
import { FormActions } from "@repo/shadcn-ui/components/form-dialog";
import { Input } from "@repo/shadcn-ui/components/input";
import { Label } from "@repo/shadcn-ui/components/label";
import { Textarea } from "@repo/shadcn-ui/components/textarea";
import { useId } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import type { z } from "zod/v4";
import { AssetDefinitionSchema, type CatalogItemDTO } from "../common";
import { CatalogError } from "./catalog-error";
import { CatalogHelp } from "./catalog-help";
import type { CatalogSaved } from "./use-catalog-save";
import { useCatalogSave } from "./use-catalog-save";
const schema = AssetDefinitionSchema.options[2];
export function CatalogRelationForm({
scope,
csrfToken,
item,
onSaved,
}: {
scope: string;
csrfToken: string;
item?: CatalogItemDTO | undefined;
onSaved: CatalogSaved;
}) {
const { t } = useTranslation("assets");
const id = useId();
const current = item?.values.kind === "relation" ? item.values : undefined;
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
defaultValues: current ?? {
kind: "relation",
name: "",
inverseName: "",
description: "",
symmetric: false,
},
});
const mutation = useCatalogSave(scope, csrfToken, onSaved, item?.id);
const symmetric = form.watch("symmetric");
return (
<form className="space-y-4" onSubmit={form.handleSubmit((values) => mutation.mutate(values))}>
<div className="space-y-2">
<Label htmlFor={id}>{t("name")}</Label>
<Input
id={id}
maxLength={100}
required
{...form.register("name")}
onChange={(event) => {
form.setValue("name", event.target.value);
if (symmetric) {
form.setValue("inverseName", event.target.value);
}
}}
/>
</div>
<label className="flex items-center gap-2 text-sm">
<input
checked={symmetric}
disabled={!!item}
onChange={(event) => {
form.setValue("symmetric", event.target.checked);
if (event.target.checked) {
form.setValue("inverseName", form.getValues("name"));
}
}}
type="checkbox"
/>
{t("symmetric")}
</label>
{!symmetric && (
<div className="space-y-2">
<Label htmlFor={`${id}-inverse`}>{t("inverseName")}</Label>
<Input id={`${id}-inverse`} maxLength={100} required {...form.register("inverseName")} />
</div>
)}
<div className="space-y-2">
<Label htmlFor={`${id}-description`}>{t("description")}</Label>
<Textarea id={`${id}-description`} maxLength={1000} {...form.register("description")} />
</div>
<CatalogError
error={mutation.error}
invalid={Object.keys(form.formState.errors).length > 0}
/>
<CatalogHelp kind="relation" scope={scope} />
<FormActions>
<Button disabled={mutation.isPending} type="submit">
{t(mutation.isPending ? "saving" : "save")}
</Button>
</FormActions>
</form>
);
}