metis-desk/modules/companies/src/web/resources/detail/ui/application-resource-usage.tsx
2026-10-25 01:19:17 +02:00

110 lines
3.8 KiB
TypeScript

import { BrowserLink } from "@metis/core/client";
import { Button } from "@repo/shadcn-ui/components/button";
import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import {
applicationApi,
applicationKeys,
} from "../../../application-configurations/shared/api/application-api";
import { softwareApi, softwareKeys } from "../../../software-installations/shared/api/software-api";
/** Las APIs públicas existentes devuelven el conjunto completo de la compañía; no consulta uso global transversal. */
export function ApplicationResourceUsage({
companyId,
resourceId,
}: {
companyId: string;
resourceId: string;
}) {
const { t } = useTranslation("companies");
const [open, setOpen] = useState(false);
const installations = useQuery({
queryKey: softwareKeys.company(companyId),
queryFn: () => softwareApi.get(companyId),
enabled: open,
retry: false,
});
const configurations = useQuery({
queryKey: applicationKeys.company(companyId),
queryFn: () => applicationApi.get(companyId),
enabled: open,
retry: false,
});
const installed =
installations.data?.configurations.filter(
(item) => item.companyId === companyId && item.values.resourceId === resourceId
) ?? [];
const configured =
configurations.data?.configurations.filter(
(item) => item.companyId === companyId && item.values.resourceId === resourceId
) ?? [];
return (
<section aria-label={t("resourceUsage.title")} className="flex flex-col gap-3">
<Button aria-expanded={open} onClick={() => setOpen(!open)} variant="outline">
{t("resourceUsage.title")}
</Button>
{open && (
<>
<p className="text-sm text-muted-foreground">{t("resourceUsage.notice")}</p>
{[
{
query: installations,
label: t("software.title"),
items: installed.map((item) => ({
id: item.id,
href: `/companies/${companyId}/assets/${item.values.assetId}`,
state: item.values.state,
version: item.values.version,
})),
},
{
query: configurations,
label: t("applications.title"),
items: configured.map((item) => ({
id: item.id,
href: `/companies/${companyId}/work-profiles/${item.values.profileId}/application-configurations`,
state: item.values.state,
version: item.values.version,
})),
},
].map((source) => (
<div key={source.label}>
<h4 className="font-medium">
{source.label}: {source.query.isSuccess ? source.items.length : "—"}
</h4>
{source.query.isFetching && <p role="status">{t("loading")}</p>}
{source.query.isError && (
<div role="alert">
<p>{t("resourceUsage.error")}</p>
<Button
onClick={async () => {
await source.query.refetch();
}}
variant="outline"
>
{t("resourceUsage.retry")}
</Button>
</div>
)}
{source.query.isSuccess && (
<ul className="max-h-60 overflow-y-auto divide-y">
{source.items.map((item) => (
<li className="py-2 text-sm" key={item.id}>
<BrowserLink href={item.href}>
{t(`mail.${item.state}`)} · {item.version ?? "—"}
</BrowserLink>
</li>
))}
</ul>
)}
</div>
))}
</>
)}
</section>
);
}