@burdenoff/microfe-theglobalfuel 2026.1107.2 → 2026.1107.4

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.
Files changed (69) hide show
  1. package/dist/theglobalfuel/components/AllocateProductionSheet.d.ts.map +1 -1
  2. package/dist/theglobalfuel/components/AllocateProductionSheet.js +304 -180
  3. package/dist/theglobalfuel/components/AllocateProductionSheet.js.map +1 -1
  4. package/dist/theglobalfuel/components/AssignEntitlementDialog.d.ts +11 -0
  5. package/dist/theglobalfuel/components/AssignEntitlementDialog.d.ts.map +1 -0
  6. package/dist/theglobalfuel/components/AssignEntitlementDialog.js +117 -0
  7. package/dist/theglobalfuel/components/AssignEntitlementDialog.js.map +1 -0
  8. package/dist/theglobalfuel/components/ConfirmActionDialog.d.ts +17 -0
  9. package/dist/theglobalfuel/components/ConfirmActionDialog.d.ts.map +1 -0
  10. package/dist/theglobalfuel/components/ConfirmActionDialog.js +29 -0
  11. package/dist/theglobalfuel/components/ConfirmActionDialog.js.map +1 -0
  12. package/dist/theglobalfuel/components/CorrectAllocationSheet.d.ts +11 -0
  13. package/dist/theglobalfuel/components/CorrectAllocationSheet.d.ts.map +1 -0
  14. package/dist/theglobalfuel/components/CorrectAllocationSheet.js +235 -0
  15. package/dist/theglobalfuel/components/CorrectAllocationSheet.js.map +1 -0
  16. package/dist/theglobalfuel/components/InterestDeckSheet.d.ts +21 -0
  17. package/dist/theglobalfuel/components/InterestDeckSheet.d.ts.map +1 -0
  18. package/dist/theglobalfuel/components/InterestDeckSheet.js +287 -0
  19. package/dist/theglobalfuel/components/InterestDeckSheet.js.map +1 -0
  20. package/dist/theglobalfuel/components/StatusBadge.d.ts.map +1 -1
  21. package/dist/theglobalfuel/components/StatusBadge.js +7 -1
  22. package/dist/theglobalfuel/components/StatusBadge.js.map +1 -1
  23. package/dist/theglobalfuel/components/UnitConversionsDialog.d.ts +18 -0
  24. package/dist/theglobalfuel/components/UnitConversionsDialog.d.ts.map +1 -0
  25. package/dist/theglobalfuel/components/UnitConversionsDialog.js +228 -0
  26. package/dist/theglobalfuel/components/UnitConversionsDialog.js.map +1 -0
  27. package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.d.ts +38 -2
  28. package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.d.ts.map +1 -1
  29. package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.js +265 -124
  30. package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.js.map +1 -1
  31. package/dist/theglobalfuel/pages/production/AllocationsPanel.d.ts +8 -1
  32. package/dist/theglobalfuel/pages/production/AllocationsPanel.d.ts.map +1 -1
  33. package/dist/theglobalfuel/pages/production/AllocationsPanel.js +288 -174
  34. package/dist/theglobalfuel/pages/production/AllocationsPanel.js.map +1 -1
  35. package/dist/theglobalfuel/pages/production/EntitlementsPanel.d.ts +14 -0
  36. package/dist/theglobalfuel/pages/production/EntitlementsPanel.d.ts.map +1 -0
  37. package/dist/theglobalfuel/pages/production/EntitlementsPanel.js +205 -0
  38. package/dist/theglobalfuel/pages/production/EntitlementsPanel.js.map +1 -0
  39. package/dist/theglobalfuel/pages/production/InterestDecksPanel.d.ts +12 -0
  40. package/dist/theglobalfuel/pages/production/InterestDecksPanel.d.ts.map +1 -0
  41. package/dist/theglobalfuel/pages/production/InterestDecksPanel.js +243 -0
  42. package/dist/theglobalfuel/pages/production/InterestDecksPanel.js.map +1 -0
  43. package/dist/theglobalfuel/pages/production/NetPositionPanel.d.ts +5 -0
  44. package/dist/theglobalfuel/pages/production/NetPositionPanel.d.ts.map +1 -1
  45. package/dist/theglobalfuel/pages/production/NetPositionPanel.js +174 -96
  46. package/dist/theglobalfuel/pages/production/NetPositionPanel.js.map +1 -1
  47. package/dist/theglobalfuel/pages/production/ProductionPage.d.ts +3 -0
  48. package/dist/theglobalfuel/pages/production/ProductionPage.d.ts.map +1 -1
  49. package/dist/theglobalfuel/pages/production/ProductionPage.js +109 -95
  50. package/dist/theglobalfuel/pages/production/ProductionPage.js.map +1 -1
  51. package/dist/theglobalfuel/types/index.d.ts +141 -0
  52. package/dist/theglobalfuel/types/index.d.ts.map +1 -1
  53. package/dist/theglobalfuel/utils/allocationForm.d.ts +62 -5
  54. package/dist/theglobalfuel/utils/allocationForm.d.ts.map +1 -1
  55. package/dist/theglobalfuel/utils/allocationForm.js +150 -63
  56. package/dist/theglobalfuel/utils/allocationForm.js.map +1 -1
  57. package/dist/theglobalfuel/utils/correctionForm.d.ts +48 -0
  58. package/dist/theglobalfuel/utils/correctionForm.d.ts.map +1 -0
  59. package/dist/theglobalfuel/utils/correctionForm.js +91 -0
  60. package/dist/theglobalfuel/utils/correctionForm.js.map +1 -0
  61. package/dist/theglobalfuel/utils/interestDeckForm.d.ts +64 -0
  62. package/dist/theglobalfuel/utils/interestDeckForm.d.ts.map +1 -0
  63. package/dist/theglobalfuel/utils/interestDeckForm.js +140 -0
  64. package/dist/theglobalfuel/utils/interestDeckForm.js.map +1 -0
  65. package/dist/theglobalfuel/utils/unitConversion.d.ts +20 -0
  66. package/dist/theglobalfuel/utils/unitConversion.d.ts.map +1 -0
  67. package/dist/theglobalfuel/utils/unitConversion.js +55 -0
  68. package/dist/theglobalfuel/utils/unitConversion.js.map +1 -0
  69. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AllocateProductionSheet.js","names":[],"sources":["../../../src/theglobalfuel/components/AllocateProductionSheet.tsx"],"sourcesContent":["/**\n * AllocateProductionSheet — split one measured production record across the\n * assets that contributed to it (BOFF-4180).\n * @module theglobalfuel/components\n *\n * MANUAL: the operator enters each asset's volume; the rows must add up to the\n * measured volume. PRO_RATA: the operator enters a factor per asset (well-test\n * rate, theoretical volume, metered share) and the measured volume is split in\n * proportion. Each row can override the asset's working interest and add a\n * royalty burden; the live preview shows the split and the net volumes before\n * anything is saved. Writes `theCreateProductionAllocation` (creates a DRAFT).\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, Trash2 } 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 { useNotify, useTheAllAssets, useTheCreateProductionAllocation } from '../hooks';\nimport { cn } from '../utils/cn';\nimport { formatDate, formatNumber, formatVolume } from '../utils/formatters';\nimport {\n buildAllocationInput,\n newAllocationRow,\n previewAllocation,\n type AllocationRowState,\n} from '../utils/allocationForm';\nimport type { TheAllocationMethod, TheProductionRecord } from '../types';\n\ninterface AllocateProductionSheetProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** The measured record being allocated; the sheet renders nothing without one. */\n record: TheProductionRecord | null;\n /** Display name of the record's measurement asset. */\n measurementAssetName?: string;\n}\n\nexport function AllocateProductionSheet({\n open,\n onOpenChange,\n record,\n measurementAssetName,\n}: AllocateProductionSheetProps) {\n const notify = useNotify();\n // Every workspace asset, not just the first page: any of them can contribute.\n const assetsQuery = useTheAllAssets({ enabled: open });\n const createAllocation = useTheCreateProductionAllocation();\n const assets = useMemo(\n () => [...(assetsQuery.data?.items ?? [])].sort((a, b) => a.name.localeCompare(b.name)),\n [assetsQuery.data]\n );\n const workingInterestByAsset = useMemo(() => {\n const map = new Map<string, number>();\n for (const a of assets) map.set(a.id, a.workingInterestPct);\n return map;\n }, [assets]);\n\n const [method, setMethod] = useState<TheAllocationMethod>('MANUAL');\n const [rows, setRows] = useState<AllocationRowState[]>(() => [newAllocationRow()]);\n const [notes, setNotes] = useState('');\n const [formError, setFormError] = useState<string | null>(null);\n\n // Start from a clean form every time the sheet opens for a record (\"adjust\n // state on prop change\" while rendering, so the first frame is already fresh).\n const seedKey = open && record ? `open:${record.id}` : 'closed';\n const [prevSeedKey, setPrevSeedKey] = useState(seedKey);\n if (seedKey !== prevSeedKey) {\n setPrevSeedKey(seedKey);\n if (open && record) {\n setMethod('MANUAL');\n setRows([newAllocationRow()]);\n setNotes('');\n setFormError(null);\n }\n }\n\n const measured = record?.volume ?? 0;\n const unit = record?.unit ?? 'BARRELS';\n const preview = previewAllocation(\n method,\n measured,\n rows,\n (assetId) => workingInterestByAsset.get(assetId) ?? 100\n );\n\n const updateRow = (key: string, patch: Partial<AllocationRowState>) =>\n setRows((current) => current.map((r) => (r.key === key ? { ...r, ...patch } : r)));\n const removeRow = (key: string) =>\n setRows((current) => (current.length > 1 ? current.filter((r) => r.key !== key) : current));\n\n const close = () => onOpenChange(false);\n\n const handleSubmit = async () => {\n if (!record) return;\n const result = buildAllocationInput({\n productionRecordId: record.id,\n method,\n measuredVolume: measured,\n rows,\n notes,\n });\n if (!result.ok) {\n setFormError(result.error);\n notify.error(result.error);\n return;\n }\n setFormError(null);\n try {\n await createAllocation.mutateAsync(result.input);\n notify.success('Allocation drafted — approve it to lock the split');\n close();\n } catch (err) {\n setFormError(err instanceof Error ? err.message : 'Failed to allocate production.');\n notify.fromError(err);\n }\n };\n\n const amountLabel = method === 'PRO_RATA' ? 'Factor' : 'Volume';\n\n return (\n <ResponsiveDialog open={open && Boolean(record)} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent data-testid=\"allocate-production-sheet\" className=\"md:max-w-3xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Allocate production</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {record\n ? `${formatVolume(record.volume, record.unit)} measured at ${\n measurementAssetName ?? 'this asset'\n }, ${formatDate(record.periodStart)} – ${formatDate(record.periodEnd)}.`\n : ''}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form\n onSubmit={(e) => {\n e.preventDefault();\n void handleSubmit();\n }}\n >\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"alMethod\">Method</Label>\n <Select value={method} onValueChange={(v) => setMethod(v as TheAllocationMethod)}>\n <SelectTrigger id=\"alMethod\" className=\"h-11 md:h-10 md:w-72\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"MANUAL\">Manual — enter each asset&rsquo;s volume</SelectItem>\n <SelectItem value=\"PRO_RATA\">Pro rata — split by factor</SelectItem>\n </SelectContent>\n </Select>\n <p className=\"text-xs text-text-tertiary\">\n {method === 'PRO_RATA'\n ? 'Factors can be well-test rates, theoretical volumes or metered shares. The measured volume is split in proportion.'\n : 'The volumes must add up to the measured volume.'}\n </p>\n </div>\n\n <div className=\"grid gap-3\">\n {assetsQuery.data?.truncated && (\n <p className=\"text-xs text-status-warning-text\">\n Showing the first {assets.length} assets of this workspace.\n </p>\n )}\n {rows.map((row, i) => {\n const p = preview.rows[i];\n const assetWi = workingInterestByAsset.get(row.assetId);\n return (\n <div\n key={row.key}\n className=\"grid gap-2 rounded-lg border border-border-seam p-3\"\n data-testid=\"allocation-row\"\n >\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-[minmax(0,2fr)_minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)_auto]\">\n <div className=\"grid gap-1\">\n <Label htmlFor={`${row.key}-asset`} className=\"text-xs\">\n Contributing asset\n </Label>\n <Select\n value={row.assetId}\n onValueChange={(v) => updateRow(row.key, { assetId: v })}\n >\n <SelectTrigger id={`${row.key}-asset`} className=\"h-10\">\n <SelectValue placeholder=\"Select an asset\" />\n </SelectTrigger>\n <SelectContent>\n {assets.map((a) => (\n <SelectItem key={a.id} value={a.id}>\n {a.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor={`${row.key}-amount`} className=\"text-xs\">\n {amountLabel}\n </Label>\n <Input\n id={`${row.key}-amount`}\n type=\"number\"\n inputMode=\"decimal\"\n min=\"0\"\n step=\"any\"\n className=\"h-10 tabular-nums\"\n value={row.amount}\n onChange={(e) => updateRow(row.key, { amount: e.target.value })}\n />\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor={`${row.key}-wi`} className=\"text-xs\">\n Working interest %\n </Label>\n <Input\n id={`${row.key}-wi`}\n type=\"number\"\n inputMode=\"decimal\"\n min=\"0\"\n max=\"100\"\n step=\"any\"\n className=\"h-10 tabular-nums\"\n placeholder={assetWi != null ? formatNumber(assetWi) : '100'}\n value={row.workingInterestPct}\n onChange={(e) =>\n updateRow(row.key, { workingInterestPct: e.target.value })\n }\n />\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor={`${row.key}-royalty`} className=\"text-xs\">\n Royalty %\n </Label>\n <Input\n id={`${row.key}-royalty`}\n type=\"number\"\n inputMode=\"decimal\"\n min=\"0\"\n max=\"100\"\n step=\"any\"\n className=\"h-10 tabular-nums\"\n placeholder=\"0\"\n value={row.royaltyPct}\n onChange={(e) => updateRow(row.key, { royaltyPct: e.target.value })}\n />\n </div>\n <div className=\"flex items-end\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`Remove row ${i + 1}`}\n disabled={rows.length === 1}\n onClick={() => removeRow(row.key)}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n {p && (\n <p className=\"text-xs tabular-nums text-text-secondary\">\n Gross {formatVolume(p.allocatedVolume, unit)} (\n {formatNumber(p.sharePct, { maximumFractionDigits: 2 })}%) · Net WI{' '}\n {formatVolume(p.netWorkingInterestVolume, unit)} · Royalty{' '}\n {formatVolume(p.royaltyVolume, unit)} · Net revenue{' '}\n {formatVolume(p.netRevenueVolume, unit)}\n </p>\n )}\n </div>\n );\n })}\n <div>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setRows((current) => [...current, newAllocationRow()])}\n >\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add asset\n </Button>\n </div>\n </div>\n\n <div\n className={cn(\n 'rounded-lg px-3 py-2 text-sm tabular-nums',\n preview.balanced\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-status-warning-bg-subtle text-status-warning-text'\n )}\n data-testid=\"allocation-balance\"\n >\n Allocated {formatVolume(preview.allocatedTotal, unit)} of{' '}\n {formatVolume(measured, unit)}\n {method === 'MANUAL' && !preview.balanced\n ? ` — ${formatVolume(Math.abs(preview.difference), unit)} ${\n preview.difference > 0 ? 'left to allocate' : 'over-allocated'\n }`\n : ''}\n {' · '}Net revenue {formatVolume(preview.netRevenueTotal, unit)}\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"alNotes\">Notes</Label>\n <Textarea\n id=\"alNotes\"\n rows={2}\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n placeholder=\"Basis for the split (well tests, meter proving, agreements).\"\n />\n </div>\n\n {formError && (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {formError}\n </p>\n )}\n </div>\n\n <ResponsiveDialogFooter className=\"max-md:flex-col-reverse max-md:gap-2\">\n <Button type=\"button\" variant=\"outline\" className=\"max-md:h-11\" onClick={close}>\n Cancel\n </Button>\n <Button type=\"submit\" className=\"max-md:h-11\" disabled={createAllocation.isPending}>\n {createAllocation.isPending ? 'Saving…' : 'Save draft allocation'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;;;;;;;AAoDA,SAAgB,EAAwB,EACtC,SACA,iBACA,WACA,2BAC+B;CAC/B,IAAM,IAAS,IAAW,EAEpB,IAAc,EAAgB,EAAE,SAAS,GAAM,CAAC,EAChD,IAAmB,GAAkC,EACrD,IAAS,QACP,CAAC,GAAI,EAAY,MAAM,SAAS,EAAE,CAAE,CAAC,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC,EACvF,CAAC,EAAY,KAAK,CACnB,EACK,IAAyB,QAAc;EAC3C,IAAM,oBAAM,IAAI,KAAqB;AACrC,OAAK,IAAM,KAAK,EAAQ,GAAI,IAAI,EAAE,IAAI,EAAE,mBAAmB;AAC3D,SAAO;IACN,CAAC,EAAO,CAAC,EAEN,CAAC,GAAQ,KAAa,EAA8B,SAAS,EAC7D,CAAC,GAAM,KAAW,QAAqC,CAAC,GAAkB,CAAC,CAAC,EAC5E,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,CAAC,GAAW,KAAgB,EAAwB,KAAK,EAIzD,IAAU,KAAQ,IAAS,QAAQ,EAAO,OAAO,UACjD,CAAC,GAAa,KAAkB,EAAS,EAAQ;AACvD,CAAI,MAAY,MACd,EAAe,EAAQ,EACnB,KAAQ,MACV,EAAU,SAAS,EACnB,EAAQ,CAAC,GAAkB,CAAC,CAAC,EAC7B,EAAS,GAAG,EACZ,EAAa,KAAK;CAItB,IAAM,IAAW,GAAQ,UAAU,GAC7B,IAAO,GAAQ,QAAQ,WACvB,IAAU,EACd,GACA,GACA,IACC,MAAY,EAAuB,IAAI,EAAQ,IAAI,IACrD,EAEK,KAAa,GAAa,MAC9B,GAAS,MAAY,EAAQ,KAAK,MAAO,EAAE,QAAQ,IAAM;EAAE,GAAG;EAAG,GAAG;EAAO,GAAG,EAAG,CAAC,EAC9E,MAAa,MACjB,GAAS,MAAa,EAAQ,SAAS,IAAI,EAAQ,QAAQ,MAAM,EAAE,QAAQ,EAAI,GAAG,EAAS,EAEvF,UAAc,EAAa,GAAM,EAEjC,KAAe,YAAY;AAC/B,MAAI,CAAC,EAAQ;EACb,IAAM,IAAS,EAAqB;GAClC,oBAAoB,EAAO;GAC3B;GACA,gBAAgB;GAChB;GACA;GACD,CAAC;AACF,MAAI,CAAC,EAAO,IAAI;AAEd,GADA,EAAa,EAAO,MAAM,EAC1B,EAAO,MAAM,EAAO,MAAM;AAC1B;;AAEF,IAAa,KAAK;AAClB,MAAI;AAGF,GAFA,MAAM,EAAiB,YAAY,EAAO,MAAM,EAChD,EAAO,QAAQ,oDAAoD,EACnE,GAAO;WACA,GAAK;AAEZ,GADA,EAAa,aAAe,QAAQ,EAAI,UAAU,iCAAiC,EACnF,EAAO,UAAU,EAAI;;IAInB,KAAc,MAAW,aAAa,WAAW;AAEvD,QACE,kBAAC,GAAD;EAAkB,MAAM,KAAQ,EAAQ;EAAuB;YAC7D,kBAAC,GAAD;GAAyB,eAAY;GAA4B,WAAU;aAA3E,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,uBAA2C,CAAA,EAClE,kBAAC,GAAD,EAAA,UACG,IACG,GAAG,EAAa,EAAO,QAAQ,EAAO,KAAK,CAAC,eAC1C,KAAwB,aACzB,IAAI,EAAW,EAAO,YAAY,CAAC,KAAK,EAAW,EAAO,UAAU,CAAC,KACtE,IACwB,CAAA,CACP,EAAA,CAAA,EAEzB,kBAAC,QAAD;IACE,WAAW,MAAM;AAEV,KADL,EAAE,gBAAgB,EACb,IAAc;;cAHvB,CAME,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD;SAAO,SAAQ;mBAAW;SAAc,CAAA;QACxC,kBAAC,GAAD;SAAQ,OAAO;SAAQ,gBAAgB,MAAM,EAAU,EAAyB;mBAAhF,CACE,kBAAC,GAAD;UAAe,IAAG;UAAW,WAAU;oBACrC,kBAAC,GAAD,EAAe,CAAA;UACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;UAAY,OAAM;oBAAS;UAAqD,CAAA,EAChF,kBAAC,GAAD;UAAY,OAAM;oBAAW;UAAuC,CAAA,CACtD,EAAA,CAAA,CACT;;QACT,kBAAC,KAAD;SAAG,WAAU;mBACV,MAAW,aACR,uHACA;SACF,CAAA;QACA;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACG,EAAY,MAAM,aACjB,kBAAC,KAAD;SAAG,WAAU;mBAAb;UAAgD;UAC3B,EAAO;UAAO;UAC/B;;QAEL,EAAK,KAAK,GAAK,MAAM;SACpB,IAAM,IAAI,EAAQ,KAAK,IACjB,IAAU,EAAuB,IAAI,EAAI,QAAQ;AACvD,gBACE,kBAAC,OAAD;UAEE,WAAU;UACV,eAAY;oBAHd,CAKE,kBAAC,OAAD;WAAK,WAAU;qBAAf;YACE,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,GAAD;cAAO,SAAS,GAAG,EAAI,IAAI;cAAS,WAAU;wBAAU;cAEhD,CAAA,EACR,kBAAC,GAAD;cACE,OAAO,EAAI;cACX,gBAAgB,MAAM,EAAU,EAAI,KAAK,EAAE,SAAS,GAAG,CAAC;wBAF1D,CAIE,kBAAC,GAAD;eAAe,IAAI,GAAG,EAAI,IAAI;eAAS,WAAU;yBAC/C,kBAAC,GAAD,EAAa,aAAY,mBAAoB,CAAA;eAC/B,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,EAAO,KAAK,MACX,kBAAC,GAAD;eAAuB,OAAO,EAAE;yBAC7B,EAAE;eACQ,EAFI,EAAE,GAEN,CACb,EACY,CAAA,CACT;gBACL;;YACN,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,GAAD;cAAO,SAAS,GAAG,EAAI,IAAI;cAAU,WAAU;wBAC5C;cACK,CAAA,EACR,kBAAC,GAAD;cACE,IAAI,GAAG,EAAI,IAAI;cACf,MAAK;cACL,WAAU;cACV,KAAI;cACJ,MAAK;cACL,WAAU;cACV,OAAO,EAAI;cACX,WAAW,MAAM,EAAU,EAAI,KAAK,EAAE,QAAQ,EAAE,OAAO,OAAO,CAAC;cAC/D,CAAA,CACE;;YACN,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,GAAD;cAAO,SAAS,GAAG,EAAI,IAAI;cAAM,WAAU;wBAAU;cAE7C,CAAA,EACR,kBAAC,GAAD;cACE,IAAI,GAAG,EAAI,IAAI;cACf,MAAK;cACL,WAAU;cACV,KAAI;cACJ,KAAI;cACJ,MAAK;cACL,WAAU;cACV,aAAa,KAAW,OAA+B,QAAxB,EAAa,EAAQ;cACpD,OAAO,EAAI;cACX,WAAW,MACT,EAAU,EAAI,KAAK,EAAE,oBAAoB,EAAE,OAAO,OAAO,CAAC;cAE5D,CAAA,CACE;;YACN,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,GAAD;cAAO,SAAS,GAAG,EAAI,IAAI;cAAW,WAAU;wBAAU;cAElD,CAAA,EACR,kBAAC,GAAD;cACE,IAAI,GAAG,EAAI,IAAI;cACf,MAAK;cACL,WAAU;cACV,KAAI;cACJ,KAAI;cACJ,MAAK;cACL,WAAU;cACV,aAAY;cACZ,OAAO,EAAI;cACX,WAAW,MAAM,EAAU,EAAI,KAAK,EAAE,YAAY,EAAE,OAAO,OAAO,CAAC;cACnE,CAAA,CACE;;YACN,kBAAC,OAAD;aAAK,WAAU;uBACb,kBAAC,GAAD;cACE,MAAK;cACL,SAAQ;cACR,MAAK;cACL,cAAY,cAAc,IAAI;cAC9B,UAAU,EAAK,WAAW;cAC1B,eAAe,GAAU,EAAI,IAAI;wBAEjC,kBAAC,IAAD,EAAQ,WAAU,WAAY,CAAA;cACvB,CAAA;aACL,CAAA;YACF;cACL,KACC,kBAAC,KAAD;WAAG,WAAU;qBAAb;YAAwD;YAC/C,EAAa,EAAE,iBAAiB,EAAK;YAAC;YAC5C,EAAa,EAAE,UAAU,EAAE,uBAAuB,GAAG,CAAC;YAAC;YAAY;YACnE,EAAa,EAAE,0BAA0B,EAAK;YAAC;YAAW;YAC1D,EAAa,EAAE,eAAe,EAAK;YAAC;YAAe;YACnD,EAAa,EAAE,kBAAkB,EAAK;YACrC;aAEF;YAlGC,EAAI,IAkGL;UAER;QACF,kBAAC,OAAD,EAAA,UACE,kBAAC,GAAD;SACE,MAAK;SACL,SAAQ;SACR,MAAK;SACL,eAAe,GAAS,MAAY,CAAC,GAAG,GAAS,GAAkB,CAAC,CAAC;mBAJvE,CAME,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,aAC5B;YACL,CAAA;QACF;;MAEN,kBAAC,OAAD;OACE,WAAW,EACT,6CACA,EAAQ,WACJ,yDACA,uDACL;OACD,eAAY;iBAPd;QAQC;QACY,EAAa,EAAQ,gBAAgB,EAAK;QAAC;QAAI;QACzD,EAAa,GAAU,EAAK;QAC5B,MAAW,YAAY,CAAC,EAAQ,WAC7B,MAAM,EAAa,KAAK,IAAI,EAAQ,WAAW,EAAE,EAAK,CAAC,GACrD,EAAQ,aAAa,IAAI,qBAAqB,qBAEhD;QACH;QAAM;QAAa,EAAa,EAAQ,iBAAiB,EAAK;QAC3D;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAU;QAAa,CAAA,EACtC,kBAAC,GAAD;QACE,IAAG;QACH,MAAM;QACN,OAAO;QACP,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM;QACzC,aAAY;QACZ,CAAA,CACE;;MAEL,KACC,kBAAC,KAAD;OAAG,MAAK;OAAQ,WAAU;iBACvB;OACC,CAAA;MAEF;QAEN,kBAAC,GAAD;KAAwB,WAAU;eAAlC,CACE,kBAAC,GAAD;MAAQ,MAAK;MAAS,SAAQ;MAAU,WAAU;MAAc,SAAS;gBAAO;MAEvE,CAAA,EACT,kBAAC,GAAD;MAAQ,MAAK;MAAS,WAAU;MAAc,UAAU,EAAiB;gBACtE,EAAiB,YAAY,YAAY;MACnC,CAAA,CACc;OACpB;MACiB;;EACT,CAAA"}
1
+ {"version":3,"file":"AllocateProductionSheet.js","names":[],"sources":["../../../src/theglobalfuel/components/AllocateProductionSheet.tsx"],"sourcesContent":["/**\n * AllocateProductionSheet — split one measured production record across the\n * assets that contributed to it (BOFF-4180).\n * @module theglobalfuel/components\n *\n * MANUAL: the operator enters each asset's volume; the rows must add up to the\n * measured volume. PRO_RATA: the operator enters a factor per asset (well-test\n * rate, theoretical volume, metered share) and the measured volume is split in\n * proportion. Each row defaults to the terms of the interest deck in force for\n * the record's period (BOFF-10787) — else the asset's own working interest and\n * no royalty — and can override working interest, royalty and overriding\n * royalty; the live preview shows the split and the net volumes before\n * anything is saved. Writes `theCreateProductionAllocation` (creates a DRAFT).\n *\n * Shrinkage and reconciliation (BOFF-10789): each row may carry a theoretical\n * (well-test) volume, and the operator can record the losses, flare, fuel and\n * re-injection between the wells and the meter. The preview then shows each\n * asset's wellhead volume and the reconciliation factor (wellhead volume over\n * theoretical volume) before anything is saved.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Plus, Trash2 } 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 useNotify,\n useTheAllAssets,\n useTheAllInterestDecks,\n useTheCreateProductionAllocation,\n} from '../hooks';\nimport { cn } from '../utils/cn';\nimport { formatDate, formatEnumLabel, formatNumber, formatVolume } from '../utils/formatters';\nimport {\n SHRINKAGE_CATEGORIES,\n SHRINKAGE_LABELS,\n buildAllocationInput,\n emptyShrinkage,\n newAllocationRow,\n previewAllocation,\n previewReconciliation,\n type AllocationRowState,\n type PreviewTerms,\n type ShrinkageState,\n} from '../utils/allocationForm';\nimport { resolveDeckForPeriod } from '../utils/interestDeckForm';\nimport type { TheAllocationMethod, TheProductionRecord } from '../types';\n\ninterface AllocateProductionSheetProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** The measured record being allocated; the sheet renders nothing without one. */\n record: TheProductionRecord | null;\n /** Display name of the record's measurement asset. */\n measurementAssetName?: string;\n}\n\nexport function AllocateProductionSheet({\n open,\n onOpenChange,\n record,\n measurementAssetName,\n}: AllocateProductionSheetProps) {\n const notify = useNotify();\n // Every workspace asset, not just the first page: any of them can contribute.\n const assetsQuery = useTheAllAssets({ enabled: open });\n const createAllocation = useTheCreateProductionAllocation();\n const assets = useMemo(\n () => [...(assetsQuery.data?.items ?? [])].sort((a, b) => a.name.localeCompare(b.name)),\n [assetsQuery.data]\n );\n const workingInterestByAsset = useMemo(() => {\n const map = new Map<string, number>();\n for (const a of assets) map.set(a.id, a.workingInterestPct);\n return map;\n }, [assets]);\n\n const [method, setMethod] = useState<TheAllocationMethod>('MANUAL');\n const [rows, setRows] = useState<AllocationRowState[]>(() => [newAllocationRow()]);\n const [notes, setNotes] = useState('');\n const [shrinkage, setShrinkage] = useState<ShrinkageState>(emptyShrinkage);\n const [formError, setFormError] = useState<string | null>(null);\n\n // Start from a clean form every time the sheet opens for a record (\"adjust\n // state on prop change\" while rendering, so the first frame is already fresh).\n const seedKey = open && record ? `open:${record.id}` : 'closed';\n const [prevSeedKey, setPrevSeedKey] = useState(seedKey);\n if (seedKey !== prevSeedKey) {\n setPrevSeedKey(seedKey);\n if (open && record) {\n setMethod('MANUAL');\n setRows([newAllocationRow()]);\n setNotes('');\n setShrinkage(emptyShrinkage());\n setFormError(null);\n }\n }\n\n // Interest decks: the deck in force for the record's period supplies each\n // row's default terms, exactly as the svc resolves them on save.\n const decksQuery = useTheAllInterestDecks({ enabled: open });\n const decks = useMemo(() => decksQuery.data?.items ?? [], [decksQuery.data]);\n const termsFor = (assetId: string): PreviewTerms & { effectiveFrom?: string } => {\n const assetWi = workingInterestByAsset.get(assetId) ?? 100;\n if (!record || !assetId) {\n return {\n workingInterestPct: assetWi,\n royaltyPct: 0,\n overridingRoyaltyPct: 0,\n source: 'ASSET_DEFAULT',\n };\n }\n const res = resolveDeckForPeriod(decks, assetId, record.periodStart, record.periodEnd);\n if (res.kind === 'deck') {\n return {\n workingInterestPct: res.workingInterestPct,\n royaltyPct: res.deck.royaltyPct,\n overridingRoyaltyPct: res.deck.overridingRoyaltyPct,\n source: 'INTEREST_DECK',\n effectiveFrom: res.deck.effectiveFrom,\n };\n }\n return {\n workingInterestPct: assetWi,\n royaltyPct: 0,\n overridingRoyaltyPct: 0,\n source: res.kind === 'spans' ? 'SPANS' : 'ASSET_DEFAULT',\n };\n };\n\n const measured = record?.volume ?? 0;\n const unit = record?.unit ?? 'BARRELS';\n const preview = previewAllocation(method, measured, rows, termsFor);\n const reconciliation = previewReconciliation(method, measured, rows, preview, shrinkage);\n\n const updateRow = (key: string, patch: Partial<AllocationRowState>) =>\n setRows((current) => current.map((r) => (r.key === key ? { ...r, ...patch } : r)));\n const removeRow = (key: string) =>\n setRows((current) => (current.length > 1 ? current.filter((r) => r.key !== key) : current));\n\n const close = () => onOpenChange(false);\n\n const handleSubmit = async () => {\n if (!record) return;\n const result = buildAllocationInput({\n productionRecordId: record.id,\n method,\n measuredVolume: measured,\n rows,\n notes,\n shrinkage,\n });\n if (!result.ok) {\n setFormError(result.error);\n notify.error(result.error);\n return;\n }\n setFormError(null);\n try {\n await createAllocation.mutateAsync(result.input);\n notify.success('Allocation drafted — approve it to lock the split');\n close();\n } catch (err) {\n setFormError(err instanceof Error ? err.message : 'Failed to allocate production.');\n notify.fromError(err);\n }\n };\n\n const amountLabel = method === 'PRO_RATA' ? 'Factor' : 'Volume';\n\n return (\n <ResponsiveDialog open={open && Boolean(record)} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent data-testid=\"allocate-production-sheet\" className=\"md:max-w-3xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Allocate production</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {record\n ? `${formatVolume(record.volume, record.unit)} measured at ${\n measurementAssetName ?? 'this asset'\n }, ${formatDate(record.periodStart)} – ${formatDate(record.periodEnd)}.`\n : ''}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <form\n onSubmit={(e) => {\n e.preventDefault();\n void handleSubmit();\n }}\n >\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"alMethod\">Method</Label>\n <Select value={method} onValueChange={(v) => setMethod(v as TheAllocationMethod)}>\n <SelectTrigger id=\"alMethod\" className=\"h-11 md:h-10 md:w-72\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"MANUAL\">Manual — enter each asset&rsquo;s volume</SelectItem>\n <SelectItem value=\"PRO_RATA\">Pro rata — split by factor</SelectItem>\n </SelectContent>\n </Select>\n <p className=\"text-xs text-text-tertiary\">\n {method === 'PRO_RATA'\n ? 'Factors can be well-test rates, theoretical volumes or metered shares. The measured volume is split in proportion; leave a factor blank to split by the theoretical volume.'\n : 'The volumes must add up to the measured volume.'}\n </p>\n </div>\n\n <div className=\"grid gap-3\">\n {assetsQuery.data?.truncated && (\n <p className=\"text-xs text-status-warning-text\">\n Showing the first {assets.length} assets of this workspace.\n </p>\n )}\n {rows.map((row, i) => {\n const p = preview.rows[i];\n const terms = termsFor(row.assetId);\n return (\n <div\n key={row.key}\n className=\"grid gap-2 rounded-lg border border-border-seam p-3\"\n data-testid=\"allocation-row\"\n >\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-[minmax(0,2fr)_minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)_minmax(0,1fr)_auto]\">\n <div className=\"grid gap-1\">\n <Label htmlFor={`${row.key}-asset`} className=\"text-xs\">\n Contributing asset\n </Label>\n <Select\n value={row.assetId}\n onValueChange={(v) => updateRow(row.key, { assetId: v })}\n >\n <SelectTrigger id={`${row.key}-asset`} className=\"h-10\">\n <SelectValue placeholder=\"Select an asset\" />\n </SelectTrigger>\n <SelectContent>\n {assets.map((a) => (\n <SelectItem key={a.id} value={a.id}>\n {a.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor={`${row.key}-amount`} className=\"text-xs\">\n {amountLabel}\n </Label>\n <Input\n id={`${row.key}-amount`}\n type=\"number\"\n inputMode=\"decimal\"\n min=\"0\"\n step=\"any\"\n className=\"h-10 tabular-nums\"\n value={row.amount}\n onChange={(e) => updateRow(row.key, { amount: e.target.value })}\n />\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor={`${row.key}-theoretical`} className=\"text-xs\">\n Theoretical\n </Label>\n <Input\n id={`${row.key}-theoretical`}\n type=\"number\"\n inputMode=\"decimal\"\n min=\"0\"\n step=\"any\"\n className=\"h-10 tabular-nums\"\n placeholder=\"Optional\"\n value={row.theoreticalVolume}\n onChange={(e) =>\n updateRow(row.key, { theoreticalVolume: e.target.value })\n }\n />\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor={`${row.key}-wi`} className=\"text-xs\">\n Working interest %\n </Label>\n <Input\n id={`${row.key}-wi`}\n type=\"number\"\n inputMode=\"decimal\"\n min=\"0\"\n max=\"100\"\n step=\"any\"\n className=\"h-10 tabular-nums\"\n placeholder={formatNumber(terms.workingInterestPct)}\n value={row.workingInterestPct}\n onChange={(e) =>\n updateRow(row.key, { workingInterestPct: e.target.value })\n }\n />\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor={`${row.key}-royalty`} className=\"text-xs\">\n Royalty %\n </Label>\n <Input\n id={`${row.key}-royalty`}\n type=\"number\"\n inputMode=\"decimal\"\n min=\"0\"\n max=\"100\"\n step=\"any\"\n className=\"h-10 tabular-nums\"\n placeholder={formatNumber(terms.royaltyPct)}\n value={row.royaltyPct}\n onChange={(e) => updateRow(row.key, { royaltyPct: e.target.value })}\n />\n </div>\n <div className=\"grid gap-1\">\n <Label htmlFor={`${row.key}-orri`} className=\"text-xs\">\n ORRI %\n </Label>\n <Input\n id={`${row.key}-orri`}\n type=\"number\"\n inputMode=\"decimal\"\n min=\"0\"\n max=\"100\"\n step=\"any\"\n className=\"h-10 tabular-nums\"\n placeholder={formatNumber(terms.overridingRoyaltyPct)}\n value={row.overridingRoyaltyPct}\n onChange={(e) =>\n updateRow(row.key, { overridingRoyaltyPct: e.target.value })\n }\n />\n </div>\n <div className=\"flex items-end\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`Remove row ${i + 1}`}\n disabled={rows.length === 1}\n onClick={() => removeRow(row.key)}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n {row.assetId && (\n <p\n className={cn(\n 'text-xs',\n p?.needsAllTerms ? 'text-status-warning-text' : 'text-text-tertiary'\n )}\n data-testid=\"allocation-row-terms\"\n >\n {terms.source === 'INTEREST_DECK'\n ? `Terms from the interest deck effective ${formatDate(terms.effectiveFrom)}${\n p?.overridden ? ' (with overrides)' : ''\n }.`\n : terms.source === 'SPANS'\n ? 'Ownership changes inside this period — enter working interest, royalty and ORRI, or split the record at the change.'\n : `No interest deck covers this period — using the asset's working interest${\n p?.overridden ? ' (with overrides)' : ''\n }.`}\n </p>\n )}\n {p && (\n <p className=\"text-xs tabular-nums text-text-secondary\">\n Gross {formatVolume(p.allocatedVolume, unit)} (\n {formatNumber(p.sharePct, { maximumFractionDigits: 2 })}%) · Net WI{' '}\n {formatVolume(p.netWorkingInterestVolume, unit)} · Royalty{' '}\n {formatVolume(p.royaltyVolume, unit)} · Net revenue{' '}\n {formatVolume(p.netRevenueVolume, unit)}\n {reconciliation.shrinkageTotal > 0 &&\n ` · Wellhead ${formatVolume(\n p.allocatedVolume + (reconciliation.rowShrinkage[i] ?? 0),\n unit\n )}`}\n </p>\n )}\n </div>\n );\n })}\n <div>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setRows((current) => [...current, newAllocationRow()])}\n >\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add asset\n </Button>\n </div>\n </div>\n\n <fieldset className=\"grid gap-2 rounded-lg border border-border-seam p-3\">\n <legend className=\"px-1 text-sm font-medium text-text-primary\">\n Shrinkage before the meter\n </legend>\n <p className=\"text-xs text-text-tertiary\">\n Volumes produced at the wells that never reached the measurement point, in{' '}\n {formatEnumLabel(unit)}. They are shared across the assets by theoretical volume (or\n factor, or allocated volume).\n </p>\n <div className=\"grid grid-cols-2 gap-2 md:grid-cols-4\">\n {SHRINKAGE_CATEGORIES.map((category) => (\n <div key={category} className=\"grid gap-1\">\n <Label htmlFor={`shrink-${category}`} className=\"text-xs\">\n {SHRINKAGE_LABELS[category]}\n </Label>\n <Input\n id={`shrink-${category}`}\n type=\"number\"\n inputMode=\"decimal\"\n min=\"0\"\n step=\"any\"\n className=\"h-10 tabular-nums\"\n placeholder=\"0\"\n value={shrinkage[category]}\n onChange={(e) =>\n setShrinkage((current) => ({ ...current, [category]: e.target.value }))\n }\n />\n </div>\n ))}\n </div>\n </fieldset>\n\n <div\n className={cn(\n 'rounded-lg px-3 py-2 text-sm tabular-nums',\n preview.balanced\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-status-warning-bg-subtle text-status-warning-text'\n )}\n data-testid=\"allocation-balance\"\n >\n Allocated {formatVolume(preview.allocatedTotal, unit)} of{' '}\n {formatVolume(measured, unit)}\n {method === 'MANUAL' && !preview.balanced\n ? ` — ${formatVolume(Math.abs(preview.difference), unit)} ${\n preview.difference > 0 ? 'left to allocate' : 'over-allocated'\n }`\n : ''}\n {' · '}Net revenue {formatVolume(preview.netRevenueTotal, unit)}\n </div>\n {(reconciliation.shrinkageTotal > 0 || reconciliation.theoreticalTotal !== null) && (\n <p\n className=\"rounded-lg bg-bg-surface px-3 py-2 text-sm tabular-nums text-text-secondary\"\n data-testid=\"allocation-reconciliation\"\n >\n Shrinkage {formatVolume(reconciliation.shrinkageTotal, unit)} · Wellhead{' '}\n {formatVolume(reconciliation.wellheadVolume, unit)}\n {reconciliation.theoreticalTotal !== null &&\n ` · Theoretical ${formatVolume(reconciliation.theoreticalTotal, unit)}`}\n {reconciliation.reconciliationFactor !== null &&\n ` · Reconciliation factor ${formatNumber(reconciliation.reconciliationFactor, {\n maximumFractionDigits: 4,\n })}`}\n </p>\n )}\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"alNotes\">Notes</Label>\n <Textarea\n id=\"alNotes\"\n rows={2}\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n placeholder=\"Basis for the split (well tests, meter proving, agreements).\"\n />\n </div>\n\n {formError && (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {formError}\n </p>\n )}\n </div>\n\n <ResponsiveDialogFooter className=\"max-md:flex-col-reverse max-md:gap-2\">\n <Button type=\"button\" variant=\"outline\" className=\"max-md:h-11\" onClick={close}>\n Cancel\n </Button>\n <Button type=\"submit\" className=\"max-md:h-11\" disabled={createAllocation.isPending}>\n {createAllocation.isPending ? 'Saving…' : 'Save draft allocation'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { TheAllocationEntry } from '../types';
2
+
3
+ interface AssignEntitlementDialogProps {
4
+ entry: TheAllocationEntry | null;
5
+ /** Net volume of the entry already assigned to contracts. */
6
+ assignedVolume: number;
7
+ onOpenChange: (open: boolean) => void;
8
+ }
9
+ export declare function AssignEntitlementDialog({ entry, assignedVolume, onOpenChange, }: AssignEntitlementDialogProps): import("react").JSX.Element;
10
+ export {};
11
+ //# sourceMappingURL=AssignEntitlementDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssignEntitlementDialog.d.ts","sourceRoot":"","sources":["../../../src/theglobalfuel/components/AssignEntitlementDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAqBH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,UAAU,4BAA4B;IACpC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACjC,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAID,wBAAgB,uBAAuB,CAAC,EACtC,KAAK,EACL,cAAc,EACd,YAAY,GACb,EAAE,4BAA4B,+BAsH9B"}
@@ -0,0 +1,117 @@
1
+ import { formatNumber as e, formatVolume as t } from "../utils/formatters.js";
2
+ import { useTheAllSellContracts as n, useTheCreateContractEntitlement as r } from "../hooks/useTheGlobalFuelApi.js";
3
+ import { useNotify as i } from "../hooks/useNotify.js";
4
+ import "../hooks/index.js";
5
+ import { useMemo as a, useState as o } from "react";
6
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
7
+ import { Button as l, Input as u, Label as d, ResponsiveDialog as f, ResponsiveDialogContent as p, ResponsiveDialogDescription as m, ResponsiveDialogFooter as h, ResponsiveDialogHeader as g, ResponsiveDialogTitle as _, Select as v, SelectContent as y, SelectItem as b, SelectTrigger as x, SelectValue as S } from "@burdenoff/fe-libs/ui";
8
+ //#region src/theglobalfuel/components/AssignEntitlementDialog.tsx
9
+ var C = (e) => Math.round(e * 1e6) / 1e6;
10
+ function w({ entry: w, assignedVolume: T, onOpenChange: E }) {
11
+ let D = i(), O = !!w, k = n({ enabled: O }), A = r(), j = w ? Math.max(C(w.netRevenueVolume - T), 0) : 0, M = a(() => (k.data?.items ?? []).filter((e) => (e.status === "SIGNED" || e.status === "ACTIVE") && e.unit === w?.unit), [k.data, w?.unit]), [N, P] = o(""), [F, I] = o(""), [L, R] = o(null), z = w ? `open:${w.id}` : "closed", [B, V] = o(z);
12
+ z !== B && (V(z), P(""), I(""), R(null));
13
+ let H = async () => {
14
+ if (!w) return;
15
+ if (!N) {
16
+ R("Pick a contract.");
17
+ return;
18
+ }
19
+ let e = F.trim() === "" ? null : Number(F);
20
+ if (e !== null && (!Number.isFinite(e) || e <= 0 || e > j + 1e-6)) {
21
+ R(`Enter a volume greater than 0 and at most ${t(j, w.unit)}.`);
22
+ return;
23
+ }
24
+ R(null);
25
+ try {
26
+ await A.mutateAsync({
27
+ contractId: N,
28
+ allocationEntryId: w.id,
29
+ volume: e
30
+ }), D.success("Volume assigned to the contract"), E(!1);
31
+ } catch (e) {
32
+ R(e instanceof Error ? e.message : "Failed to assign the volume."), D.fromError(e);
33
+ }
34
+ };
35
+ return /* @__PURE__ */ s(f, {
36
+ open: O,
37
+ onOpenChange: E,
38
+ children: /* @__PURE__ */ c(p, {
39
+ "data-testid": "assign-entitlement-dialog",
40
+ children: [
41
+ /* @__PURE__ */ c(g, { children: [/* @__PURE__ */ s(_, { children: "Assign to contract" }), /* @__PURE__ */ s(m, { children: w ? `${w.asset?.name ?? "Asset"}: ${t(j, w.unit)} of ${t(w.netRevenueVolume, w.unit)} net revenue volume is not yet assigned.` : "" })] }),
42
+ /* @__PURE__ */ c("div", {
43
+ className: "grid gap-4 py-2",
44
+ children: [
45
+ /* @__PURE__ */ c("div", {
46
+ className: "grid gap-1.5",
47
+ children: [/* @__PURE__ */ s(d, {
48
+ htmlFor: "entContract",
49
+ children: "Sell contract *"
50
+ }), /* @__PURE__ */ c(v, {
51
+ value: N,
52
+ onValueChange: P,
53
+ children: [/* @__PURE__ */ s(x, {
54
+ id: "entContract",
55
+ className: "h-11 md:h-10",
56
+ children: /* @__PURE__ */ s(S, { placeholder: M.length === 0 ? "No signed or active sell contract" : "Select a contract" })
57
+ }), /* @__PURE__ */ s(y, { children: M.map((e) => /* @__PURE__ */ c(b, {
58
+ value: e.id,
59
+ children: [
60
+ e.reference,
61
+ " — ",
62
+ e.counterparty
63
+ ]
64
+ }, e.id)) })]
65
+ })]
66
+ }),
67
+ /* @__PURE__ */ c("div", {
68
+ className: "grid gap-1.5",
69
+ children: [
70
+ /* @__PURE__ */ s(d, {
71
+ htmlFor: "entVolume",
72
+ children: "Volume"
73
+ }),
74
+ /* @__PURE__ */ s(u, {
75
+ id: "entVolume",
76
+ type: "number",
77
+ inputMode: "decimal",
78
+ min: "0",
79
+ step: "any",
80
+ className: "h-11 tabular-nums md:h-10",
81
+ placeholder: e(j, { maximumFractionDigits: 6 }),
82
+ value: F,
83
+ onChange: (e) => I(e.target.value)
84
+ }),
85
+ /* @__PURE__ */ s("p", {
86
+ className: "text-xs text-text-tertiary",
87
+ children: "Leave blank to assign all of it."
88
+ })
89
+ ]
90
+ }),
91
+ L && /* @__PURE__ */ s("p", {
92
+ role: "alert",
93
+ className: "text-sm text-status-error-text",
94
+ children: L
95
+ })
96
+ ]
97
+ }),
98
+ /* @__PURE__ */ c(h, {
99
+ className: "max-md:flex-col-reverse max-md:gap-2",
100
+ children: [/* @__PURE__ */ s(l, {
101
+ variant: "outline",
102
+ onClick: () => E(!1),
103
+ children: "Cancel"
104
+ }), /* @__PURE__ */ s(l, {
105
+ onClick: () => void H(),
106
+ disabled: A.isPending || j <= 0,
107
+ children: A.isPending ? "Assigning…" : "Assign volume"
108
+ })]
109
+ })
110
+ ]
111
+ })
112
+ });
113
+ }
114
+ //#endregion
115
+ export { w as AssignEntitlementDialog };
116
+
117
+ //# sourceMappingURL=AssignEntitlementDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssignEntitlementDialog.js","names":[],"sources":["../../../src/theglobalfuel/components/AssignEntitlementDialog.tsx"],"sourcesContent":["/**\n * AssignEntitlementDialog — assign net volume from one entry of an APPROVED\n * allocation to a sell contract (BOFF-10787).\n * @module theglobalfuel/components\n *\n * Offers SIGNED/ACTIVE sell contracts in the entry's unit. The volume defaults\n * to the entry's remaining net revenue volume (net revenue minus what is\n * already assigned). Writes `theCreateContractEntitlement`; the svc re-checks\n * eligibility, unit, commodity and the remaining volume under a lock.\n */\n\nimport { useMemo, useState } from 'react';\nimport {\n Button,\n Input,\n Label,\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 { useNotify, useTheAllSellContracts, useTheCreateContractEntitlement } from '../hooks';\nimport { formatNumber, formatVolume } from '../utils/formatters';\nimport type { TheAllocationEntry } from '../types';\n\ninterface AssignEntitlementDialogProps {\n entry: TheAllocationEntry | null;\n /** Net volume of the entry already assigned to contracts. */\n assignedVolume: number;\n onOpenChange: (open: boolean) => void;\n}\n\nconst round6 = (n: number): number => Math.round(n * 1e6) / 1e6;\n\nexport function AssignEntitlementDialog({\n entry,\n assignedVolume,\n onOpenChange,\n}: AssignEntitlementDialogProps) {\n const notify = useNotify();\n const open = Boolean(entry);\n const contractsQuery = useTheAllSellContracts({ enabled: open });\n const create = useTheCreateContractEntitlement();\n const remaining = entry ? Math.max(round6(entry.netRevenueVolume - assignedVolume), 0) : 0;\n\n const contracts = useMemo(\n () =>\n (contractsQuery.data?.items ?? []).filter(\n (c) => (c.status === 'SIGNED' || c.status === 'ACTIVE') && c.unit === entry?.unit\n ),\n [contractsQuery.data, entry?.unit]\n );\n\n const [contractId, setContractId] = useState('');\n const [volume, setVolume] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const seedKey = entry ? `open:${entry.id}` : 'closed';\n const [prevSeedKey, setPrevSeedKey] = useState(seedKey);\n if (seedKey !== prevSeedKey) {\n setPrevSeedKey(seedKey);\n setContractId('');\n setVolume('');\n setError(null);\n }\n\n const submit = async () => {\n if (!entry) return;\n if (!contractId) {\n setError('Pick a contract.');\n return;\n }\n const n = volume.trim() === '' ? null : Number(volume);\n if (n !== null && (!Number.isFinite(n) || n <= 0 || n > remaining + 1e-6)) {\n setError(`Enter a volume greater than 0 and at most ${formatVolume(remaining, entry.unit)}.`);\n return;\n }\n setError(null);\n try {\n await create.mutateAsync({ contractId, allocationEntryId: entry.id, volume: n });\n notify.success('Volume assigned to the contract');\n onOpenChange(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to assign the volume.');\n notify.fromError(err);\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent data-testid=\"assign-entitlement-dialog\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Assign to contract</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {entry\n ? `${entry.asset?.name ?? 'Asset'}: ${formatVolume(remaining, entry.unit)} of ${formatVolume(\n entry.netRevenueVolume,\n entry.unit\n )} net revenue volume is not yet assigned.`\n : ''}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"entContract\">Sell contract *</Label>\n <Select value={contractId} onValueChange={setContractId}>\n <SelectTrigger id=\"entContract\" className=\"h-11 md:h-10\">\n <SelectValue\n placeholder={\n contracts.length === 0\n ? 'No signed or active sell contract'\n : 'Select a contract'\n }\n />\n </SelectTrigger>\n <SelectContent>\n {contracts.map((c) => (\n <SelectItem key={c.id} value={c.id}>\n {c.reference} — {c.counterparty}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"entVolume\">Volume</Label>\n <Input\n id=\"entVolume\"\n type=\"number\"\n inputMode=\"decimal\"\n min=\"0\"\n step=\"any\"\n className=\"h-11 tabular-nums md:h-10\"\n placeholder={formatNumber(remaining, { maximumFractionDigits: 6 })}\n value={volume}\n onChange={(e) => setVolume(e.target.value)}\n />\n <p className=\"text-xs text-text-tertiary\">Leave blank to assign all of it.</p>\n </div>\n {error && (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error}\n </p>\n )}\n </div>\n <ResponsiveDialogFooter className=\"max-md:flex-col-reverse max-md:gap-2\">\n <Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button onClick={() => void submit()} disabled={create.isPending || remaining <= 0}>\n {create.isPending ? 'Assigning…' : 'Assign volume'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * ConfirmActionDialog — a small confirmation step before a destructive
3
+ * action (deleting an interest deck, removing an entitlement).
4
+ * @module theglobalfuel/components
5
+ */
6
+ interface ConfirmActionDialogProps {
7
+ open: boolean;
8
+ title: string;
9
+ description: string;
10
+ confirmLabel: string;
11
+ pending?: boolean;
12
+ onConfirm: () => void;
13
+ onCancel: () => void;
14
+ }
15
+ export declare function ConfirmActionDialog({ open, title, description, confirmLabel, pending, onConfirm, onCancel, }: ConfirmActionDialogProps): import("react").JSX.Element;
16
+ export {};
17
+ //# sourceMappingURL=ConfirmActionDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfirmActionDialog.d.ts","sourceRoot":"","sources":["../../../src/theglobalfuel/components/ConfirmActionDialog.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,UAAU,wBAAwB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,KAAK,EACL,WAAW,EACX,YAAY,EACZ,OAAe,EACf,SAAS,EACT,QAAQ,GACT,EAAE,wBAAwB,+BAmB1B"}
@@ -0,0 +1,29 @@
1
+ import { jsx as e, jsxs as t } from "react/jsx-runtime";
2
+ import { Button as n, ResponsiveDialog as r, ResponsiveDialogContent as i, ResponsiveDialogDescription as a, ResponsiveDialogFooter as o, ResponsiveDialogHeader as s, ResponsiveDialogTitle as c } from "@burdenoff/fe-libs/ui";
3
+ //#region src/theglobalfuel/components/ConfirmActionDialog.tsx
4
+ function l({ open: l, title: u, description: d, confirmLabel: f, pending: p = !1, onConfirm: m, onCancel: h }) {
5
+ return /* @__PURE__ */ e(r, {
6
+ open: l,
7
+ onOpenChange: (e) => !e && h(),
8
+ children: /* @__PURE__ */ t(i, {
9
+ "data-testid": "confirm-action-dialog",
10
+ children: [/* @__PURE__ */ t(s, { children: [/* @__PURE__ */ e(c, { children: u }), /* @__PURE__ */ e(a, { children: d })] }), /* @__PURE__ */ t(o, {
11
+ className: "max-md:flex-col-reverse max-md:gap-2",
12
+ children: [/* @__PURE__ */ e(n, {
13
+ variant: "outline",
14
+ onClick: h,
15
+ children: "Cancel"
16
+ }), /* @__PURE__ */ e(n, {
17
+ variant: "destructive",
18
+ onClick: m,
19
+ disabled: p,
20
+ children: p ? "Working…" : f
21
+ })]
22
+ })]
23
+ })
24
+ });
25
+ }
26
+ //#endregion
27
+ export { l as ConfirmActionDialog };
28
+
29
+ //# sourceMappingURL=ConfirmActionDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfirmActionDialog.js","names":[],"sources":["../../../src/theglobalfuel/components/ConfirmActionDialog.tsx"],"sourcesContent":["/**\n * ConfirmActionDialog — a small confirmation step before a destructive\n * action (deleting an interest deck, removing an entitlement).\n * @module theglobalfuel/components\n */\n\nimport {\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\n\ninterface ConfirmActionDialogProps {\n open: boolean;\n title: string;\n description: string;\n confirmLabel: string;\n pending?: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\nexport function ConfirmActionDialog({\n open,\n title,\n description,\n confirmLabel,\n pending = false,\n onConfirm,\n onCancel,\n}: ConfirmActionDialogProps) {\n return (\n <ResponsiveDialog open={open} onOpenChange={(o) => !o && onCancel()}>\n <ResponsiveDialogContent data-testid=\"confirm-action-dialog\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{title}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>{description}</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <ResponsiveDialogFooter className=\"max-md:flex-col-reverse max-md:gap-2\">\n <Button variant=\"outline\" onClick={onCancel}>\n Cancel\n </Button>\n <Button variant=\"destructive\" onClick={onConfirm} disabled={pending}>\n {pending ? 'Working…' : confirmLabel}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;AA0BA,SAAgB,EAAoB,EAClC,SACA,UACA,gBACA,iBACA,aAAU,IACV,cACA,eAC2B;AAC3B,QACE,kBAAC,GAAD;EAAwB;EAAM,eAAe,MAAM,CAAC,KAAK,GAAU;YACjE,kBAAC,GAAD;GAAyB,eAAY;aAArC,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAwB,GAA8B,CAAA,EACtD,kBAAC,GAAD,EAAA,UAA8B,GAA0C,CAAA,CACjD,EAAA,CAAA,EACzB,kBAAC,GAAD;IAAwB,WAAU;cAAlC,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,SAAS;eAAU;KAEpC,CAAA,EACT,kBAAC,GAAD;KAAQ,SAAQ;KAAc,SAAS;KAAW,UAAU;eACzD,IAAU,aAAa;KACjB,CAAA,CACc;MACD;;EACT,CAAA"}
@@ -0,0 +1,11 @@
1
+ import { TheProductionAllocation } from '../types';
2
+
3
+ interface CorrectAllocationSheetProps {
4
+ open: boolean;
5
+ onOpenChange: (open: boolean) => void;
6
+ /** The APPROVED original allocation being corrected. */
7
+ allocation: TheProductionAllocation | null;
8
+ }
9
+ export declare function CorrectAllocationSheet({ open, onOpenChange, allocation, }: CorrectAllocationSheetProps): import("react").JSX.Element;
10
+ export {};
11
+ //# sourceMappingURL=CorrectAllocationSheet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CorrectAllocationSheet.d.ts","sourceRoot":"","sources":["../../../src/theglobalfuel/components/CorrectAllocationSheet.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAoCH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,UAAU,2BAA2B;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,wDAAwD;IACxD,UAAU,EAAE,uBAAuB,GAAG,IAAI,CAAC;CAC5C;AAED,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,YAAY,EACZ,UAAU,GACX,EAAE,2BAA2B,+BAqQ7B"}
@@ -0,0 +1,235 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { formatDate as t, formatVolume as n } from "../utils/formatters.js";
3
+ import { useTheAllAssets as r, useTheCreateAllocationCorrection as i, useTheProductionAllocations as a } from "../hooks/useTheGlobalFuelApi.js";
4
+ import { useNotify as o } from "../hooks/useNotify.js";
5
+ import "../hooks/index.js";
6
+ import { appliedCorrectionDeltas as ee, buildCorrectionInput as s, newCorrectionRow as c, previewCorrection as l } from "../utils/correctionForm.js";
7
+ import { useMemo as u, useState as d } from "react";
8
+ import { Plus as f, Trash2 as p } from "lucide-react";
9
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
10
+ import { Button as g, Input as _, Label as v, ResponsiveDialog as y, ResponsiveDialogContent as te, ResponsiveDialogDescription as ne, ResponsiveDialogFooter as re, ResponsiveDialogHeader as b, ResponsiveDialogTitle as x, Select as S, SelectContent as C, SelectItem as w, SelectTrigger as T, SelectValue as E, Textarea as D } from "@burdenoff/fe-libs/ui";
11
+ //#region src/theglobalfuel/components/CorrectAllocationSheet.tsx
12
+ function O({ open: O, onOpenChange: k, allocation: A }) {
13
+ let j = o(), M = r({ enabled: O }), N = a({
14
+ correctsAllocationId: A?.id,
15
+ pagination: { limit: 100 }
16
+ }, { enabled: O && !!A }), P = i(), F = u(() => [...M.data?.items ?? []].sort((e, t) => e.name.localeCompare(t.name)), [M.data]), I = u(() => ee(N.data?.items ?? []), [N.data]), [L, R] = d(() => [c()]), [z, B] = d(""), [V, H] = d(""), [U, W] = d(""), [G, K] = d(""), [q, J] = d(null), Y = O && A ? `open:${A.id}` : "closed", [ie, ae] = d(Y);
17
+ Y !== ie && (ae(Y), O && A && (R([c(A.entries[0]?.assetId ?? "")]), B(""), H(""), W(""), K(""), J(null)));
18
+ let X = A?.unit ?? "BARRELS", Z = A ? l(A, L, I) : null, Q = (e, t) => R((n) => n.map((n) => n.key === e ? {
19
+ ...n,
20
+ ...t
21
+ } : n)), oe = (e) => R((t) => t.length > 1 ? t.filter((t) => t.key !== e) : t), $ = () => k(!1), se = async () => {
22
+ if (!A) return;
23
+ let e = s({
24
+ allocationId: A.id,
25
+ rows: L,
26
+ reason: z,
27
+ notes: V,
28
+ bookFrom: U,
29
+ bookTo: G
30
+ });
31
+ if (!e.ok) {
32
+ J(e.error), j.error(e.error);
33
+ return;
34
+ }
35
+ J(null);
36
+ try {
37
+ await P.mutateAsync(e.input), j.success("Correction drafted — approve it to apply it to the period"), $();
38
+ } catch (e) {
39
+ J(e instanceof Error ? e.message : "Failed to record the correction."), j.fromError(e);
40
+ }
41
+ };
42
+ return /* @__PURE__ */ m(y, {
43
+ open: O && !!A,
44
+ onOpenChange: k,
45
+ children: /* @__PURE__ */ h(te, {
46
+ "data-testid": "correct-allocation-sheet",
47
+ className: "md:max-w-2xl",
48
+ children: [/* @__PURE__ */ h(b, { children: [/* @__PURE__ */ m(x, { children: "Correct allocation" }), /* @__PURE__ */ m(ne, { children: A ? `Prior-period adjustment to ${A.measurementAsset?.name ?? "this allocation"}, ${t(A.periodStart)} – ${t(A.periodEnd)}. The approved allocation stays as it is.` : "" })] }), /* @__PURE__ */ h("form", {
49
+ onSubmit: (e) => {
50
+ e.preventDefault(), se();
51
+ },
52
+ children: [/* @__PURE__ */ h("div", {
53
+ className: "grid gap-4 py-2",
54
+ children: [
55
+ L.map((t, r) => {
56
+ let i = Z?.rows[r];
57
+ return /* @__PURE__ */ h("div", {
58
+ className: "grid gap-2 rounded-lg border border-border-seam p-3",
59
+ "data-testid": "correction-row",
60
+ children: [/* @__PURE__ */ h("div", {
61
+ className: "grid grid-cols-1 gap-2 md:grid-cols-[minmax(0,2fr)_minmax(0,1fr)_auto]",
62
+ children: [
63
+ /* @__PURE__ */ h("div", {
64
+ className: "grid gap-1",
65
+ children: [/* @__PURE__ */ m(v, {
66
+ htmlFor: `${t.key}-asset`,
67
+ className: "text-xs",
68
+ children: "Asset"
69
+ }), /* @__PURE__ */ h(S, {
70
+ value: t.assetId,
71
+ onValueChange: (e) => Q(t.key, { assetId: e }),
72
+ children: [/* @__PURE__ */ m(T, {
73
+ id: `${t.key}-asset`,
74
+ className: "h-10",
75
+ children: /* @__PURE__ */ m(E, { placeholder: "Select an asset" })
76
+ }), /* @__PURE__ */ m(C, { children: F.map((e) => /* @__PURE__ */ m(w, {
77
+ value: e.id,
78
+ children: e.name
79
+ }, e.id)) })]
80
+ })]
81
+ }),
82
+ /* @__PURE__ */ h("div", {
83
+ className: "grid gap-1",
84
+ children: [/* @__PURE__ */ m(v, {
85
+ htmlFor: `${t.key}-delta`,
86
+ className: "text-xs",
87
+ children: "Volume change"
88
+ }), /* @__PURE__ */ m(_, {
89
+ id: `${t.key}-delta`,
90
+ type: "number",
91
+ inputMode: "decimal",
92
+ step: "any",
93
+ className: "h-10 tabular-nums",
94
+ placeholder: "e.g. -40",
95
+ value: t.delta,
96
+ onChange: (e) => Q(t.key, { delta: e.target.value })
97
+ })]
98
+ }),
99
+ /* @__PURE__ */ m("div", {
100
+ className: "flex items-end",
101
+ children: /* @__PURE__ */ m(g, {
102
+ type: "button",
103
+ variant: "ghost",
104
+ size: "icon",
105
+ "aria-label": `Remove row ${r + 1}`,
106
+ disabled: L.length === 1,
107
+ onClick: () => oe(t.key),
108
+ children: /* @__PURE__ */ m(p, { className: "h-4 w-4" })
109
+ })
110
+ })
111
+ ]
112
+ }), i && t.assetId && /* @__PURE__ */ h("p", {
113
+ className: e("text-xs tabular-nums", i.negative ? "text-status-error-text" : "text-text-secondary"),
114
+ children: [
115
+ n(i.before, X),
116
+ " → ",
117
+ n(i.after, X),
118
+ i.negative ? " — below zero, reduce the change" : ""
119
+ ]
120
+ })]
121
+ }, t.key);
122
+ }),
123
+ /* @__PURE__ */ m("div", { children: /* @__PURE__ */ h(g, {
124
+ type: "button",
125
+ variant: "outline",
126
+ size: "sm",
127
+ onClick: () => R((e) => [...e, c()]),
128
+ children: [/* @__PURE__ */ m(f, { className: "mr-1.5 h-4 w-4" }), " Add asset"]
129
+ }) }),
130
+ Z && /* @__PURE__ */ h("p", {
131
+ className: "rounded-lg bg-bg-surface px-3 py-2 text-sm tabular-nums text-text-secondary",
132
+ "data-testid": "correction-total",
133
+ children: [
134
+ "Measured volume change ",
135
+ Z.measuredDelta > 0 ? "+" : "",
136
+ n(Z.measuredDelta, X)
137
+ ]
138
+ }),
139
+ /* @__PURE__ */ h("div", {
140
+ className: "grid gap-1.5",
141
+ children: [
142
+ /* @__PURE__ */ m("p", {
143
+ className: "text-sm font-medium text-text-primary",
144
+ children: "Book in period"
145
+ }),
146
+ /* @__PURE__ */ h("div", {
147
+ className: "flex flex-wrap items-end gap-3",
148
+ children: [/* @__PURE__ */ h("div", {
149
+ className: "grid gap-1",
150
+ children: [/* @__PURE__ */ m(v, {
151
+ htmlFor: "corrFrom",
152
+ className: "text-xs",
153
+ children: "From"
154
+ }), /* @__PURE__ */ m(_, {
155
+ id: "corrFrom",
156
+ type: "date",
157
+ className: "h-10 w-44",
158
+ value: U,
159
+ onChange: (e) => W(e.target.value)
160
+ })]
161
+ }), /* @__PURE__ */ h("div", {
162
+ className: "grid gap-1",
163
+ children: [/* @__PURE__ */ m(v, {
164
+ htmlFor: "corrTo",
165
+ className: "text-xs",
166
+ children: "To"
167
+ }), /* @__PURE__ */ m(_, {
168
+ id: "corrTo",
169
+ type: "date",
170
+ className: "h-10 w-44",
171
+ value: G,
172
+ onChange: (e) => K(e.target.value)
173
+ })]
174
+ })]
175
+ }),
176
+ /* @__PURE__ */ m("p", {
177
+ className: "text-xs text-text-tertiary",
178
+ children: "Leave blank to book it in the original’s period. If that period is closed, book it in a later, open period."
179
+ })
180
+ ]
181
+ }),
182
+ /* @__PURE__ */ h("div", {
183
+ className: "grid gap-1.5",
184
+ children: [/* @__PURE__ */ m(v, {
185
+ htmlFor: "corrReason",
186
+ children: "Reason *"
187
+ }), /* @__PURE__ */ m(D, {
188
+ id: "corrReason",
189
+ rows: 2,
190
+ value: z,
191
+ onChange: (e) => B(e.target.value),
192
+ placeholder: "e.g. Meter re-proved; late well-test; ownership restated."
193
+ })]
194
+ }),
195
+ /* @__PURE__ */ h("div", {
196
+ className: "grid gap-1.5",
197
+ children: [/* @__PURE__ */ m(v, {
198
+ htmlFor: "corrNotes",
199
+ children: "Notes"
200
+ }), /* @__PURE__ */ m(D, {
201
+ id: "corrNotes",
202
+ rows: 2,
203
+ value: V,
204
+ onChange: (e) => H(e.target.value)
205
+ })]
206
+ }),
207
+ q && /* @__PURE__ */ m("p", {
208
+ role: "alert",
209
+ className: "text-sm text-status-error-text",
210
+ children: q
211
+ })
212
+ ]
213
+ }), /* @__PURE__ */ h(re, {
214
+ className: "max-md:flex-col-reverse max-md:gap-2",
215
+ children: [/* @__PURE__ */ m(g, {
216
+ type: "button",
217
+ variant: "outline",
218
+ className: "max-md:h-11",
219
+ onClick: $,
220
+ children: "Cancel"
221
+ }), /* @__PURE__ */ m(g, {
222
+ type: "submit",
223
+ className: "max-md:h-11",
224
+ disabled: P.isPending,
225
+ children: P.isPending ? "Saving…" : "Save draft correction"
226
+ })]
227
+ })]
228
+ })]
229
+ })
230
+ });
231
+ }
232
+ //#endregion
233
+ export { O as CorrectAllocationSheet };
234
+
235
+ //# sourceMappingURL=CorrectAllocationSheet.js.map