358 lines
12 KiB
TypeScript
358 lines
12 KiB
TypeScript
import { AssetCatalogPage } from "@metis/assets/client";
|
|
import { ResourceCatalogPage, ResourcesPage } from "@metis/resources/client";
|
|
import { Button } from "@repo/shadcn-ui/components/button";
|
|
import {
|
|
EntityAction,
|
|
EntityListHeader,
|
|
EntityRowActions,
|
|
EntityTable,
|
|
} from "@repo/shadcn-ui/components/entity-list";
|
|
import { FormDialog } from "@repo/shadcn-ui/components/form-dialog";
|
|
import {
|
|
BoxIcon,
|
|
BriefcaseIcon,
|
|
KeyRoundIcon,
|
|
LayersIcon,
|
|
LayoutTemplateIcon,
|
|
MailIcon,
|
|
MapPinIcon,
|
|
UsersIcon,
|
|
} from "@repo/shadcn-ui/lib/icons";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import type { ReactNode } from "react";
|
|
import { useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
import type { CompanyDTO } from "../../common";
|
|
import { AccessWorkspacePage } from "../access/access-workspace";
|
|
import { companyApi, companyKeys } from "../api/company-api";
|
|
import { ApplicationConfigurationsPage } from "../applications/application-configurations-page";
|
|
import { CertificateConfigurationsPage } from "../certificates/certificate-configurations-page";
|
|
import { FolderMappingsPage } from "../folders/folder-mappings-page";
|
|
import { InterventionTemplatesPage } from "../intervention-templates/templates-page";
|
|
import { InterventionsPage } from "../interventions/interventions-page";
|
|
import { MailConfigurationsPage } from "../mail/mail-configurations-page";
|
|
import { MailboxRelations } from "../mail/mailbox-relations";
|
|
import { PrinterConfigurationsPage } from "../printers/printer-configurations-page";
|
|
import { ProcessesPage } from "../processes/processes-page";
|
|
import { WorkProfilesPage } from "../profiles/work-profiles-page";
|
|
import { RemoteConnectionsPage } from "../remote/remote-connections-page";
|
|
import { TeleworkAuthorizationsPage } from "../telework/telework-authorizations-page";
|
|
import { TicketsPage } from "../tickets/tickets-page";
|
|
|
|
import { CompanyFeedback } from "./company-feedback";
|
|
import { CompanyForm } from "./company-form";
|
|
import { CompanyInventory } from "./company-inventory";
|
|
import { DepartmentSection } from "./department-section";
|
|
import { PeopleSection } from "./people-section";
|
|
import { SiteForm } from "./site-form";
|
|
|
|
function SiteSection({ company, csrfToken }: { company: CompanyDTO; csrfToken: string }) {
|
|
const { t, i18n } = 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">
|
|
<EntityListHeader
|
|
actions={<Button onClick={() => setCreateOpen(true)}>{t("newSite")}</Button>}
|
|
locale={i18n.language}
|
|
title={t("sites")}
|
|
/>
|
|
<EntityTable
|
|
columns={[
|
|
{
|
|
id: "name",
|
|
label: t("name"),
|
|
value: (site) => site.name,
|
|
hideable: false,
|
|
render: (site) => <span className="font-medium">{site.name}</span>,
|
|
},
|
|
{
|
|
id: "address",
|
|
label: t("addressOptional"),
|
|
value: (site) => site.address,
|
|
render: (site) => (
|
|
<span className="whitespace-pre-wrap">{site.address ?? t("noAddress")}</span>
|
|
),
|
|
},
|
|
{
|
|
id: "actions",
|
|
label: t("actions"),
|
|
hideable: false,
|
|
render: (site) => (
|
|
<EntityRowActions label={t("actions")}>
|
|
<EntityAction onClick={() => setSelectedId(site.id)}>{t("edit")}</EntityAction>
|
|
</EntityRowActions>
|
|
),
|
|
},
|
|
]}
|
|
emptyMessage={t("noSites")}
|
|
items={company.sites}
|
|
locale={i18n.language}
|
|
title={t("sites")}
|
|
/>
|
|
<FormDialog
|
|
cancelLabel={t("cancel")}
|
|
formActions
|
|
onOpenChange={setCreateOpen}
|
|
open={createOpen}
|
|
title={t("newSite")}
|
|
>
|
|
<SiteForm
|
|
companyId={company.id}
|
|
csrfToken={csrfToken}
|
|
onSaved={() => setCreateOpen(false)}
|
|
showTitle={false}
|
|
/>
|
|
</FormDialog>
|
|
<FormDialog
|
|
cancelLabel={t("cancel")}
|
|
formActions
|
|
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>
|
|
);
|
|
}
|
|
type CompanyTab =
|
|
| "interventions"
|
|
| "intervention-templates"
|
|
| "tickets"
|
|
| "processes"
|
|
| "remote"
|
|
| "telework"
|
|
| "people"
|
|
| "departments"
|
|
| "sites"
|
|
| "access"
|
|
| "resource-catalog"
|
|
| "resources"
|
|
| "assets"
|
|
| "inventory"
|
|
| "workstations"
|
|
| "certificates"
|
|
| "applications"
|
|
| "printers"
|
|
| "folders"
|
|
| "mail"
|
|
| "profiles";
|
|
|
|
export function CompanyDetail({
|
|
id,
|
|
csrfToken,
|
|
onBack,
|
|
embedded = false,
|
|
accountNavigation,
|
|
}: {
|
|
id: string;
|
|
csrfToken: string;
|
|
onBack: () => void;
|
|
embedded?: boolean;
|
|
accountNavigation?: ReactNode;
|
|
}) {
|
|
const { t } = useTranslation("companies");
|
|
const { t: resourceTranslation } = useTranslation("resources");
|
|
const { t: assetTranslation } = useTranslation("assets");
|
|
const { t: accessTranslation } = useTranslation("access");
|
|
const query = useQuery({ queryKey: companyKeys.detail(id), queryFn: () => companyApi.get(id) });
|
|
const [editOpen, setEditOpen] = useState(false);
|
|
const [tab, setTab] = useState<CompanyTab>(embedded ? "inventory" : "people");
|
|
const company = query.data?.company;
|
|
const layout = embedded
|
|
? {
|
|
root: "company-space",
|
|
heading: "company-space-heading",
|
|
navigation: "company-space-navigation",
|
|
main: "company-space-main",
|
|
}
|
|
: {
|
|
root: "space-y-6",
|
|
heading: "flex flex-wrap items-start justify-between gap-4",
|
|
navigation: "flex flex-wrap gap-2",
|
|
main: undefined,
|
|
};
|
|
|
|
return (
|
|
<div className={layout.root}>
|
|
{!embedded && (
|
|
<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>
|
|
)}
|
|
{embedded && <footer className="company-space-account">{accountNavigation}</footer>}
|
|
{company && (
|
|
<>
|
|
<div className={layout.heading}>
|
|
<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")}
|
|
formActions
|
|
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={layout.navigation}>
|
|
{(
|
|
[
|
|
["inventory", assetTranslation("inventory"), BoxIcon],
|
|
["resources", resourceTranslation("inventory"), LayersIcon],
|
|
["workstations", assetTranslation("workstations"), BriefcaseIcon],
|
|
["profiles", t("profiles.title"), UsersIcon],
|
|
["telework", t("telework.title"), KeyRoundIcon],
|
|
["remote", t("remote.title"), KeyRoundIcon],
|
|
["processes", t("processes.title"), LayoutTemplateIcon],
|
|
["intervention-templates", t("interventionTemplates.title"), LayoutTemplateIcon],
|
|
["interventions", t("interventions.title"), LayoutTemplateIcon],
|
|
["tickets", t("tickets.title"), LayoutTemplateIcon],
|
|
["people", `${t("people")} (${company.people.length})`, UsersIcon],
|
|
[
|
|
"departments",
|
|
`${t("departments")} (${company.departments.length})`,
|
|
BriefcaseIcon,
|
|
],
|
|
["sites", `${t("sites")} (${company.sites.length})`, MapPinIcon],
|
|
["access", accessTranslation("title"), KeyRoundIcon],
|
|
["folders", t("folders.title"), MailIcon],
|
|
["certificates", t("certificates.title"), LayersIcon],
|
|
["applications", t("applications.title"), LayersIcon],
|
|
["printers", t("printers.title"), LayersIcon],
|
|
["mail", t("mail.title"), MailIcon],
|
|
["assets", assetTranslation("companyTitle"), LayoutTemplateIcon],
|
|
["resource-catalog", resourceTranslation("companyTitle"), LayoutTemplateIcon],
|
|
] as const
|
|
).map(([section, label, Icon]) => (
|
|
<Button
|
|
aria-pressed={tab === section}
|
|
key={section}
|
|
onClick={() => setTab(section)}
|
|
variant="ghost"
|
|
>
|
|
<Icon aria-hidden="true" className="size-4 shrink-0" />
|
|
{label}
|
|
</Button>
|
|
))}
|
|
</nav>
|
|
|
|
<main className={layout.main} key={tab}>
|
|
{embedded && <div className="company-space-breadcrumb">{company.name}</div>}
|
|
<CompanySection company={company} csrfToken={csrfToken} tab={tab} />
|
|
</main>
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function CompanySection({
|
|
company,
|
|
csrfToken,
|
|
tab,
|
|
}: {
|
|
company: CompanyDTO;
|
|
csrfToken: string;
|
|
tab: CompanyTab;
|
|
}) {
|
|
switch (tab) {
|
|
case "intervention-templates":
|
|
return <InterventionTemplatesPage csrfToken={csrfToken} scope={company.id} />;
|
|
case "interventions":
|
|
return <InterventionsPage companyId={company.id} csrfToken={csrfToken} />;
|
|
case "tickets":
|
|
return <TicketsPage companyId={company.id} csrfToken={csrfToken} />;
|
|
case "processes":
|
|
return <ProcessesPage company={company} csrfToken={csrfToken} />;
|
|
case "remote":
|
|
return <RemoteConnectionsPage company={company} csrfToken={csrfToken} />;
|
|
case "telework":
|
|
return <TeleworkAuthorizationsPage company={company} csrfToken={csrfToken} />;
|
|
case "people":
|
|
return <PeopleSection company={company} csrfToken={csrfToken} />;
|
|
case "departments":
|
|
return <DepartmentSection company={company} csrfToken={csrfToken} />;
|
|
case "sites":
|
|
return <SiteSection company={company} csrfToken={csrfToken} />;
|
|
case "access":
|
|
return (
|
|
<AccessWorkspacePage csrfToken={csrfToken} people={company.people} scope={company.id} />
|
|
);
|
|
case "profiles":
|
|
return <WorkProfilesPage company={company} csrfToken={csrfToken} />;
|
|
case "certificates":
|
|
return <CertificateConfigurationsPage company={company} csrfToken={csrfToken} />;
|
|
case "applications":
|
|
return <ApplicationConfigurationsPage company={company} csrfToken={csrfToken} />;
|
|
case "printers":
|
|
return <PrinterConfigurationsPage company={company} csrfToken={csrfToken} />;
|
|
case "folders":
|
|
return <FolderMappingsPage company={company} csrfToken={csrfToken} />;
|
|
case "mail":
|
|
return <MailConfigurationsPage company={company} csrfToken={csrfToken} />;
|
|
case "workstations":
|
|
return <CompanyInventory company={company} csrfToken={csrfToken} workstations />;
|
|
case "inventory":
|
|
return <CompanyInventory company={company} csrfToken={csrfToken} />;
|
|
case "resources":
|
|
return (
|
|
<ResourcesPage
|
|
companyId={company.id}
|
|
csrfToken={csrfToken}
|
|
relatedContent={(resource) =>
|
|
resource.values.integration?.kind === "mailbox" ? (
|
|
<MailboxRelations company={company} csrfToken={csrfToken} mailboxId={resource.id} />
|
|
) : null
|
|
}
|
|
/>
|
|
);
|
|
case "resource-catalog":
|
|
return <ResourceCatalogPage csrfToken={csrfToken} scope={company.id} />;
|
|
case "assets":
|
|
return <AssetCatalogPage csrfToken={csrfToken} scope={company.id} />;
|
|
}
|
|
}
|