metis-desk/modules/companies/src/web/local-identity-environments/detail/ui/asset-local-identities.tsx

147 lines
5.2 KiB
TypeScript
Raw Normal View History

import { BrowserLink } from "@metis/core/client";
import { CursorPagination } from "@repo/rdx-ui/components/cursor-pagination";
import { DetailPanel } from "@repo/rdx-ui/components/detail-panel";
import { EntityStatus } from "@repo/rdx-ui/components/entity-list";
import { KeyRoundIcon } from "@repo/rdx-ui/lib/icons";
import { Button } from "@repo/shadcn-ui/components/button";
import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { getAccess } from "../../../access/list/api";
import { accessKeys } from "../../../access/shared/queries";
import type { LocalIdentityOperation } from "../../manage/ui/local-identity-editor";
import { LocalIdentityEditor } from "../../manage/ui/local-identity-editor";
import { localIdentityApi, localIdentityKeys } from "../../shared/api/local-identity-api";
export function AssetLocalIdentities({
companyId,
asset,
}: {
companyId: string;
asset: { id: string; name: string };
}) {
const { t } = useTranslation("companies");
const [cursors, setCursors] = useState<string[]>([]);
const cursor = cursors.at(-1);
const [operation, setOperation] = useState<LocalIdentityOperation>();
const query = useQuery({
queryKey: localIdentityKeys.page(companyId, asset.id, cursor),
queryFn: () => localIdentityApi.get(companyId, asset.id, cursor),
});
const access = useQuery({
queryKey: accessKeys.scope(companyId),
queryFn: () => getAccess(companyId),
});
const label = (id: string) => {
const value = access.data?.items.find((item) => item.id === id)?.values;
return value && "name" in value ? value.name : "—";
};
return (
<DetailPanel
actions={
<div className="flex flex-wrap gap-2">
<Button onClick={() => setOperation({ action: "associate", asset })} variant="outline">
{t("localIdentity.associate")}
</Button>
<Button onClick={() => setOperation({ action: "create", asset })}>
{t("localIdentity.create")}
</Button>
</div>
}
icon={<KeyRoundIcon />}
title={t("localIdentity.title")}
>
<p className="text-sm text-muted-foreground">{t("localIdentity.description")}</p>
{(query.isPending || access.isPending) && <p role="status">{t("loading")}</p>}
{(query.isError || access.isError) && (
<div role="alert">
<p>{t("localIdentity.error")}</p>
<Button
onClick={async () => {
await Promise.all([query.refetch(), access.refetch()]);
}}
variant="outline"
>
{t("retry")}
</Button>
</div>
)}
{!(query.isError || access.isError) && query.data && (
<>
{query.data.environments.length ? (
<ul className="flex flex-col gap-3">
{query.data.environments.map((item) => (
<li
className="flex flex-wrap items-start justify-between gap-3 rounded-lg border p-3 text-sm"
key={item.id}
>
<div className="min-w-0 flex flex-col gap-1">
<BrowserLink
href={`/companies/${companyId}/access?kind=account&environmentId=${item.environmentId}`}
>
{label(item.environmentId)}
</BrowserLink>
<p>{label(item.systemId)}</p>
<EntityStatus active={!item.endedAt}>
{t(item.endedAt ? "localIdentity.ended" : "localIdentity.current")}
</EntityStatus>
<p>
<time dateTime={item.startedAt}>{item.startedAt}</time> ·{" "}
{item.endedAt ?? "—"}
</p>
</div>
{!item.endedAt && (
<Button
onClick={() =>
setOperation({ action: "retire", environmentId: item.environmentId })
}
variant="outline"
>
{t("localIdentity.retire")}
</Button>
)}
</li>
))}
</ul>
) : (
<p className="text-sm text-muted-foreground" role="status">
{t("localIdentity.empty")}
</p>
)}
</>
)}
<CursorPagination
canNext={!!query.data?.nextAfterId && !query.isError}
canPrevious={cursors.length > 0}
nextLabel={t("localIdentity.next")}
onNext={() => {
const next = query.data?.nextAfterId;
if (next) {
setCursors((values) => [...values, next]);
}
}}
onPrevious={() => setCursors((values) => values.slice(0, -1))}
onRestart={() => {
setCursors([]);
query.refetch().catch(() => undefined);
}}
pending={query.isFetching}
previousLabel={t("localIdentity.previous")}
restartLabel={t("retry")}
/>
{operation && (
<LocalIdentityEditor
companyId={companyId}
onClose={() => setOperation(undefined)}
operation={operation}
/>
)}
</DetailPanel>
);
}