Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | 22x 22x 22x 1x 22x 24x 1x 22x 1x 23x 23x 1x 154x 1x 1x 1x 1x 4x 4x 1x 3x 2x 4x 1x 1x 1x 1x 1x | import { Badge } from "@/ahuora-design-system/ui/badge";
import type { EconomicsResultLineRead } from "@/api/apiStore.gen";
import { cn } from "@/lib/utils";
import {
capitalFactorLabel,
capitalFactorMeta,
capitalFactorsFromPayload,
formatAmountWithUnit,
} from "./economicsCapitalFactors";
export function CapitalCostBreakdown({
lines,
stale,
runId,
unitName,
className,
fullWidth = false,
}: {
lines: EconomicsResultLineRead[];
stale: boolean;
runId: number | null;
unitName?: string;
className?: string;
fullWidth?: boolean;
}) {
const matchingLines = lines.filter((line) =>
line.kind.toString().includes("capital"),
);
return (
<div
className={cn(
"mt-3 w-full rounded-md border border-input bg-muted/20 p-3 text-left",
!fullWidth && "md:w-1/2",
className,
)}
>
<div className="flex flex-wrap items-center justify-between gap-2">
<span className="text-sm font-semibold text-muted-foreground">
Capital cost
</span>
{stale && (
<Badge variant="secondary" size="xs" borderRadius="round">
Stale
</Badge>
)}
</div>
{matchingLines.length === 0 ? (
<div className="mt-2 text-xs text-muted-foreground">
{runId
? "No capital result is available for this unit."
: "Calculate results to see capital cost."}
</div>
) : (
<div className="mt-3 space-y-3">
{matchingLines.map((line) => (
<div key={line.id}>
{resultLineLabel(line.label, unitName) !== "Capital cost" && (
<div className="mb-2 text-xs text-muted-foreground">
{resultLineLabel(line.label, unitName)}
</div>
)}
<CapitalFactorBreakdown line={line} />
</div>
))}
</div>
)}
</div>
);
}
export function CapitalFactorBreakdown({
line,
}: {
line: EconomicsResultLineRead;
}) {
const factors = capitalFactorsFromPayload(line.warning_payload);
return (
<dl className="space-y-2.5">
{factors.map((factor, index) => (
<div
key={`${factor.kind}-${index}`}
className="grid grid-cols-[minmax(0,1fr)_auto] items-start gap-x-3"
>
<dt className="min-w-0">
<div className="truncate text-sm text-muted-foreground">
{capitalFactorLabel(factor)}
</div>
{capitalFactorMeta(factor) && (
<div className="mt-0.5 truncate text-xs leading-4 text-muted-foreground/70">
{capitalFactorMeta(factor)}
</div>
)}
</dt>
<dd className="justify-self-end text-right text-sm tabular-nums text-muted-foreground">
{formatAmountWithUnit(factor.amount, line.unit)}
</dd>
</div>
))}
<div className="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-x-3 border-t border-input/70 pt-2">
<dt className="text-sm font-semibold text-foreground">Total</dt>
<dd className="justify-self-end text-right text-sm font-semibold tabular-nums text-foreground">
{formatAmountWithUnit(line.amount, line.unit)}
</dd>
</div>
</dl>
);
}
function resultLineLabel(label: string, unitName?: string) {
const trimmedLabel = label.trim();
const trimmedUnitName = unitName?.trim();
Iif (!trimmedUnitName) return trimmedLabel;
if (trimmedLabel.toLowerCase().startsWith(trimmedUnitName.toLowerCase())) {
return capitalizeFirst(
trimmedLabel.slice(trimmedUnitName.length).trim() || "Result",
);
}
return trimmedLabel;
}
function capitalizeFirst(value: string) {
return value ? `${value[0].toUpperCase()}${value.slice(1)}` : value;
}
|