212 lines
7.1 KiB
TypeScript
212 lines
7.1 KiB
TypeScript
import { Button } from "@repo/shadcn-ui/components/button";
|
|
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
|
import {
|
|
EntityAction,
|
|
EntityListHeader,
|
|
EntityRowActions,
|
|
EntityStatus,
|
|
EntityTable,
|
|
} from "@repo/shadcn-ui/components/entity-list";
|
|
import { FormDialog } from "@repo/shadcn-ui/components/form-dialog";
|
|
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 { TemplateSteps } from "./template-detail";
|
|
import { InterventionTemplateFeedback } from "./template-feedback";
|
|
import { InterventionTemplateForm, copyTemplateValues } from "./template-form";
|
|
import { TemplateHistory } from "./template-history";
|
|
export function InterventionTemplatesPage({
|
|
scope = "global",
|
|
csrfToken,
|
|
}: {
|
|
scope?: string;
|
|
csrfToken: string;
|
|
}) {
|
|
const { t, i18n } = 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">
|
|
<EntityListHeader
|
|
actions={
|
|
<>
|
|
{" "}
|
|
<Button disabled={!query.data || query.isError} onClick={() => setEditor({})}>
|
|
{t("interventionTemplates.new")}
|
|
</Button>{" "}
|
|
</>
|
|
}
|
|
description={t(
|
|
scope === "global"
|
|
? "interventionTemplates.scopeGlobal"
|
|
: "interventionTemplates.scopeCompany"
|
|
)}
|
|
locale={i18n.language}
|
|
title={t("interventionTemplates.title")}
|
|
/>
|
|
|
|
{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 && (
|
|
<EntityTable
|
|
columns={[
|
|
{
|
|
id: "name",
|
|
label: t("name"),
|
|
value: (template) => template.values.name,
|
|
hideable: false,
|
|
render: (template) => <>{template.values.name}</>,
|
|
},
|
|
{
|
|
id: "scope",
|
|
label: t("interventionTemplates.global"),
|
|
value: (template) =>
|
|
t(
|
|
template.companyId
|
|
? "interventionTemplates.company"
|
|
: "interventionTemplates.global"
|
|
),
|
|
render: (template) => (
|
|
<>
|
|
{t(
|
|
template.companyId
|
|
? "interventionTemplates.company"
|
|
: "interventionTemplates.global"
|
|
)}
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
id: "state",
|
|
label: t("status"),
|
|
value: (template) => t(template.values.active ? "active" : "inactive"),
|
|
render: (template) => (
|
|
<EntityStatus active={template.values.active}>
|
|
{t(template.values.active ? "active" : "inactive")}
|
|
</EntityStatus>
|
|
),
|
|
},
|
|
{
|
|
id: "steps",
|
|
label: t("interventions.steps"),
|
|
value: (template) => template.values.steps.length,
|
|
render: (template) => (
|
|
<details>
|
|
<summary className="cursor-pointer">
|
|
{t("interventions.steps")} ({template.values.steps.length})
|
|
</summary>
|
|
<TemplateSteps values={template.values} />
|
|
</details>
|
|
),
|
|
},
|
|
{
|
|
id: "history",
|
|
label: t("history"),
|
|
render: (template) => <TemplateHistory template={template} />,
|
|
},
|
|
{
|
|
id: "actions",
|
|
label: t("actions"),
|
|
hideable: false,
|
|
render: (template) => (
|
|
<EntityRowActions label={t("actions")}>
|
|
{" "}
|
|
<div className="contents">
|
|
{template.companyId === (scope === "global" ? null : scope) && (
|
|
<EntityAction onClick={() => setEditor({ id: template.id })}>
|
|
{t("edit")}
|
|
</EntityAction>
|
|
)}
|
|
<EntityAction onClick={() => setEditor({ copyId: template.id })}>
|
|
{t("interventionTemplates.copy")}
|
|
</EntityAction>
|
|
</div>{" "}
|
|
</EntityRowActions>
|
|
),
|
|
},
|
|
]}
|
|
emptyMessage={t("interventionTemplates.empty")}
|
|
filterKey={String(showInactive)}
|
|
filters={
|
|
<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>
|
|
}
|
|
filtersApplied={!showInactive}
|
|
items={visible}
|
|
locale={i18n.language}
|
|
onClearFilters={() => setShowInactive(true)}
|
|
onSearchChange={setSearch}
|
|
search={search}
|
|
searchPlaceholder={t("interventionTemplates.filter")}
|
|
title={t("interventionTemplates.title")}
|
|
/>
|
|
)}
|
|
<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>
|
|
);
|
|
}
|