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                              103x         143x   145x     135x 135x       175x                               135x 135x   135x 135x 5x 135x     135x 135x                       135x   135x   10x 135x 145x       5x 5x 230x   777x 147x 135x                                 138x                             540x      
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>
  );
}