182 lines
6.6 KiB
TypeScript
182 lines
6.6 KiB
TypeScript
import { Button } from "@repo/shadcn-ui/components/button";
|
||
import { EntityTable } from "@repo/shadcn-ui/components/entity-list";
|
||
import { FormDialog } from "@repo/shadcn-ui/components/form-dialog";
|
||
import { entityListLabels } from "@repo/shadcn-ui/lib/entity-list";
|
||
import { useTranslation } from "react-i18next";
|
||
|
||
import { CompanyIdentity } from "../../../companies/shared/ui/company-identity";
|
||
import { BlockState, DashboardNavigationButton, DashboardTable } from "../../shared";
|
||
import { entryDestination } from "../../shared/helpers";
|
||
import { useDashboardDetailsDialogController } from "../controllers";
|
||
import { detailsTitle } from "../helpers";
|
||
import type { DashboardDetailsDialogProps } from "../types";
|
||
|
||
export function DashboardDetailsDialog(props: DashboardDetailsDialogProps) {
|
||
const { details, onClose, onNavigate } = props;
|
||
const { page, setPage, query, companies, total } = useDashboardDetailsDialogController(props);
|
||
const { t, i18n } = useTranslation("companies");
|
||
const title = t(detailsTitle(details));
|
||
const labels = entityListLabels(i18n.language);
|
||
const range = labels.range(
|
||
total ? (page - 1) * 10 + 1 : 0,
|
||
Math.min(page * 10, total),
|
||
total,
|
||
t("dashboard.records")
|
||
);
|
||
return (
|
||
<FormDialog
|
||
cancelLabel={t("cancel")}
|
||
onOpenChange={(open) => {
|
||
if (!open) {
|
||
onClose();
|
||
}
|
||
}}
|
||
open
|
||
title={title}
|
||
wide
|
||
>
|
||
{details.companies ? (
|
||
<BlockState
|
||
empty={!companies.data?.items.length}
|
||
emptyText={t("noCompanies")}
|
||
query={companies}
|
||
>
|
||
<EntityTable
|
||
columns={[
|
||
{
|
||
id: "name",
|
||
label: t("name"),
|
||
value: (item) => item.name,
|
||
render: (item) => (
|
||
<DashboardNavigationButton
|
||
onClick={() =>
|
||
onNavigate({ area: "company", id: item.id, section: "inventory" })
|
||
}
|
||
>
|
||
<CompanyIdentity companyId={item.id} name={item.name} />
|
||
</DashboardNavigationButton>
|
||
),
|
||
},
|
||
{
|
||
id: "tickets",
|
||
label: t("dashboard.openTickets"),
|
||
value: (item) => item.tickets,
|
||
render: (item) => item.tickets,
|
||
},
|
||
{
|
||
id: "interventions",
|
||
label: t("dashboard.pendingInterventions"),
|
||
value: (item) => item.interventions,
|
||
render: (item) => item.interventions,
|
||
},
|
||
{
|
||
id: "assets",
|
||
label: t("dashboard.registeredAssets"),
|
||
value: (item) => item.assets,
|
||
render: (item) => item.assets,
|
||
},
|
||
{
|
||
id: "people",
|
||
label: t("people"),
|
||
value: (item) => item.people,
|
||
render: (item) => item.people,
|
||
},
|
||
]}
|
||
entityLabel={t("title")}
|
||
items={companies.data?.items ?? []}
|
||
locale={i18n.language}
|
||
title={title}
|
||
/>
|
||
</BlockState>
|
||
) : (
|
||
<>
|
||
<BlockState
|
||
empty={!query.data?.items.length}
|
||
emptyText={t("dashboard.noMatches")}
|
||
query={query}
|
||
>
|
||
<p className="text-sm text-muted-foreground">{range}</p>
|
||
<div className="rounded-panel bg-card">
|
||
<DashboardTable caption={title}>
|
||
<thead>
|
||
<tr>
|
||
<th scope="col">{t("name")}</th>
|
||
<th scope="col">{t("tickets.company")}</th>
|
||
{details.activity && <th scope="col">{t("dashboard.recordedAt")}</th>}
|
||
{details.activity && <th scope="col">{t("dashboard.recordedBy")}</th>}
|
||
{details.work && <th scope="col">{t("tickets.state")}</th>}
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{query.data?.items.map((item) => (
|
||
<tr key={item.eventId ?? item.id}>
|
||
<td>
|
||
<DashboardNavigationButton
|
||
onClick={() => onNavigate(entryDestination(item))}
|
||
>
|
||
{item.reference ? `${item.reference} · ` : ""}
|
||
{item.name}
|
||
</DashboardNavigationButton>
|
||
</td>
|
||
<td>
|
||
<CompanyIdentity companyId={item.companyId} name={item.companyName} />
|
||
</td>
|
||
{details.activity && (
|
||
<td>
|
||
{item.recordedAt
|
||
? new Intl.DateTimeFormat(i18n.language, {
|
||
dateStyle: "medium",
|
||
timeStyle: "short",
|
||
}).format(new Date(item.recordedAt))
|
||
: "—"}
|
||
</td>
|
||
)}
|
||
{details.activity && <td>{item.actorName ?? "—"}</td>}
|
||
{details.work && (
|
||
<td>
|
||
{t(
|
||
(item.kind === "ticket" ? "tickets.states." : "interventions.states.") +
|
||
item.state
|
||
)}
|
||
</td>
|
||
)}
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</DashboardTable>
|
||
</div>
|
||
</BlockState>
|
||
{!details.activity && query.isSuccess && (
|
||
<footer className="flex flex-wrap items-center justify-between gap-3 border-t pt-4 text-sm">
|
||
<span>{range}</span>
|
||
<div className="flex items-center gap-2">
|
||
<Button
|
||
aria-label={labels.previous}
|
||
disabled={page === 1}
|
||
onClick={() => setPage(page - 1)}
|
||
size="sm"
|
||
variant="outline"
|
||
>
|
||
‹
|
||
</Button>
|
||
<span>
|
||
{labels.page} {page}
|
||
</span>
|
||
<Button
|
||
aria-label={labels.next}
|
||
disabled={page * 10 >= total}
|
||
onClick={() => setPage(page + 1)}
|
||
size="sm"
|
||
variant="outline"
|
||
>
|
||
›
|
||
</Button>
|
||
</div>
|
||
</footer>
|
||
)}
|
||
</>
|
||
)}
|
||
</FormDialog>
|
||
);
|
||
}
|