@burdenoff/microfe-theglobalfuel 2026.705.2 → 2026.705.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/theglobalfuel/hooks/useCursorPager.js +6 -15
- package/dist/theglobalfuel/hooks/useCursorPager.js.map +1 -1
- package/dist/theglobalfuel/pages/assets/AssetsPage.js +99 -99
- package/dist/theglobalfuel/pages/assets/AssetsPage.js.map +1 -1
- package/dist/theglobalfuel/pages/compliance/CompliancePage.js +250 -250
- package/dist/theglobalfuel/pages/compliance/CompliancePage.js.map +1 -1
- package/dist/theglobalfuel/pages/contracts/ContractsPage.js +2 -2
- package/dist/theglobalfuel/pages/contracts/ContractsPage.js.map +1 -1
- package/dist/theglobalfuel/pages/emissions/EmissionsPage.js +174 -174
- package/dist/theglobalfuel/pages/emissions/EmissionsPage.js.map +1 -1
- package/dist/theglobalfuel/pages/inventory/InventoryPage.js +60 -60
- package/dist/theglobalfuel/pages/inventory/InventoryPage.js.map +1 -1
- package/dist/theglobalfuel/pages/production/ProductionPage.js +2 -2
- package/dist/theglobalfuel/pages/production/ProductionPage.js.map +1 -1
- package/dist/theglobalfuel/pages/reserves/ReservesPage.js +152 -152
- package/dist/theglobalfuel/pages/reserves/ReservesPage.js.map +1 -1
- package/dist/theglobalfuel/pages/shipments/ShipmentsPage.js +6 -6
- package/dist/theglobalfuel/pages/shipments/ShipmentsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompliancePage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/compliance/CompliancePage.tsx"],"sourcesContent":["/**\n * Compliance — certifications, licenses & permits register.\n * @module theglobalfuel/pages/compliance\n *\n * Reads `theListComplianceCerts` (a cursor connection — `{ items, pageInfo }`);\n * writes `theCreateComplianceCert` and lifecycle transitions via\n * `theTransitionComplianceCert`. Every figure comes from the real\n * `wspace-theglobalfuel-svc` contract — no mock data.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, ScrollText, BadgeCheck, Clock, AlertTriangle, Info } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n StatusBadge,\n DataTable,\n type Column,\n FilterTabs,\n type FilterOption,\n LoadingView,\n ErrorView,\n EmptyState,\n EvidenceUpload,\n FileLink,\n TheTags,\n TheDiscussion,\n Pagination,\n} from '../../components';\nimport {\n useTheComplianceCerts,\n useTheCreateComplianceCert,\n useTheTransitionComplianceCert,\n useTheAssets,\n useCursorPager,\n useNotify,\n type UploadedTheFile,\n} from '../../hooks';\nimport { useTheGlobalFuel } from '../../context';\nimport {\n formatDate,\n formatEnumLabel,\n isExpired,\n toBackendDate,\n COMPLIANCE_TRANSITIONS,\n nextStatuses,\n} from '../../utils';\nimport type {\n TheComplianceCert,\n TheComplianceCategory,\n TheComplianceStatus,\n} from '../../types';\n\n// ── Enum option sets (mirror svc) ────────────────────────────────────────────\nconst STATUS_FILTERS: FilterOption<TheComplianceStatus | 'ALL'>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'PENDING', label: 'Pending' },\n { value: 'SUBMITTED', label: 'Submitted' },\n { value: 'APPROVED', label: 'Approved' },\n { value: 'REJECTED', label: 'Rejected' },\n { value: 'EXPIRED', label: 'Expired' },\n { value: 'REVOKED', label: 'Revoked' },\n];\n\nconst CATEGORIES: TheComplianceCategory[] = [\n 'EXTRACTION_LICENSE',\n 'ENVIRONMENTAL_PERMIT',\n 'EXPORT_LICENSE',\n 'SAFETY_AUDIT',\n 'EMISSIONS_REPORT',\n 'CARBON_CREDIT',\n 'CHAIN_OF_CUSTODY',\n];\n\nconst THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;\n\nconst PAGE_SIZE = 25;\n\n/** True when a cert is still valid but expires within the next 30 days. */\nfunction isExpiringSoon(date: string | null | undefined): boolean {\n if (!date) return false;\n const d = new Date(date);\n if (Number.isNaN(d.getTime())) return false;\n const diff = d.getTime() - Date.now();\n return diff >= 0 && diff <= THIRTY_DAYS_MS;\n}\n\ninterface CreateFormState {\n title: string;\n category: TheComplianceCategory;\n assetId: string;\n authority: string;\n certNumber: string;\n issuedAt: string;\n expiresAt: string;\n notes: string;\n}\n\nconst EMPTY_FORM: CreateFormState = {\n title: '',\n category: 'EXTRACTION_LICENSE',\n assetId: '',\n authority: '',\n certNumber: '',\n issuedAt: '',\n expiresAt: '',\n notes: '',\n};\n\nexport function CompliancePage() {\n const { currentUser } = useTheGlobalFuel();\n const notify = useNotify();\n\n const [status, setStatus] = useState<TheComplianceStatus | 'ALL'>('ALL');\n const [category, setCategory] = useState<TheComplianceCategory | 'ALL'>('ALL');\n const [assetFilter, setAssetFilter] = useState<string>('ALL');\n const pager = useCursorPager();\n\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<CreateFormState>(EMPTY_FORM);\n const [evidence, setEvidence] = useState<UploadedTheFile | null>(null);\n const [formError, setFormError] = useState<string | null>(null);\n\n // Track the cert whose transition is in flight, so only that row's Select\n // disables (transitionCert.isPending is shared across all rows).\n const [transitioningId, setTransitioningId] = useState<string | null>(null);\n // Details drawer — surfaces Tags + Discussion (reuses tags/conversations svc).\n const [detailsCert, setDetailsCert] = useState<TheComplianceCert | null>(null);\n const [detailsOpen, setDetailsOpen] = useState(false);\n\n // Assets feed both the filter Select and the create dialog's asset picker.\n const assets = useTheAssets({ pagination: { limit: 100 } });\n const assetItems = assets.data?.items ?? [];\n\n // Unfiltered register drives the header KPIs (portfolio-wide, filter-agnostic).\n // Broad limit so the KPIs stay representative and don't reflect a single page.\n const allCerts = useTheComplianceCerts(undefined, undefined, undefined, { limit: 200 });\n // Filtered register drives the (cursor-paginated) table.\n const { data, isLoading, isError, refetch } = useTheComplianceCerts(\n assetFilter === 'ALL' ? undefined : assetFilter,\n category === 'ALL' ? undefined : category,\n status === 'ALL' ? undefined : status,\n { limit: PAGE_SIZE, cursor: pager.cursor }\n );\n\n const createCert = useTheCreateComplianceCert();\n const transitionCert = useTheTransitionComplianceCert();\n\n const rows = useMemo(() => data?.items ?? [], [data]);\n const filtersActive = status !== 'ALL' || category !== 'ALL' || assetFilter !== 'ALL';\n\n // ── Header metrics (portfolio-wide) ────────────────────────────────────────\n const portfolio = useMemo(() => allCerts.data?.items ?? [], [allCerts.data]);\n const total = portfolio.length;\n const approvedCount = portfolio.filter((c) => c.status === 'APPROVED').length;\n const pendingCount = portfolio.filter((c) => c.status === 'PENDING').length;\n const atRiskCount = portfolio.filter(\n (c) => c.status === 'EXPIRED' || isExpired(c.expiresAt) || isExpiringSoon(c.expiresAt)\n ).length;\n\n // Per-status counts for the filter tabs.\n const statusCounts = useMemo(() => {\n const counts: Record<string, number> = {};\n for (const c of portfolio) counts[c.status] = (counts[c.status] ?? 0) + 1;\n return counts;\n }, [portfolio]);\n const statusFilterOptions = STATUS_FILTERS.map((opt) => ({\n ...opt,\n count: opt.value === 'ALL' ? total : (statusCounts[opt.value] ?? 0),\n }));\n\n const resetForm = () => {\n setForm(EMPTY_FORM);\n setEvidence(null);\n setFormError(null);\n };\n\n const handleCreate = async () => {\n if (!form.title.trim()) {\n setFormError('A certification title is required.');\n return;\n }\n setFormError(null);\n try {\n await createCert.mutateAsync({\n title: form.title.trim(),\n category: form.category,\n assetId: form.assetId || null,\n authority: form.authority.trim() || null,\n certNumber: form.certNumber.trim() || null,\n ownerId: currentUser?.id || null,\n issuedAt: toBackendDate(form.issuedAt) ?? null,\n expiresAt: toBackendDate(form.expiresAt) ?? null,\n evidenceFileId: evidence?.fileId ?? null,\n notes: form.notes.trim() || null,\n });\n notify.success('Certification created');\n setDialogOpen(false);\n resetForm();\n } catch (err) {\n notify.fromError(err);\n setFormError(err instanceof Error ? err.message : 'Failed to create certification.');\n }\n };\n\n // ── Columns ────────────────────────────────────────────────────────────────\n const columns: Column<TheComplianceCert>[] = [\n {\n key: 'title',\n header: 'Certification',\n render: (c) => (\n <div className=\"min-w-0\">\n <p className=\"truncate font-semibold text-text-primary\">{c.title}</p>\n <p className=\"truncate text-xs text-text-tertiary\">{formatEnumLabel(c.category)}</p>\n </div>\n ),\n },\n {\n key: 'category',\n header: 'Category',\n hideOnMobile: true,\n render: (c) => <span className=\"text-text-secondary\">{formatEnumLabel(c.category)}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (c) => <StatusBadge status={c.status} />,\n },\n {\n key: 'authority',\n header: 'Authority',\n hideOnMobile: true,\n render: (c) =>\n c.authority ? (\n <span className=\"text-text-secondary\">{c.authority}</span>\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'certNumber',\n header: 'Cert. no.',\n hideOnMobile: true,\n render: (c) =>\n c.certNumber ? (\n <span className=\"tabular-nums text-text-secondary\">{c.certNumber}</span>\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'issued',\n header: 'Issued',\n align: 'right',\n hideOnMobile: true,\n render: (c) => <span className=\"text-text-tertiary\">{formatDate(c.issuedAt)}</span>,\n },\n {\n key: 'expires',\n header: 'Expires',\n align: 'right',\n render: (c) => {\n if (!c.expiresAt) return <span className=\"text-text-tertiary\">—</span>;\n const expired = isExpired(c.expiresAt);\n const soon = !expired && isExpiringSoon(c.expiresAt);\n return (\n <div className=\"flex items-center justify-end gap-2\">\n <span className=\"tabular-nums text-text-secondary\">{formatDate(c.expiresAt)}</span>\n {expired ? (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-error-bg-subtle px-2 py-0.5 text-xs font-medium text-status-error-text\">\n <AlertTriangle className=\"h-3 w-3\" aria-hidden=\"true\" /> Expired\n </span>\n ) : soon ? (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text\">\n <Clock className=\"h-3 w-3\" aria-hidden=\"true\" /> Expiring soon\n </span>\n ) : null}\n </div>\n );\n },\n },\n {\n key: 'evidence',\n header: 'Evidence',\n hideOnMobile: true,\n render: (c) =>\n c.evidence?.id ? (\n <FileLink fileId={c.evidence.id} />\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'details',\n header: '',\n align: 'right',\n render: (c) => (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setDetailsCert(c);\n setDetailsOpen(true);\n }}\n aria-label={`View details for ${c.title}`}\n >\n <Info className=\"h-4 w-4\" />\n <span className=\"ml-1.5 hidden sm:inline\">Details</span>\n </Button>\n ),\n },\n {\n key: 'action',\n header: '',\n align: 'right',\n render: (c) => {\n // Offer ONLY the legal next states; a terminal status (e.g. REVOKED)\n // has none, so the control is disabled. The current status shows on the\n // trigger via the placeholder (it is not itself a selectable option).\n const nexts = nextStatuses(COMPLIANCE_TRANSITIONS, c.status);\n const terminal = nexts.length === 0;\n return (\n <Select\n value={c.status}\n disabled={terminal || transitioningId === c.id}\n onValueChange={(v) => {\n if (v === c.status) return;\n setTransitioningId(c.id);\n transitionCert.mutate(\n { id: c.id, status: v as TheComplianceStatus },\n {\n onSuccess: () => notify.success(`Status updated to ${formatEnumLabel(v)}`),\n onError: (e) => notify.fromError(e),\n onSettled: () => setTransitioningId(null),\n }\n );\n }}\n >\n <SelectTrigger\n className=\"h-8 w-36\"\n aria-label={terminal ? `Status for ${c.title}` : `Change status for ${c.title}`}\n >\n <SelectValue placeholder={formatEnumLabel(c.status)} />\n </SelectTrigger>\n <SelectContent>\n {nexts.map((s) => (\n <SelectItem key={s} value={s}>\n {formatEnumLabel(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n },\n },\n ];\n\n return (\n <PageLayout\n title=\"Compliance\"\n description=\"Licenses, permits & certifications — issuance, expiry & audit evidence across the resource portfolio.\"\n actions={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New certification\n </Button>\n }\n >\n {/* KPI strip */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <MetricsCard\n icon={<ScrollText className=\"h-5 w-5\" />}\n value={total}\n label=\"Total certifications\"\n featured\n />\n <MetricsCard\n icon={<BadgeCheck className=\"h-5 w-5\" />}\n value={approvedCount}\n label=\"Approved\"\n accent=\"success\"\n />\n <MetricsCard\n icon={<Clock className=\"h-5 w-5\" />}\n value={pendingCount}\n label=\"Pending\"\n />\n <MetricsCard\n icon={<AlertTriangle className=\"h-5 w-5\" />}\n value={atRiskCount}\n label=\"Expired / expiring\"\n accent=\"warning\"\n pill={\n atRiskCount > 0 ? (\n <span className=\"rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text\">\n Action needed\n </span>\n ) : undefined\n }\n />\n </div>\n\n {/* Filters */}\n <div className=\"mb-5 mt-6 flex flex-wrap items-center gap-3\">\n <FilterTabs\n options={statusFilterOptions}\n value={status}\n onChange={(v) => {\n setStatus(v);\n pager.reset();\n }}\n />\n <Select\n value={category}\n onValueChange={(v) => {\n setCategory(v as TheComplianceCategory | 'ALL');\n pager.reset();\n }}\n >\n <SelectTrigger className=\"w-52\" aria-label=\"Filter by category\">\n <SelectValue placeholder=\"All categories\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All categories</SelectItem>\n {CATEGORIES.map((cat) => (\n <SelectItem key={cat} value={cat}>\n {formatEnumLabel(cat)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select\n value={assetFilter}\n onValueChange={(v) => {\n setAssetFilter(v);\n pager.reset();\n }}\n >\n <SelectTrigger className=\"w-52\" aria-label=\"Filter by asset\">\n <SelectValue placeholder=\"All assets\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All assets</SelectItem>\n {assetItems.map((a) => (\n <SelectItem key={a.id} value={a.id}>\n {a.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading certifications…\" />\n ) : isError ? (\n <ErrorView message=\"Couldn't load certifications.\" onRetry={() => refetch()} />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ScrollText className=\"h-10 w-10\" />}\n title=\"No certifications yet\"\n description={\n filtersActive\n ? 'No certifications match these filters.'\n : 'Register your first license, permit or certification to track issuance, expiry and audit evidence.'\n }\n action={\n !filtersActive ? (\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New certification\n </Button>\n ) : undefined\n }\n />\n ) : (\n <>\n <DataTable columns={columns} rows={rows} rowKey={(c) => c.id} />\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo?.hasMore ?? false}\n onPageChange={(p) => pager.onPageChange(p, data?.pageInfo?.cursor)}\n />\n </>\n )}\n\n {/* New certification dialog */}\n <ResponsiveDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n setDialogOpen(open);\n if (!open) resetForm();\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New certification</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Register a license, permit or certification and attach its supporting evidence.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form\n onSubmit={(e) => {\n e.preventDefault();\n void handleCreate();\n }}\n >\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccTitle\">Title *</Label>\n <Input\n id=\"ccTitle\"\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n placeholder=\"Block 7 extraction license\"\n />\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccCategory\">Category</Label>\n <Select\n value={form.category}\n onValueChange={(v) =>\n setForm({ ...form, category: v as TheComplianceCategory })\n }\n >\n <SelectTrigger id=\"ccCategory\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CATEGORIES.map((cat) => (\n <SelectItem key={cat} value={cat}>\n {formatEnumLabel(cat)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccAsset\">Asset</Label>\n <Select\n value={form.assetId}\n onValueChange={(v) => setForm({ ...form, assetId: v })}\n >\n <SelectTrigger id=\"ccAsset\">\n <SelectValue placeholder=\"Optional\" />\n </SelectTrigger>\n <SelectContent>\n {assetItems.map((a) => (\n <SelectItem key={a.id} value={a.id}>\n {a.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccAuthority\">Issuing authority</Label>\n <Input\n id=\"ccAuthority\"\n value={form.authority}\n onChange={(e) => setForm({ ...form, authority: e.target.value })}\n placeholder=\"Ministry of Petroleum & Natural Gas\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccNumber\">Certificate number</Label>\n <Input\n id=\"ccNumber\"\n value={form.certNumber}\n onChange={(e) => setForm({ ...form, certNumber: e.target.value })}\n placeholder=\"EXT-2026-00471\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccIssued\">Issued</Label>\n <Input\n id=\"ccIssued\"\n type=\"date\"\n value={form.issuedAt}\n onChange={(e) => setForm({ ...form, issuedAt: e.target.value })}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccExpires\">Expires</Label>\n <Input\n id=\"ccExpires\"\n type=\"date\"\n value={form.expiresAt}\n onChange={(e) => setForm({ ...form, expiresAt: e.target.value })}\n />\n </div>\n </div>\n\n <EvidenceUpload\n id=\"evidence\"\n label=\"Evidence document\"\n accept=\"image/*,application/pdf\"\n value={evidence}\n onChange={setEvidence}\n hint=\"Attach the signed certificate, permit or audit report (PDF or image).\"\n />\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccNotes\">Notes</Label>\n <Textarea\n id=\"ccNotes\"\n rows={2}\n value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })}\n placeholder=\"Optional context about scope, conditions or renewal.\"\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setDialogOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={createCert.isPending}>\n {createCert.isPending ? 'Creating…' : 'Create certification'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Certification details — summary + Tags + Discussion (reuse) */}\n <ResponsiveDialog\n open={detailsOpen}\n onOpenChange={(open) => {\n setDetailsOpen(open);\n if (!open) setDetailsCert(null);\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-xl\">\n {detailsCert && (\n <>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{detailsCert.title}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {formatEnumLabel(detailsCert.category)}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"max-h-[70vh] space-y-6 overflow-y-auto py-2\">\n {/* Summary */}\n <dl className=\"grid grid-cols-2 gap-x-4 gap-y-3 text-sm\">\n <div className=\"space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Status</dt>\n <dd>\n <StatusBadge status={detailsCert.status} />\n </dd>\n </div>\n <div className=\"space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Asset</dt>\n <dd className=\"text-text-secondary\">\n {detailsCert.assetId\n ? (assetItems.find((a) => a.id === detailsCert.assetId)?.name ?? '—')\n : '—'}\n </dd>\n </div>\n <div className=\"space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Authority</dt>\n <dd className=\"text-text-secondary\">{detailsCert.authority || '—'}</dd>\n </div>\n <div className=\"space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Certificate no.</dt>\n <dd className=\"tabular-nums text-text-secondary\">\n {detailsCert.certNumber || '—'}\n </dd>\n </div>\n <div className=\"space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Issued</dt>\n <dd className=\"text-text-secondary\">{formatDate(detailsCert.issuedAt)}</dd>\n </div>\n <div className=\"space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Expires</dt>\n <dd className=\"text-text-secondary\">{formatDate(detailsCert.expiresAt)}</dd>\n </div>\n {detailsCert.evidence?.id && (\n <div className=\"col-span-2 space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Evidence</dt>\n <dd>\n <FileLink fileId={detailsCert.evidence.id} />\n </dd>\n </div>\n )}\n {detailsCert.notes && (\n <div className=\"col-span-2 space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Notes</dt>\n <dd className=\"whitespace-pre-wrap text-text-secondary\">\n {detailsCert.notes}\n </dd>\n </div>\n )}\n </dl>\n\n {/* Tags — reuses wspace-tags-svc, keyed by the cert id */}\n <div className=\"border-t border-border-seam pt-4\">\n <TheTags entityType=\"theglobalfuel-compliance\" entityId={detailsCert.id} />\n </div>\n\n {/* Discussion — reuses wspace-conversations-svc, keyed by the cert id */}\n <div className=\"border-t border-border-seam pt-4\">\n <TheDiscussion\n entityType=\"theglobalfuel-compliance\"\n entityId={detailsCert.id}\n title=\"Discussion\"\n description=\"Coordinate review, renewal & audit evidence for this certification.\"\n />\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setDetailsOpen(false)}>\n Close\n </Button>\n </ResponsiveDialogFooter>\n </>\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CompliancePage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/compliance/CompliancePage.tsx"],"sourcesContent":["/**\n * Compliance — certifications, licenses & permits register.\n * @module theglobalfuel/pages/compliance\n *\n * Reads `theListComplianceCerts` (a cursor connection — `{ items, pageInfo }`);\n * writes `theCreateComplianceCert` and lifecycle transitions via\n * `theTransitionComplianceCert`. Every figure comes from the real\n * `wspace-theglobalfuel-svc` contract — no mock data.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, ScrollText, BadgeCheck, Clock, AlertTriangle, Info } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n StatusBadge,\n DataTable,\n type Column,\n FilterTabs,\n type FilterOption,\n LoadingView,\n ErrorView,\n EmptyState,\n EvidenceUpload,\n FileLink,\n TheTags,\n TheDiscussion,\n Pagination,\n} from '../../components';\nimport {\n useTheComplianceCerts,\n useTheCreateComplianceCert,\n useTheTransitionComplianceCert,\n useTheAssets,\n useCursorPager,\n useNotify,\n type UploadedTheFile,\n} from '../../hooks';\nimport { useTheGlobalFuel } from '../../context';\nimport {\n formatDate,\n formatEnumLabel,\n isExpired,\n toBackendDate,\n COMPLIANCE_TRANSITIONS,\n nextStatuses,\n} from '../../utils';\nimport type {\n TheComplianceCert,\n TheComplianceCategory,\n TheComplianceStatus,\n} from '../../types';\n\n// ── Enum option sets (mirror svc) ────────────────────────────────────────────\nconst STATUS_FILTERS: FilterOption<TheComplianceStatus | 'ALL'>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'PENDING', label: 'Pending' },\n { value: 'SUBMITTED', label: 'Submitted' },\n { value: 'APPROVED', label: 'Approved' },\n { value: 'REJECTED', label: 'Rejected' },\n { value: 'EXPIRED', label: 'Expired' },\n { value: 'REVOKED', label: 'Revoked' },\n];\n\nconst CATEGORIES: TheComplianceCategory[] = [\n 'EXTRACTION_LICENSE',\n 'ENVIRONMENTAL_PERMIT',\n 'EXPORT_LICENSE',\n 'SAFETY_AUDIT',\n 'EMISSIONS_REPORT',\n 'CARBON_CREDIT',\n 'CHAIN_OF_CUSTODY',\n];\n\nconst THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;\n\nconst PAGE_SIZE = 25;\n\n/** True when a cert is still valid but expires within the next 30 days. */\nfunction isExpiringSoon(date: string | null | undefined): boolean {\n if (!date) return false;\n const d = new Date(date);\n if (Number.isNaN(d.getTime())) return false;\n const diff = d.getTime() - Date.now();\n return diff >= 0 && diff <= THIRTY_DAYS_MS;\n}\n\ninterface CreateFormState {\n title: string;\n category: TheComplianceCategory;\n assetId: string;\n authority: string;\n certNumber: string;\n issuedAt: string;\n expiresAt: string;\n notes: string;\n}\n\nconst EMPTY_FORM: CreateFormState = {\n title: '',\n category: 'EXTRACTION_LICENSE',\n assetId: '',\n authority: '',\n certNumber: '',\n issuedAt: '',\n expiresAt: '',\n notes: '',\n};\n\nexport function CompliancePage() {\n const { currentUser } = useTheGlobalFuel();\n const notify = useNotify();\n\n const [status, setStatus] = useState<TheComplianceStatus | 'ALL'>('ALL');\n const [category, setCategory] = useState<TheComplianceCategory | 'ALL'>('ALL');\n const [assetFilter, setAssetFilter] = useState<string>('ALL');\n const pager = useCursorPager();\n\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<CreateFormState>(EMPTY_FORM);\n const [evidence, setEvidence] = useState<UploadedTheFile | null>(null);\n const [formError, setFormError] = useState<string | null>(null);\n\n // Track the cert whose transition is in flight, so only that row's Select\n // disables (transitionCert.isPending is shared across all rows).\n const [transitioningId, setTransitioningId] = useState<string | null>(null);\n // Details drawer — surfaces Tags + Discussion (reuses tags/conversations svc).\n const [detailsCert, setDetailsCert] = useState<TheComplianceCert | null>(null);\n const [detailsOpen, setDetailsOpen] = useState(false);\n\n // Assets feed both the filter Select and the create dialog's asset picker.\n const assets = useTheAssets({ pagination: { limit: 100 } });\n const assetItems = assets.data?.items ?? [];\n\n // Unfiltered register drives the header KPIs (portfolio-wide, filter-agnostic).\n // Broad limit so the KPIs stay representative and don't reflect a single page.\n const allCerts = useTheComplianceCerts(undefined, undefined, undefined, { limit: 200 });\n // Filtered register drives the (cursor-paginated) table.\n const { data, isLoading, isError, refetch } = useTheComplianceCerts(\n assetFilter === 'ALL' ? undefined : assetFilter,\n category === 'ALL' ? undefined : category,\n status === 'ALL' ? undefined : status,\n { limit: PAGE_SIZE, offset: pager.page * PAGE_SIZE }\n );\n\n const createCert = useTheCreateComplianceCert();\n const transitionCert = useTheTransitionComplianceCert();\n\n const rows = useMemo(() => data?.items ?? [], [data]);\n const filtersActive = status !== 'ALL' || category !== 'ALL' || assetFilter !== 'ALL';\n\n // ── Header metrics (portfolio-wide) ────────────────────────────────────────\n const portfolio = useMemo(() => allCerts.data?.items ?? [], [allCerts.data]);\n const total = portfolio.length;\n const approvedCount = portfolio.filter((c) => c.status === 'APPROVED').length;\n const pendingCount = portfolio.filter((c) => c.status === 'PENDING').length;\n const atRiskCount = portfolio.filter(\n (c) => c.status === 'EXPIRED' || isExpired(c.expiresAt) || isExpiringSoon(c.expiresAt)\n ).length;\n\n // Per-status counts for the filter tabs.\n const statusCounts = useMemo(() => {\n const counts: Record<string, number> = {};\n for (const c of portfolio) counts[c.status] = (counts[c.status] ?? 0) + 1;\n return counts;\n }, [portfolio]);\n const statusFilterOptions = STATUS_FILTERS.map((opt) => ({\n ...opt,\n count: opt.value === 'ALL' ? total : (statusCounts[opt.value] ?? 0),\n }));\n\n const resetForm = () => {\n setForm(EMPTY_FORM);\n setEvidence(null);\n setFormError(null);\n };\n\n const handleCreate = async () => {\n if (!form.title.trim()) {\n setFormError('A certification title is required.');\n return;\n }\n setFormError(null);\n try {\n await createCert.mutateAsync({\n title: form.title.trim(),\n category: form.category,\n assetId: form.assetId || null,\n authority: form.authority.trim() || null,\n certNumber: form.certNumber.trim() || null,\n ownerId: currentUser?.id || null,\n issuedAt: toBackendDate(form.issuedAt) ?? null,\n expiresAt: toBackendDate(form.expiresAt) ?? null,\n evidenceFileId: evidence?.fileId ?? null,\n notes: form.notes.trim() || null,\n });\n notify.success('Certification created');\n setDialogOpen(false);\n resetForm();\n } catch (err) {\n notify.fromError(err);\n setFormError(err instanceof Error ? err.message : 'Failed to create certification.');\n }\n };\n\n // ── Columns ────────────────────────────────────────────────────────────────\n const columns: Column<TheComplianceCert>[] = [\n {\n key: 'title',\n header: 'Certification',\n render: (c) => (\n <div className=\"min-w-0\">\n <p className=\"truncate font-semibold text-text-primary\">{c.title}</p>\n <p className=\"truncate text-xs text-text-tertiary\">{formatEnumLabel(c.category)}</p>\n </div>\n ),\n },\n {\n key: 'category',\n header: 'Category',\n hideOnMobile: true,\n render: (c) => <span className=\"text-text-secondary\">{formatEnumLabel(c.category)}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (c) => <StatusBadge status={c.status} />,\n },\n {\n key: 'authority',\n header: 'Authority',\n hideOnMobile: true,\n render: (c) =>\n c.authority ? (\n <span className=\"text-text-secondary\">{c.authority}</span>\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'certNumber',\n header: 'Cert. no.',\n hideOnMobile: true,\n render: (c) =>\n c.certNumber ? (\n <span className=\"tabular-nums text-text-secondary\">{c.certNumber}</span>\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'issued',\n header: 'Issued',\n align: 'right',\n hideOnMobile: true,\n render: (c) => <span className=\"text-text-tertiary\">{formatDate(c.issuedAt)}</span>,\n },\n {\n key: 'expires',\n header: 'Expires',\n align: 'right',\n render: (c) => {\n if (!c.expiresAt) return <span className=\"text-text-tertiary\">—</span>;\n const expired = isExpired(c.expiresAt);\n const soon = !expired && isExpiringSoon(c.expiresAt);\n return (\n <div className=\"flex items-center justify-end gap-2\">\n <span className=\"tabular-nums text-text-secondary\">{formatDate(c.expiresAt)}</span>\n {expired ? (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-error-bg-subtle px-2 py-0.5 text-xs font-medium text-status-error-text\">\n <AlertTriangle className=\"h-3 w-3\" aria-hidden=\"true\" /> Expired\n </span>\n ) : soon ? (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text\">\n <Clock className=\"h-3 w-3\" aria-hidden=\"true\" /> Expiring soon\n </span>\n ) : null}\n </div>\n );\n },\n },\n {\n key: 'evidence',\n header: 'Evidence',\n hideOnMobile: true,\n render: (c) =>\n c.evidence?.id ? (\n <FileLink fileId={c.evidence.id} />\n ) : (\n <span className=\"text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'details',\n header: '',\n align: 'right',\n render: (c) => (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setDetailsCert(c);\n setDetailsOpen(true);\n }}\n aria-label={`View details for ${c.title}`}\n >\n <Info className=\"h-4 w-4\" />\n <span className=\"ml-1.5 hidden sm:inline\">Details</span>\n </Button>\n ),\n },\n {\n key: 'action',\n header: '',\n align: 'right',\n render: (c) => {\n // Offer ONLY the legal next states; a terminal status (e.g. REVOKED)\n // has none, so the control is disabled. The current status shows on the\n // trigger via the placeholder (it is not itself a selectable option).\n const nexts = nextStatuses(COMPLIANCE_TRANSITIONS, c.status);\n const terminal = nexts.length === 0;\n return (\n <Select\n value={c.status}\n disabled={terminal || transitioningId === c.id}\n onValueChange={(v) => {\n if (v === c.status) return;\n setTransitioningId(c.id);\n transitionCert.mutate(\n { id: c.id, status: v as TheComplianceStatus },\n {\n onSuccess: () => notify.success(`Status updated to ${formatEnumLabel(v)}`),\n onError: (e) => notify.fromError(e),\n onSettled: () => setTransitioningId(null),\n }\n );\n }}\n >\n <SelectTrigger\n className=\"h-8 w-36\"\n aria-label={terminal ? `Status for ${c.title}` : `Change status for ${c.title}`}\n >\n <SelectValue placeholder={formatEnumLabel(c.status)} />\n </SelectTrigger>\n <SelectContent>\n {nexts.map((s) => (\n <SelectItem key={s} value={s}>\n {formatEnumLabel(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n },\n },\n ];\n\n return (\n <PageLayout\n title=\"Compliance\"\n description=\"Licenses, permits & certifications — issuance, expiry & audit evidence across the resource portfolio.\"\n actions={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New certification\n </Button>\n }\n >\n {/* KPI strip */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <MetricsCard\n icon={<ScrollText className=\"h-5 w-5\" />}\n value={total}\n label=\"Total certifications\"\n featured\n />\n <MetricsCard\n icon={<BadgeCheck className=\"h-5 w-5\" />}\n value={approvedCount}\n label=\"Approved\"\n accent=\"success\"\n />\n <MetricsCard\n icon={<Clock className=\"h-5 w-5\" />}\n value={pendingCount}\n label=\"Pending\"\n />\n <MetricsCard\n icon={<AlertTriangle className=\"h-5 w-5\" />}\n value={atRiskCount}\n label=\"Expired / expiring\"\n accent=\"warning\"\n pill={\n atRiskCount > 0 ? (\n <span className=\"rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text\">\n Action needed\n </span>\n ) : undefined\n }\n />\n </div>\n\n {/* Filters */}\n <div className=\"mb-5 mt-6 flex flex-wrap items-center gap-3\">\n <FilterTabs\n options={statusFilterOptions}\n value={status}\n onChange={(v) => {\n setStatus(v);\n pager.reset();\n }}\n />\n <Select\n value={category}\n onValueChange={(v) => {\n setCategory(v as TheComplianceCategory | 'ALL');\n pager.reset();\n }}\n >\n <SelectTrigger className=\"w-52\" aria-label=\"Filter by category\">\n <SelectValue placeholder=\"All categories\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All categories</SelectItem>\n {CATEGORIES.map((cat) => (\n <SelectItem key={cat} value={cat}>\n {formatEnumLabel(cat)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select\n value={assetFilter}\n onValueChange={(v) => {\n setAssetFilter(v);\n pager.reset();\n }}\n >\n <SelectTrigger className=\"w-52\" aria-label=\"Filter by asset\">\n <SelectValue placeholder=\"All assets\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All assets</SelectItem>\n {assetItems.map((a) => (\n <SelectItem key={a.id} value={a.id}>\n {a.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading certifications…\" />\n ) : isError ? (\n <ErrorView message=\"Couldn't load certifications.\" onRetry={() => refetch()} />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ScrollText className=\"h-10 w-10\" />}\n title=\"No certifications yet\"\n description={\n filtersActive\n ? 'No certifications match these filters.'\n : 'Register your first license, permit or certification to track issuance, expiry and audit evidence.'\n }\n action={\n !filtersActive ? (\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New certification\n </Button>\n ) : undefined\n }\n />\n ) : (\n <>\n <DataTable columns={columns} rows={rows} rowKey={(c) => c.id} />\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo?.hasMore ?? false}\n onPageChange={pager.onPageChange}\n />\n </>\n )}\n\n {/* New certification dialog */}\n <ResponsiveDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n setDialogOpen(open);\n if (!open) resetForm();\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New certification</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Register a license, permit or certification and attach its supporting evidence.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form\n onSubmit={(e) => {\n e.preventDefault();\n void handleCreate();\n }}\n >\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccTitle\">Title *</Label>\n <Input\n id=\"ccTitle\"\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n placeholder=\"Block 7 extraction license\"\n />\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccCategory\">Category</Label>\n <Select\n value={form.category}\n onValueChange={(v) =>\n setForm({ ...form, category: v as TheComplianceCategory })\n }\n >\n <SelectTrigger id=\"ccCategory\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CATEGORIES.map((cat) => (\n <SelectItem key={cat} value={cat}>\n {formatEnumLabel(cat)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccAsset\">Asset</Label>\n <Select\n value={form.assetId}\n onValueChange={(v) => setForm({ ...form, assetId: v })}\n >\n <SelectTrigger id=\"ccAsset\">\n <SelectValue placeholder=\"Optional\" />\n </SelectTrigger>\n <SelectContent>\n {assetItems.map((a) => (\n <SelectItem key={a.id} value={a.id}>\n {a.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccAuthority\">Issuing authority</Label>\n <Input\n id=\"ccAuthority\"\n value={form.authority}\n onChange={(e) => setForm({ ...form, authority: e.target.value })}\n placeholder=\"Ministry of Petroleum & Natural Gas\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccNumber\">Certificate number</Label>\n <Input\n id=\"ccNumber\"\n value={form.certNumber}\n onChange={(e) => setForm({ ...form, certNumber: e.target.value })}\n placeholder=\"EXT-2026-00471\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccIssued\">Issued</Label>\n <Input\n id=\"ccIssued\"\n type=\"date\"\n value={form.issuedAt}\n onChange={(e) => setForm({ ...form, issuedAt: e.target.value })}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccExpires\">Expires</Label>\n <Input\n id=\"ccExpires\"\n type=\"date\"\n value={form.expiresAt}\n onChange={(e) => setForm({ ...form, expiresAt: e.target.value })}\n />\n </div>\n </div>\n\n <EvidenceUpload\n id=\"evidence\"\n label=\"Evidence document\"\n accept=\"image/*,application/pdf\"\n value={evidence}\n onChange={setEvidence}\n hint=\"Attach the signed certificate, permit or audit report (PDF or image).\"\n />\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"ccNotes\">Notes</Label>\n <Textarea\n id=\"ccNotes\"\n rows={2}\n value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })}\n placeholder=\"Optional context about scope, conditions or renewal.\"\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setDialogOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={createCert.isPending}>\n {createCert.isPending ? 'Creating…' : 'Create certification'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Certification details — summary + Tags + Discussion (reuse) */}\n <ResponsiveDialog\n open={detailsOpen}\n onOpenChange={(open) => {\n setDetailsOpen(open);\n if (!open) setDetailsCert(null);\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-xl\">\n {detailsCert && (\n <>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{detailsCert.title}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {formatEnumLabel(detailsCert.category)}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"max-h-[70vh] space-y-6 overflow-y-auto py-2\">\n {/* Summary */}\n <dl className=\"grid grid-cols-2 gap-x-4 gap-y-3 text-sm\">\n <div className=\"space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Status</dt>\n <dd>\n <StatusBadge status={detailsCert.status} />\n </dd>\n </div>\n <div className=\"space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Asset</dt>\n <dd className=\"text-text-secondary\">\n {detailsCert.assetId\n ? (assetItems.find((a) => a.id === detailsCert.assetId)?.name ?? '—')\n : '—'}\n </dd>\n </div>\n <div className=\"space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Authority</dt>\n <dd className=\"text-text-secondary\">{detailsCert.authority || '—'}</dd>\n </div>\n <div className=\"space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Certificate no.</dt>\n <dd className=\"tabular-nums text-text-secondary\">\n {detailsCert.certNumber || '—'}\n </dd>\n </div>\n <div className=\"space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Issued</dt>\n <dd className=\"text-text-secondary\">{formatDate(detailsCert.issuedAt)}</dd>\n </div>\n <div className=\"space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Expires</dt>\n <dd className=\"text-text-secondary\">{formatDate(detailsCert.expiresAt)}</dd>\n </div>\n {detailsCert.evidence?.id && (\n <div className=\"col-span-2 space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Evidence</dt>\n <dd>\n <FileLink fileId={detailsCert.evidence.id} />\n </dd>\n </div>\n )}\n {detailsCert.notes && (\n <div className=\"col-span-2 space-y-1\">\n <dt className=\"text-xs text-text-tertiary\">Notes</dt>\n <dd className=\"whitespace-pre-wrap text-text-secondary\">\n {detailsCert.notes}\n </dd>\n </div>\n )}\n </dl>\n\n {/* Tags — reuses wspace-tags-svc, keyed by the cert id */}\n <div className=\"border-t border-border-seam pt-4\">\n <TheTags entityType=\"theglobalfuel-compliance\" entityId={detailsCert.id} />\n </div>\n\n {/* Discussion — reuses wspace-conversations-svc, keyed by the cert id */}\n <div className=\"border-t border-border-seam pt-4\">\n <TheDiscussion\n entityType=\"theglobalfuel-compliance\"\n entityId={detailsCert.id}\n title=\"Discussion\"\n description=\"Coordinate review, renewal & audit evidence for this certification.\"\n />\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setDetailsOpen(false)}>\n Close\n </Button>\n </ResponsiveDialogFooter>\n </>\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -101,7 +101,7 @@ function L() {
|
|
|
101
101
|
commodity: H === "ALL" ? void 0 : H,
|
|
102
102
|
pagination: {
|
|
103
103
|
limit: N,
|
|
104
|
-
|
|
104
|
+
offset: U.page * N
|
|
105
105
|
}
|
|
106
106
|
}), X = p(), Z = ae(() => Y?.items ?? [], [Y]), Q = [
|
|
107
107
|
{
|
|
@@ -302,7 +302,7 @@ function L() {
|
|
|
302
302
|
pageSize: N,
|
|
303
303
|
itemCount: Z.length,
|
|
304
304
|
hasMore: Y?.pageInfo?.hasMore ?? !1,
|
|
305
|
-
onPageChange:
|
|
305
|
+
onPageChange: U.onPageChange
|
|
306
306
|
})] }),
|
|
307
307
|
/* @__PURE__ */ v(oe, {
|
|
308
308
|
open: me,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContractsPage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/contracts/ContractsPage.tsx"],"sourcesContent":["/**\n * Offtake contracts — list, filter, and create commodity purchase/sale contracts.\n * @module theglobalfuel/pages/contracts\n *\n * Reads `theListContracts`; writes `theCreateContract`.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, FileSignature } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataTable, type Column } from '../../components/DataTable';\nimport { FilterTabs, type FilterOption } from '../../components/FilterTabs';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { EmptyState } from '../../components/EmptyState';\nimport { Pagination } from '../../components/Pagination';\nimport { MiniBar } from '../../components/MiniBar';\nimport { useTheGlobalFuel } from '../../context';\nimport { useTheContracts, useTheCreateContract, useCursorPager, useNotify } from '../../hooks';\nimport { formatEnumLabel, formatMoney, formatUnit, formatVolume } from '../../utils/formatters';\nimport { toBackendDate } from '../../utils';\nimport type {\n TheOfftakeContract,\n TheContractStatus,\n TheContractSide,\n TheCommodityType,\n ThePricingBasis,\n TheUnit,\n} from '../../types';\n\nconst STATUS_FILTERS: FilterOption<TheContractStatus | 'ALL'>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'NEGOTIATION', label: 'Negotiation' },\n { value: 'SIGNED', label: 'Signed' },\n { value: 'ACTIVE', label: 'Active' },\n { value: 'COMPLETED', label: 'Completed' },\n { value: 'SUSPENDED', label: 'Suspended' },\n { value: 'TERMINATED', label: 'Terminated' },\n];\n\nconst SIDES: TheContractSide[] = ['BUY', 'SELL'];\nconst PRICING_BASES: ThePricingBasis[] = ['FIXED', 'INDEX_LINKED', 'FORMULA', 'SPOT'];\nconst COMMODITIES: TheCommodityType[] = [\n 'CRUDE_OIL',\n 'NATURAL_GAS',\n 'LNG',\n 'COAL',\n 'ELECTRICITY',\n 'HYDROGEN',\n 'BIOFUEL',\n 'IRON_ORE',\n 'COPPER',\n 'BAUXITE',\n 'GOLD',\n 'LITHIUM',\n 'RARE_EARTH',\n];\nconst UNITS: TheUnit[] = ['BARRELS', 'CUBIC_METERS', 'TONNES', 'KILOGRAMS', 'MWH', 'GJ', 'MCF'];\n\nconst PAGE_SIZE = 25;\n\nconst clampPercent = (value: number): number => Math.max(0, Math.min(100, value));\n\n/** Fraction of a contract's volume that has been delivered (0–100). */\nconst deliveryPct = (c: TheOfftakeContract): number =>\n c.contractedVolume > 0 ? clampPercent((c.deliveredVolume / c.contractedVolume) * 100) : 0;\n\ninterface CreateFormState {\n reference: string;\n counterparty: string;\n commodity: TheCommodityType;\n side: TheContractSide;\n pricingBasis: ThePricingBasis;\n pricePerUnit: string;\n priceIndex: string;\n currency: string;\n contractedVolume: string;\n unit: TheUnit;\n deliveryLocation: string;\n startDate: string;\n endDate: string;\n notes: string;\n}\n\nconst EMPTY_FORM: CreateFormState = {\n reference: '',\n counterparty: '',\n commodity: 'CRUDE_OIL',\n side: 'SELL',\n pricingBasis: 'FIXED',\n pricePerUnit: '',\n priceIndex: '',\n currency: 'USD',\n contractedVolume: '',\n unit: 'BARRELS',\n deliveryLocation: '',\n startDate: '',\n endDate: '',\n notes: '',\n};\n\nexport function ContractsPage() {\n const { navigateTo } = useTheGlobalFuel();\n const notify = useNotify();\n const [status, setStatus] = useState<TheContractStatus | 'ALL'>('ALL');\n const [side, setSide] = useState<TheContractSide | 'ALL'>('ALL');\n const [commodity, setCommodity] = useState<TheCommodityType | 'ALL'>('ALL');\n const pager = useCursorPager();\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<CreateFormState>(EMPTY_FORM);\n const [formError, setFormError] = useState<string | null>(null);\n\n const { data, isLoading, isError, refetch } = useTheContracts({\n status: status === 'ALL' ? undefined : status,\n side: side === 'ALL' ? undefined : side,\n commodity: commodity === 'ALL' ? undefined : commodity,\n pagination: { limit: PAGE_SIZE, cursor: pager.cursor },\n });\n const createContract = useTheCreateContract();\n\n const rows = useMemo(() => data?.items ?? [], [data]);\n\n const columns: Column<TheOfftakeContract>[] = [\n {\n key: 'reference',\n header: 'Contract',\n render: (c) => (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-accent\">\n <FileSignature className=\"h-4 w-4\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate font-semibold text-text-primary\">{c.reference}</p>\n <p className=\"truncate text-xs text-text-tertiary\">{c.counterparty}</p>\n </div>\n </div>\n ),\n },\n {\n key: 'side',\n header: 'Side',\n render: (c) => <StatusBadge status={c.side} />,\n },\n {\n key: 'commodity',\n header: 'Commodity',\n hideOnMobile: true,\n render: (c) => <span className=\"text-text-secondary\">{formatEnumLabel(c.commodity)}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (c) => <StatusBadge status={c.status} />,\n },\n {\n key: 'price',\n header: 'Price',\n align: 'right',\n hideOnMobile: true,\n sortValue: (c) => c.pricePerUnit,\n render: (c) => (\n <span className=\"tabular-nums text-text-secondary\">\n {formatMoney(c.pricePerUnit, c.currency)}\n <span className=\"text-text-tertiary\">/{formatUnit(c.unit)}</span>\n </span>\n ),\n },\n {\n key: 'delivery',\n header: 'Delivered',\n sortValue: (c) => deliveryPct(c),\n render: (c) => {\n const pct = deliveryPct(c);\n return (\n <div className=\"min-w-[9rem]\">\n <MiniBar\n value={pct}\n barClassName=\"bg-[var(--color-status-success-text)]\"\n className=\"mb-1\"\n />\n <p className=\"text-xs tabular-nums text-text-tertiary\">\n {formatVolume(c.deliveredVolume, c.unit)} / {formatVolume(c.contractedVolume, c.unit)}\n </p>\n </div>\n );\n },\n },\n {\n key: 'updated',\n header: 'Updated',\n align: 'right',\n hideOnMobile: true,\n render: (c) => (\n <span className=\"text-text-tertiary\">\n {new Date(c.updatedAt).toLocaleDateString(undefined, {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n })}\n </span>\n ),\n },\n ];\n\n const resetForm = () => {\n setForm(EMPTY_FORM);\n setFormError(null);\n };\n\n const handleCreate = async () => {\n if (!form.reference.trim()) {\n setFormError('Contract reference is required.');\n return;\n }\n if (!form.counterparty.trim()) {\n setFormError('Counterparty is required.');\n return;\n }\n setFormError(null);\n try {\n const created = await createContract.mutateAsync({\n reference: form.reference.trim(),\n counterparty: form.counterparty.trim(),\n commodity: form.commodity,\n side: form.side,\n pricingBasis: form.pricingBasis,\n pricePerUnit: form.pricePerUnit ? Number(form.pricePerUnit) : null,\n priceIndex: form.priceIndex.trim() || null,\n currency: form.currency.trim().toUpperCase() || 'USD',\n contractedVolume: form.contractedVolume ? Number(form.contractedVolume) : null,\n unit: form.unit,\n deliveryLocation: form.deliveryLocation.trim() || null,\n startDate: toBackendDate(form.startDate) ?? null,\n endDate: toBackendDate(form.endDate) ?? null,\n notes: form.notes.trim() || null,\n });\n notify.success('Contract created');\n setDialogOpen(false);\n resetForm();\n navigateTo(`contracts/${created.id}`);\n } catch (err) {\n setFormError(err instanceof Error ? err.message : 'Failed to create contract.');\n notify.fromError(err);\n }\n };\n\n return (\n <PageLayout\n title=\"Offtake Contracts\"\n description=\"Commodity purchase & sale agreements — pricing, contracted volume and delivery progress.\"\n actions={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New contract\n </Button>\n }\n >\n <div className=\"mb-5 flex flex-wrap items-center gap-3\">\n <FilterTabs\n options={STATUS_FILTERS}\n value={status}\n onChange={(v) => {\n setStatus(v);\n pager.reset();\n }}\n />\n <Select\n value={side}\n onValueChange={(v) => {\n setSide(v as TheContractSide | 'ALL');\n pager.reset();\n }}\n >\n <SelectTrigger className=\"w-40\" aria-label=\"Filter by side\">\n <SelectValue placeholder=\"All sides\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All sides</SelectItem>\n {SIDES.map((s) => (\n <SelectItem key={s} value={s}>\n {formatEnumLabel(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select\n value={commodity}\n onValueChange={(v) => {\n setCommodity(v as TheCommodityType | 'ALL');\n pager.reset();\n }}\n >\n <SelectTrigger className=\"w-48\" aria-label=\"Filter by commodity\">\n <SelectValue placeholder=\"All commodities\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All commodities</SelectItem>\n {COMMODITIES.map((c) => (\n <SelectItem key={c} value={c}>\n {formatEnumLabel(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading contracts…\" />\n ) : isError ? (\n <ErrorView message=\"Couldn't load contracts.\" onRetry={() => refetch()} />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<FileSignature className=\"h-10 w-10\" />}\n title=\"No contracts yet\"\n description={\n status === 'ALL' && side === 'ALL' && commodity === 'ALL'\n ? 'Create your first offtake contract to track pricing, volume and delivery.'\n : 'No contracts match these filters.'\n }\n action={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New contract\n </Button>\n }\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(c) => c.id}\n onRowClick={(c) => navigateTo(`contracts/${c.id}`)}\n rowLabel={(c) => `Open ${c.reference}`}\n />\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo?.hasMore ?? false}\n onPageChange={(target) => pager.onPageChange(target, data?.pageInfo?.cursor)}\n />\n </>\n )}\n\n <ResponsiveDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n setDialogOpen(open);\n if (!open) resetForm();\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New offtake contract</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Register a commodity purchase or sale agreement with a counterparty.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form\n onSubmit={(e) => {\n e.preventDefault();\n void handleCreate();\n }}\n >\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"reference\">Reference *</Label>\n <Input\n id=\"reference\"\n value={form.reference}\n onChange={(e) => setForm({ ...form, reference: e.target.value })}\n placeholder=\"OFT-2026-0142\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"counterparty\">Counterparty *</Label>\n <Input\n id=\"counterparty\"\n value={form.counterparty}\n onChange={(e) => setForm({ ...form, counterparty: e.target.value })}\n placeholder=\"Vitol S.A.\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"commodity\">Commodity *</Label>\n <Select\n value={form.commodity}\n onValueChange={(v) => setForm({ ...form, commodity: v as TheCommodityType })}\n >\n <SelectTrigger id=\"commodity\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {COMMODITIES.map((c) => (\n <SelectItem key={c} value={c}>\n {formatEnumLabel(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"side\">Side</Label>\n <Select\n value={form.side}\n onValueChange={(v) => setForm({ ...form, side: v as TheContractSide })}\n >\n <SelectTrigger id=\"side\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SIDES.map((s) => (\n <SelectItem key={s} value={s}>\n {formatEnumLabel(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"pricingBasis\">Pricing basis</Label>\n <Select\n value={form.pricingBasis}\n onValueChange={(v) => setForm({ ...form, pricingBasis: v as ThePricingBasis })}\n >\n <SelectTrigger id=\"pricingBasis\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PRICING_BASES.map((p) => (\n <SelectItem key={p} value={p}>\n {formatEnumLabel(p)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"priceIndex\">Price index</Label>\n <Input\n id=\"priceIndex\"\n value={form.priceIndex}\n onChange={(e) => setForm({ ...form, priceIndex: e.target.value })}\n placeholder=\"Brent, TTF, Henry Hub\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-3\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"pricePerUnit\">Price / unit</Label>\n <Input\n id=\"pricePerUnit\"\n type=\"number\"\n min=\"0\"\n step=\"any\"\n value={form.pricePerUnit}\n onChange={(e) => setForm({ ...form, pricePerUnit: e.target.value })}\n placeholder=\"82.50\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"currency\">Currency</Label>\n <Input\n id=\"currency\"\n value={form.currency}\n onChange={(e) => setForm({ ...form, currency: e.target.value })}\n placeholder=\"USD\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"unit\">Unit</Label>\n <Select\n value={form.unit}\n onValueChange={(v) => setForm({ ...form, unit: v as TheUnit })}\n >\n <SelectTrigger id=\"unit\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {UNITS.map((u) => (\n <SelectItem key={u} value={u}>\n {formatUnit(u)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"contractedVolume\">Contracted volume</Label>\n <Input\n id=\"contractedVolume\"\n type=\"number\"\n min=\"0\"\n step=\"any\"\n value={form.contractedVolume}\n onChange={(e) => setForm({ ...form, contractedVolume: e.target.value })}\n placeholder=\"500000\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"deliveryLocation\">Delivery location</Label>\n <Input\n id=\"deliveryLocation\"\n value={form.deliveryLocation}\n onChange={(e) => setForm({ ...form, deliveryLocation: e.target.value })}\n placeholder=\"Rotterdam\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"startDate\">Start date</Label>\n <Input\n id=\"startDate\"\n type=\"date\"\n value={form.startDate}\n onChange={(e) => setForm({ ...form, startDate: e.target.value })}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"endDate\">End date</Label>\n <Input\n id=\"endDate\"\n type=\"date\"\n value={form.endDate}\n onChange={(e) => setForm({ ...form, endDate: e.target.value })}\n />\n </div>\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"notes\">Notes</Label>\n <Textarea\n id=\"notes\"\n rows={2}\n value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })}\n placeholder=\"Optional context about this contract.\"\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n setDialogOpen(false);\n resetForm();\n }}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={createContract.isPending}>\n {createContract.isPending ? 'Creating…' : 'Create contract'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ContractsPage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/contracts/ContractsPage.tsx"],"sourcesContent":["/**\n * Offtake contracts — list, filter, and create commodity purchase/sale contracts.\n * @module theglobalfuel/pages/contracts\n *\n * Reads `theListContracts`; writes `theCreateContract`.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, FileSignature } from 'lucide-react';\nimport {\n Button,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataTable, type Column } from '../../components/DataTable';\nimport { FilterTabs, type FilterOption } from '../../components/FilterTabs';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { EmptyState } from '../../components/EmptyState';\nimport { Pagination } from '../../components/Pagination';\nimport { MiniBar } from '../../components/MiniBar';\nimport { useTheGlobalFuel } from '../../context';\nimport { useTheContracts, useTheCreateContract, useCursorPager, useNotify } from '../../hooks';\nimport { formatEnumLabel, formatMoney, formatUnit, formatVolume } from '../../utils/formatters';\nimport { toBackendDate } from '../../utils';\nimport type {\n TheOfftakeContract,\n TheContractStatus,\n TheContractSide,\n TheCommodityType,\n ThePricingBasis,\n TheUnit,\n} from '../../types';\n\nconst STATUS_FILTERS: FilterOption<TheContractStatus | 'ALL'>[] = [\n { value: 'ALL', label: 'All' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'NEGOTIATION', label: 'Negotiation' },\n { value: 'SIGNED', label: 'Signed' },\n { value: 'ACTIVE', label: 'Active' },\n { value: 'COMPLETED', label: 'Completed' },\n { value: 'SUSPENDED', label: 'Suspended' },\n { value: 'TERMINATED', label: 'Terminated' },\n];\n\nconst SIDES: TheContractSide[] = ['BUY', 'SELL'];\nconst PRICING_BASES: ThePricingBasis[] = ['FIXED', 'INDEX_LINKED', 'FORMULA', 'SPOT'];\nconst COMMODITIES: TheCommodityType[] = [\n 'CRUDE_OIL',\n 'NATURAL_GAS',\n 'LNG',\n 'COAL',\n 'ELECTRICITY',\n 'HYDROGEN',\n 'BIOFUEL',\n 'IRON_ORE',\n 'COPPER',\n 'BAUXITE',\n 'GOLD',\n 'LITHIUM',\n 'RARE_EARTH',\n];\nconst UNITS: TheUnit[] = ['BARRELS', 'CUBIC_METERS', 'TONNES', 'KILOGRAMS', 'MWH', 'GJ', 'MCF'];\n\nconst PAGE_SIZE = 25;\n\nconst clampPercent = (value: number): number => Math.max(0, Math.min(100, value));\n\n/** Fraction of a contract's volume that has been delivered (0–100). */\nconst deliveryPct = (c: TheOfftakeContract): number =>\n c.contractedVolume > 0 ? clampPercent((c.deliveredVolume / c.contractedVolume) * 100) : 0;\n\ninterface CreateFormState {\n reference: string;\n counterparty: string;\n commodity: TheCommodityType;\n side: TheContractSide;\n pricingBasis: ThePricingBasis;\n pricePerUnit: string;\n priceIndex: string;\n currency: string;\n contractedVolume: string;\n unit: TheUnit;\n deliveryLocation: string;\n startDate: string;\n endDate: string;\n notes: string;\n}\n\nconst EMPTY_FORM: CreateFormState = {\n reference: '',\n counterparty: '',\n commodity: 'CRUDE_OIL',\n side: 'SELL',\n pricingBasis: 'FIXED',\n pricePerUnit: '',\n priceIndex: '',\n currency: 'USD',\n contractedVolume: '',\n unit: 'BARRELS',\n deliveryLocation: '',\n startDate: '',\n endDate: '',\n notes: '',\n};\n\nexport function ContractsPage() {\n const { navigateTo } = useTheGlobalFuel();\n const notify = useNotify();\n const [status, setStatus] = useState<TheContractStatus | 'ALL'>('ALL');\n const [side, setSide] = useState<TheContractSide | 'ALL'>('ALL');\n const [commodity, setCommodity] = useState<TheCommodityType | 'ALL'>('ALL');\n const pager = useCursorPager();\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<CreateFormState>(EMPTY_FORM);\n const [formError, setFormError] = useState<string | null>(null);\n\n const { data, isLoading, isError, refetch } = useTheContracts({\n status: status === 'ALL' ? undefined : status,\n side: side === 'ALL' ? undefined : side,\n commodity: commodity === 'ALL' ? undefined : commodity,\n pagination: { limit: PAGE_SIZE, offset: pager.page * PAGE_SIZE },\n });\n const createContract = useTheCreateContract();\n\n const rows = useMemo(() => data?.items ?? [], [data]);\n\n const columns: Column<TheOfftakeContract>[] = [\n {\n key: 'reference',\n header: 'Contract',\n render: (c) => (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-accent\">\n <FileSignature className=\"h-4 w-4\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate font-semibold text-text-primary\">{c.reference}</p>\n <p className=\"truncate text-xs text-text-tertiary\">{c.counterparty}</p>\n </div>\n </div>\n ),\n },\n {\n key: 'side',\n header: 'Side',\n render: (c) => <StatusBadge status={c.side} />,\n },\n {\n key: 'commodity',\n header: 'Commodity',\n hideOnMobile: true,\n render: (c) => <span className=\"text-text-secondary\">{formatEnumLabel(c.commodity)}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (c) => <StatusBadge status={c.status} />,\n },\n {\n key: 'price',\n header: 'Price',\n align: 'right',\n hideOnMobile: true,\n sortValue: (c) => c.pricePerUnit,\n render: (c) => (\n <span className=\"tabular-nums text-text-secondary\">\n {formatMoney(c.pricePerUnit, c.currency)}\n <span className=\"text-text-tertiary\">/{formatUnit(c.unit)}</span>\n </span>\n ),\n },\n {\n key: 'delivery',\n header: 'Delivered',\n sortValue: (c) => deliveryPct(c),\n render: (c) => {\n const pct = deliveryPct(c);\n return (\n <div className=\"min-w-[9rem]\">\n <MiniBar\n value={pct}\n barClassName=\"bg-[var(--color-status-success-text)]\"\n className=\"mb-1\"\n />\n <p className=\"text-xs tabular-nums text-text-tertiary\">\n {formatVolume(c.deliveredVolume, c.unit)} / {formatVolume(c.contractedVolume, c.unit)}\n </p>\n </div>\n );\n },\n },\n {\n key: 'updated',\n header: 'Updated',\n align: 'right',\n hideOnMobile: true,\n render: (c) => (\n <span className=\"text-text-tertiary\">\n {new Date(c.updatedAt).toLocaleDateString(undefined, {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n })}\n </span>\n ),\n },\n ];\n\n const resetForm = () => {\n setForm(EMPTY_FORM);\n setFormError(null);\n };\n\n const handleCreate = async () => {\n if (!form.reference.trim()) {\n setFormError('Contract reference is required.');\n return;\n }\n if (!form.counterparty.trim()) {\n setFormError('Counterparty is required.');\n return;\n }\n setFormError(null);\n try {\n const created = await createContract.mutateAsync({\n reference: form.reference.trim(),\n counterparty: form.counterparty.trim(),\n commodity: form.commodity,\n side: form.side,\n pricingBasis: form.pricingBasis,\n pricePerUnit: form.pricePerUnit ? Number(form.pricePerUnit) : null,\n priceIndex: form.priceIndex.trim() || null,\n currency: form.currency.trim().toUpperCase() || 'USD',\n contractedVolume: form.contractedVolume ? Number(form.contractedVolume) : null,\n unit: form.unit,\n deliveryLocation: form.deliveryLocation.trim() || null,\n startDate: toBackendDate(form.startDate) ?? null,\n endDate: toBackendDate(form.endDate) ?? null,\n notes: form.notes.trim() || null,\n });\n notify.success('Contract created');\n setDialogOpen(false);\n resetForm();\n navigateTo(`contracts/${created.id}`);\n } catch (err) {\n setFormError(err instanceof Error ? err.message : 'Failed to create contract.');\n notify.fromError(err);\n }\n };\n\n return (\n <PageLayout\n title=\"Offtake Contracts\"\n description=\"Commodity purchase & sale agreements — pricing, contracted volume and delivery progress.\"\n actions={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New contract\n </Button>\n }\n >\n <div className=\"mb-5 flex flex-wrap items-center gap-3\">\n <FilterTabs\n options={STATUS_FILTERS}\n value={status}\n onChange={(v) => {\n setStatus(v);\n pager.reset();\n }}\n />\n <Select\n value={side}\n onValueChange={(v) => {\n setSide(v as TheContractSide | 'ALL');\n pager.reset();\n }}\n >\n <SelectTrigger className=\"w-40\" aria-label=\"Filter by side\">\n <SelectValue placeholder=\"All sides\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All sides</SelectItem>\n {SIDES.map((s) => (\n <SelectItem key={s} value={s}>\n {formatEnumLabel(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select\n value={commodity}\n onValueChange={(v) => {\n setCommodity(v as TheCommodityType | 'ALL');\n pager.reset();\n }}\n >\n <SelectTrigger className=\"w-48\" aria-label=\"Filter by commodity\">\n <SelectValue placeholder=\"All commodities\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All commodities</SelectItem>\n {COMMODITIES.map((c) => (\n <SelectItem key={c} value={c}>\n {formatEnumLabel(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading contracts…\" />\n ) : isError ? (\n <ErrorView message=\"Couldn't load contracts.\" onRetry={() => refetch()} />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<FileSignature className=\"h-10 w-10\" />}\n title=\"No contracts yet\"\n description={\n status === 'ALL' && side === 'ALL' && commodity === 'ALL'\n ? 'Create your first offtake contract to track pricing, volume and delivery.'\n : 'No contracts match these filters.'\n }\n action={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New contract\n </Button>\n }\n />\n ) : (\n <>\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(c) => c.id}\n onRowClick={(c) => navigateTo(`contracts/${c.id}`)}\n rowLabel={(c) => `Open ${c.reference}`}\n />\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={rows.length}\n hasMore={data?.pageInfo?.hasMore ?? false}\n onPageChange={pager.onPageChange}\n />\n </>\n )}\n\n <ResponsiveDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n setDialogOpen(open);\n if (!open) resetForm();\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New offtake contract</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Register a commodity purchase or sale agreement with a counterparty.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form\n onSubmit={(e) => {\n e.preventDefault();\n void handleCreate();\n }}\n >\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"reference\">Reference *</Label>\n <Input\n id=\"reference\"\n value={form.reference}\n onChange={(e) => setForm({ ...form, reference: e.target.value })}\n placeholder=\"OFT-2026-0142\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"counterparty\">Counterparty *</Label>\n <Input\n id=\"counterparty\"\n value={form.counterparty}\n onChange={(e) => setForm({ ...form, counterparty: e.target.value })}\n placeholder=\"Vitol S.A.\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"commodity\">Commodity *</Label>\n <Select\n value={form.commodity}\n onValueChange={(v) => setForm({ ...form, commodity: v as TheCommodityType })}\n >\n <SelectTrigger id=\"commodity\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {COMMODITIES.map((c) => (\n <SelectItem key={c} value={c}>\n {formatEnumLabel(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"side\">Side</Label>\n <Select\n value={form.side}\n onValueChange={(v) => setForm({ ...form, side: v as TheContractSide })}\n >\n <SelectTrigger id=\"side\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SIDES.map((s) => (\n <SelectItem key={s} value={s}>\n {formatEnumLabel(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"pricingBasis\">Pricing basis</Label>\n <Select\n value={form.pricingBasis}\n onValueChange={(v) => setForm({ ...form, pricingBasis: v as ThePricingBasis })}\n >\n <SelectTrigger id=\"pricingBasis\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {PRICING_BASES.map((p) => (\n <SelectItem key={p} value={p}>\n {formatEnumLabel(p)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"priceIndex\">Price index</Label>\n <Input\n id=\"priceIndex\"\n value={form.priceIndex}\n onChange={(e) => setForm({ ...form, priceIndex: e.target.value })}\n placeholder=\"Brent, TTF, Henry Hub\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-3\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"pricePerUnit\">Price / unit</Label>\n <Input\n id=\"pricePerUnit\"\n type=\"number\"\n min=\"0\"\n step=\"any\"\n value={form.pricePerUnit}\n onChange={(e) => setForm({ ...form, pricePerUnit: e.target.value })}\n placeholder=\"82.50\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"currency\">Currency</Label>\n <Input\n id=\"currency\"\n value={form.currency}\n onChange={(e) => setForm({ ...form, currency: e.target.value })}\n placeholder=\"USD\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"unit\">Unit</Label>\n <Select\n value={form.unit}\n onValueChange={(v) => setForm({ ...form, unit: v as TheUnit })}\n >\n <SelectTrigger id=\"unit\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {UNITS.map((u) => (\n <SelectItem key={u} value={u}>\n {formatUnit(u)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"contractedVolume\">Contracted volume</Label>\n <Input\n id=\"contractedVolume\"\n type=\"number\"\n min=\"0\"\n step=\"any\"\n value={form.contractedVolume}\n onChange={(e) => setForm({ ...form, contractedVolume: e.target.value })}\n placeholder=\"500000\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"deliveryLocation\">Delivery location</Label>\n <Input\n id=\"deliveryLocation\"\n value={form.deliveryLocation}\n onChange={(e) => setForm({ ...form, deliveryLocation: e.target.value })}\n placeholder=\"Rotterdam\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"startDate\">Start date</Label>\n <Input\n id=\"startDate\"\n type=\"date\"\n value={form.startDate}\n onChange={(e) => setForm({ ...form, startDate: e.target.value })}\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"endDate\">End date</Label>\n <Input\n id=\"endDate\"\n type=\"date\"\n value={form.endDate}\n onChange={(e) => setForm({ ...form, endDate: e.target.value })}\n />\n </div>\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"notes\">Notes</Label>\n <Textarea\n id=\"notes\"\n rows={2}\n value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })}\n placeholder=\"Optional context about this contract.\"\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n setDialogOpen(false);\n resetForm();\n }}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={createContract.isPending}>\n {createContract.isPending ? 'Creating…' : 'Create contract'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|