diff --git a/clients/admin/src/api/files.ts b/clients/admin/src/api/files.ts index 4b063e6d78..56bcfbb13f 100644 --- a/clients/admin/src/api/files.ts +++ b/clients/admin/src/api/files.ts @@ -1,19 +1,20 @@ import { apiFetch } from "@/lib/api-client"; import type { PagedResponse } from "@/lib/api-types"; -// Mirrors FSH.Modules.Files.Domain.Visibility — Public/Private numeric codes -// match the server's int? Visibility shape on the FileAssetDto. +// Mirrors FSH.Modules.Files.Domain.Visibility — the server serializes this +// enum as its string name (JsonStringEnumConverter) on the FileAssetDto and +// accepts the same string names on request bodies. export const Visibility = { - Public: 0, - Private: 1, + Public: "Public", + Private: "Private", } as const; export type VisibilityValue = (typeof Visibility)[keyof typeof Visibility]; -// Mirrors FSH.Modules.Files.Domain.FileAssetStatus. +// Mirrors FSH.Modules.Files.Domain.FileAssetStatus — serialized as string name. export const FileAssetStatus = { - PendingUpload: 0, - Available: 1, - Quarantined: 2, + PendingUpload: "PendingUpload", + Available: "Available", + Quarantined: "Quarantined", } as const; export type FileAssetStatusValue = (typeof FileAssetStatus)[keyof typeof FileAssetStatus]; diff --git a/clients/admin/src/pages/billing/invoice-detail.tsx b/clients/admin/src/pages/billing/invoice-detail.tsx index 93851479ae..9d0b18d932 100644 --- a/clients/admin/src/pages/billing/invoice-detail.tsx +++ b/clients/admin/src/pages/billing/invoice-detail.tsx @@ -19,6 +19,8 @@ import { Input } from "@/components/ui/input"; import { EntityPageHeader, SettingsSection, Field } from "@/components/list"; import { ApiRequestError } from "@/lib/api-client"; import { cn } from "@/lib/cn"; +import { useAuth } from "@/auth/use-auth"; +import { BillingPermissions } from "@/lib/permissions"; // ─── helpers ───────────────────────────────────────────────────────── @@ -72,6 +74,9 @@ export function InvoiceDetailPage() { const { invoiceId = "" } = useParams<{ invoiceId: string }>(); const navigate = useNavigate(); const queryClient = useQueryClient(); + const { user: currentUser } = useAuth(); + // Issue / mark-paid / void and PDF download all require Billing.Manage on the server. + const canManageBilling = (currentUser?.permissions ?? []).includes(BillingPermissions.Manage); const query = useQuery({ queryKey: ["billing", "invoice", invoiceId], @@ -179,18 +184,20 @@ export function InvoiceDetailPage() { } > - + {canManageBilling && ( + + )} ) : null} @@ -202,10 +209,16 @@ export function InvoiceDetailPage() { description={ invoice ? `${invoice.lineItems.length} line${invoice.lineItems.length === 1 ? "" : "s"}` - : "Loading…" + : query.isError + ? "Unavailable" + : "Loading…" } > - {query.isLoading ? ( + {query.isError ? ( +
+ {describe(query.error, "Failed to load line items.")} +
+ ) : query.isLoading ? (