metis-desk/modules/companies/src/web/intervention-templates/templates-page.tsx

161 lines
5.8 KiB
TypeScript
Raw Normal View History

2026-10-07 11:03:01 +00:00
import { Button } from "@repo/shadcn-ui/components/button";
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
import { FormDialog } from "@repo/shadcn-ui/components/form-dialog";
import { Input } from "@repo/shadcn-ui/components/input";
import { Label } from "@repo/shadcn-ui/components/label";
import { useQuery } from "@tanstack/react-query";
import { useId, useState } from "react";
import { useTranslation } from "react-i18next";
import { interventionTemplateApi, interventionTemplateKeys } from "./template-api";
import { TemplateHistory, TemplateSteps } from "./template-detail";
import { InterventionTemplateForm, copyTemplateValues } from "./template-form";
import { InterventionTemplateFeedback } from "./template-help";
export function InterventionTemplatesPage({
scope = "global",
csrfToken,
}: {
scope?: string;
csrfToken: string;
}) {
const { t } = useTranslation("companies");
const prefix = useId();
const query = useQuery({
queryKey: interventionTemplateKeys.list(scope),
queryFn: () => interventionTemplateApi.list(scope),
});
const [editor, setEditor] = useState<{ id?: string; copyId?: string }>();
const [search, setSearch] = useState("");
const [showInactive, setShowInactive] = useState(true);
const templates = query.data?.templates ?? [];
const selected = templates.find((item) => item.id === editor?.id);
const copied = templates.find((item) => item.id === editor?.copyId);
const visible = templates.filter(
(item) =>
(showInactive || item.values.active) &&
item.values.name.toLocaleLowerCase().includes(search.trim().toLocaleLowerCase())
);
return (
<div className="space-y-5">
<header className="flex items-center justify-between gap-4">
<h2 className="text-xl font-semibold">{t("interventionTemplates.title")}</h2>
<Button disabled={!query.data || query.isError} onClick={() => setEditor({})}>
{t("interventionTemplates.new")}
</Button>
</header>
<p className="text-sm text-muted-foreground">
{t(
scope === "global"
? "interventionTemplates.scopeGlobal"
: "interventionTemplates.scopeCompany"
)}
</p>
<div className="flex flex-wrap gap-4 items-center">
<div>
<Label className="sr-only" htmlFor={`${prefix}-search`}>
{t("interventionTemplates.filter")}
</Label>
<Input
id={`${prefix}-search`}
onChange={(event) => setSearch(event.target.value)}
placeholder={t("interventionTemplates.filter")}
value={search}
/>
</div>
<div className="flex gap-2 items-center">
<Checkbox
checked={showInactive}
id={`${prefix}-inactive`}
onCheckedChange={(value) => setShowInactive(value === true)}
/>
<Label htmlFor={`${prefix}-inactive`}>{t("interventionTemplates.showInactive")}</Label>
</div>
</div>
{query.isPending && <p role="status">{t("loading")}</p>}
<InterventionTemplateFeedback error={query.error} />
{query.isError && (
<Button
onClick={() => {
query.refetch().catch(() => undefined);
}}
variant="outline"
>
{t("retry")}
</Button>
)}
{query.data && !query.isError && (
<>
{!visible.length && <p>{t("interventionTemplates.empty")}</p>}
{visible.map((template) => (
<section className="rounded-xl border p-5 space-y-4" key={template.id}>
<header className="flex flex-wrap justify-between gap-3">
<div>
<h3 className="font-medium">{template.values.name}</h3>
<p className="text-sm text-muted-foreground">
{t(
template.companyId
? "interventionTemplates.company"
: "interventionTemplates.global"
)}{" "}
· {t(template.values.active ? "active" : "inactive")}
</p>
</div>
<div className="flex gap-2">
{template.companyId === (scope === "global" ? null : scope) && (
<Button
onClick={() => setEditor({ id: template.id })}
size="sm"
variant="outline"
>
{t("edit")}
</Button>
)}
<Button
onClick={() => setEditor({ copyId: template.id })}
size="sm"
variant="outline"
>
{t("interventionTemplates.copy")}
</Button>
</div>
</header>
<TemplateSteps values={template.values} />
<TemplateHistory template={template} />
</section>
))}
</>
)}
<FormDialog
cancelLabel={t("cancel")}
formActions
onOpenChange={(open) => {
if (!open) {
setEditor(undefined);
}
}}
open={editor !== undefined}
title={t(selected ? "interventionTemplates.edit" : "interventionTemplates.new")}
wide
>
{editor && (
<InterventionTemplateForm
csrfToken={csrfToken}
initialValues={
copied
? copyTemplateValues(
copied,
`${t("interventionTemplates.copyOf")} ${copied.values.name}`
)
: undefined
}
key={editor.id ?? editor.copyId ?? "new"}
onSaved={() => setEditor(undefined)}
scope={scope}
template={selected}
/>
)}
</FormDialog>
</div>
);
}