All files / src/pages/flowsheet-page/economics/cost-curves/library CostCurveLibraryPanel.tsx

89.47% Statements 34/38
56.52% Branches 26/46
50% Functions 2/4
100% Lines 27/27

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                              76x         48x   53x     48x 48x       57x                               48x 48x   48x 48x 1x 48x     48x 48x                       48x   48x   5x 48x 53x       1x 1x 89x   312x 52x 48x                                 22x                             192x      
import { Library } from "lucide-react";
import { useState } from "react";
import { Spinner } from "@/ahuora-design-system/ui/spinner";
import type {
  CostCurveAuthoring,
  CostCurveEquipmentCategoryRead,
  CostCurveRead,
  PatchedCostCurveAuthoring,
} from "@/api/apiStore.gen";
import { CostCurveDialog } from "../authoring/CostCurveDialog";
import { type CostCurveDraft, costCurveCreatePayload } from "../model/payloads";
import type { CostCurveUsageSummary, SaveState } from "../types";
import { FormState } from "../ui/FormState";
import { CostCurveLibraryCard } from "./CostCurveLibraryCard";
 
const emptyUsage: CostCurveUsageSummary = {
  unitNames: [],
  capitalLineLabels: [],
};
 
export function CostCurveLibraryPanel({
  curves,
  equipmentOptions = [],
  canEdit,
  saving,
  loading = false,
  loadError = false,
  onCreateCurve,
  onPatchCurve,
  onDeleteCurve,
  usageByCurveId = {},
}: {
  curves: CostCurveRead[];
  equipmentOptions?: readonly CostCurveEquipmentCategoryRead[];
  canEdit: boolean;
  saving: boolean;
  loading?: boolean;
  loadError?: boolean;
  onCreateCurve: (curve: CostCurveAuthoring) => Promise<void | CostCurveRead>;
  onPatchCurve: (
    curveId: number,
    patch: PatchedCostCurveAuthoring,
  ) => Promise<void>;
  onDeleteCurve: (curveId: number) => Promise<void>;
  usageByCurveId?: Record<number, CostCurveUsageSummary>;
}) {
  const activeCurves = curves.filter((curve) => curve.active !== false);
  const [libraryState, setLibraryState] = useState<SaveState>({
    kind: "idle",
  });
  const markSaved = (message: string) => {
    setLibraryState({ kind: "saved", message });
  };
  return (
    <section
      className="rounded-md border bg-card p-4"
      aria-label="Cost curve library"
    >
      <div className="mb-3 flex flex-wrap items-center justify-between gap-2">
        <div className="flex items-start gap-2">
          <Library className="mt-0.5 size-4 text-primary" aria-hidden="true" />
          <div>
            <h4 className="text-sm font-semibold">Cost curve library</h4>
            <p className="mt-1.5 text-xs text-muted-foreground">
              Create or edit study curves before assigning them to capital
              lines.
            </p>
          </div>
        </div>
        <CostCurveDialog
          canEdit={canEdit && !loading && !loadError}
          saving={saving}
          draftDefaults={{
            equipment_category: equipmentOptions[0]?.value ?? "",
          }}
          equipmentOptions={equipmentOptions}
          existingCurves={curves}
          onSubmit={async (draft: CostCurveDraft) => {
            await onCreateCurve(costCurveCreatePayload(draft));
            markSaved("Cost curve created");
          }}
        />
      </div>
      <FormState state={libraryState} />
      {loading ? (
        <div className="flex items-center gap-2 text-sm text-muted-foreground">
          <Spinner size="small" />
          Loading cost curves
        </div>
      ) : loadError ? (
        <div
          className="rounded-md border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive"
          role="alert"
        >
          Could not load cost curve library.
        </div>
      ) : curves.length === 0 ? (
        <div className="text-sm text-muted-foreground">No cost curves</div>
      ) : (
        <div className="grid gap-3">
          {activeCurves.map((curve) => (
            <CostCurveLibraryCard
              key={curve.id}
              curve={curve}
              curves={curves}
              canEdit={canEdit}
              saving={saving}
              equipmentOptions={equipmentOptions}
              usage={usageByCurveId[curve.id] ?? emptyUsage}
              onPatchCurve={onPatchCurve}
              onDeleteCurve={onDeleteCurve}
              onSaved={markSaved}
            />
          ))}
        </div>
      )}
    </section>
  );
}