2026-10-03 10:36:03 +00:00
|
|
|
import { Button } from "@repo/shadcn-ui/components/button";
|
|
|
|
|
import { Card, CardContent } from "@repo/shadcn-ui/components/card";
|
|
|
|
|
import { FormDialog } from "@repo/shadcn-ui/components/form-dialog";
|
|
|
|
|
import { useQuery } from "@tanstack/react-query";
|
|
|
|
|
import { useState } from "react";
|
|
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
|
|
|
|
|
|
import type { CompanyDTO } from "../../common";
|
|
|
|
|
import { companyApi, companyKeys } from "../api/company-api";
|
|
|
|
|
|
|
|
|
|
import { CompanyFeedback } from "./company-feedback";
|
|
|
|
|
import { CompanyForm } from "./company-form";
|
2026-10-03 11:13:15 +00:00
|
|
|
import { DepartmentSection } from "./department-section";
|
|
|
|
|
import { PeopleSection } from "./people-section";
|
2026-10-03 10:36:03 +00:00
|
|
|
import { SiteForm } from "./site-form";
|
|
|
|
|
|
|
|
|
|
function SiteSection({ company, csrfToken }: { company: CompanyDTO; csrfToken: string }) {
|
|
|
|
|
const { t } = useTranslation("companies");
|
|
|
|
|
const [createOpen, setCreateOpen] = useState(false);
|
|
|
|
|
const [selectedId, setSelectedId] = useState<string | undefined>();
|
|
|
|
|
const selected = company.sites.find((site) => site.id === selectedId);
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
<Card>
|
|
|
|
|
<CardContent className="pt-6">
|
|
|
|
|
<div className="mb-4 flex items-center justify-between gap-4">
|
|
|
|
|
<h2 className="text-lg font-semibold">{t("sites")}</h2>
|
|
|
|
|
<Button onClick={() => setCreateOpen(true)}>{t("newSite")}</Button>
|
|
|
|
|
</div>
|
|
|
|
|
{company.sites.length === 0 ? (
|
|
|
|
|
<p className="text-muted-foreground">{t("noSites")}</p>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="divide-y">
|
|
|
|
|
{company.sites.map((site) => (
|
|
|
|
|
<div className="flex items-start justify-between gap-4 py-4" key={site.id}>
|
|
|
|
|
<div className="min-w-0">
|
|
|
|
|
<h3 className="font-medium">{site.name}</h3>
|
|
|
|
|
<p className="mt-2 whitespace-pre-wrap break-words text-sm text-muted-foreground">
|
|
|
|
|
{site.address ?? t("noAddress")}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<Button onClick={() => setSelectedId(site.id)} size="sm" variant="outline">
|
|
|
|
|
{t("edit")}
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
<FormDialog
|
|
|
|
|
cancelLabel={t("cancel")}
|
|
|
|
|
onOpenChange={setCreateOpen}
|
|
|
|
|
open={createOpen}
|
|
|
|
|
title={t("newSite")}
|
|
|
|
|
>
|
|
|
|
|
<SiteForm
|
|
|
|
|
companyId={company.id}
|
|
|
|
|
csrfToken={csrfToken}
|
|
|
|
|
onSaved={() => setCreateOpen(false)}
|
|
|
|
|
showTitle={false}
|
|
|
|
|
/>
|
|
|
|
|
</FormDialog>
|
|
|
|
|
<FormDialog
|
|
|
|
|
cancelLabel={t("cancel")}
|
|
|
|
|
onOpenChange={(open) => {
|
|
|
|
|
if (!open) {
|
|
|
|
|
setSelectedId(undefined);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
open={selected !== undefined}
|
|
|
|
|
title={t("editSite")}
|
|
|
|
|
>
|
|
|
|
|
{selected && (
|
|
|
|
|
<SiteForm
|
|
|
|
|
companyId={company.id}
|
|
|
|
|
csrfToken={csrfToken}
|
|
|
|
|
key={selected.id}
|
|
|
|
|
onSaved={() => setSelectedId(undefined)}
|
|
|
|
|
showTitle={false}
|
|
|
|
|
site={selected}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</FormDialog>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
export function CompanyDetail({
|
|
|
|
|
id,
|
|
|
|
|
csrfToken,
|
|
|
|
|
onBack,
|
|
|
|
|
}: {
|
|
|
|
|
id: string;
|
|
|
|
|
csrfToken: string;
|
|
|
|
|
onBack: () => void;
|
|
|
|
|
}) {
|
|
|
|
|
const { t } = useTranslation("companies");
|
|
|
|
|
const query = useQuery({ queryKey: companyKeys.detail(id), queryFn: () => companyApi.get(id) });
|
|
|
|
|
const [editOpen, setEditOpen] = useState(false);
|
2026-10-03 11:13:15 +00:00
|
|
|
const [tab, setTab] = useState<"people" | "departments" | "sites">("people");
|
2026-10-03 10:36:03 +00:00
|
|
|
const company = query.data?.company;
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
<Button onClick={onBack} variant="outline">
|
|
|
|
|
{t("back")}
|
|
|
|
|
</Button>
|
|
|
|
|
{query.isPending && <p role="status">{t("loading")}</p>}
|
|
|
|
|
<CompanyFeedback error={query.error} />
|
|
|
|
|
{query.isError && (
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
query.refetch().catch(() => undefined);
|
|
|
|
|
}}
|
|
|
|
|
variant="outline"
|
|
|
|
|
>
|
|
|
|
|
{t("retry")}
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
{company && (
|
|
|
|
|
<>
|
|
|
|
|
<div className="flex flex-wrap items-start justify-between gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<h1 className="text-2xl font-semibold">{company.name}</h1>
|
|
|
|
|
<p className="mt-1 text-sm text-muted-foreground">
|
|
|
|
|
{t(company.active ? "activeCompany" : "inactiveCompany")}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<Button onClick={() => setEditOpen(true)} variant="outline">
|
|
|
|
|
{t("editCompany")}
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
<FormDialog
|
|
|
|
|
cancelLabel={t("cancel")}
|
|
|
|
|
onOpenChange={setEditOpen}
|
|
|
|
|
open={editOpen}
|
|
|
|
|
title={t("editCompany")}
|
|
|
|
|
>
|
|
|
|
|
<CompanyForm
|
|
|
|
|
company={company}
|
|
|
|
|
csrfToken={csrfToken}
|
|
|
|
|
key={company.id}
|
|
|
|
|
onSaved={() => setEditOpen(false)}
|
|
|
|
|
showTitle={false}
|
|
|
|
|
/>
|
|
|
|
|
</FormDialog>
|
|
|
|
|
<nav aria-label={t("companySections")} className="flex gap-2">
|
|
|
|
|
<Button
|
2026-10-03 11:13:15 +00:00
|
|
|
aria-pressed={tab === "people"}
|
|
|
|
|
onClick={() => setTab("people")}
|
|
|
|
|
variant={tab === "people" ? "secondary" : "outline"}
|
|
|
|
|
>
|
|
|
|
|
{t("people")} ({company.people.length})
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
aria-pressed={tab === "departments"}
|
|
|
|
|
onClick={() => setTab("departments")}
|
|
|
|
|
variant={tab === "departments" ? "secondary" : "outline"}
|
2026-10-03 10:36:03 +00:00
|
|
|
>
|
2026-10-03 11:13:15 +00:00
|
|
|
{t("departments")} ({company.departments.length})
|
2026-10-03 10:36:03 +00:00
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
aria-pressed={tab === "sites"}
|
|
|
|
|
onClick={() => setTab("sites")}
|
|
|
|
|
variant={tab === "sites" ? "secondary" : "outline"}
|
|
|
|
|
>
|
|
|
|
|
{t("sites")} ({company.sites.length})
|
|
|
|
|
</Button>
|
|
|
|
|
</nav>
|
2026-10-03 11:13:15 +00:00
|
|
|
{tab === "people" ? (
|
|
|
|
|
<PeopleSection company={company} csrfToken={csrfToken} />
|
|
|
|
|
) : tab === "departments" ? (
|
|
|
|
|
<DepartmentSection company={company} csrfToken={csrfToken} />
|
2026-10-03 10:36:03 +00:00
|
|
|
) : (
|
|
|
|
|
<SiteSection company={company} csrfToken={csrfToken} />
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|