"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import type { ScenarioInputPayload } from "@/lib/api";
const LOCATION_OPTIONS = [
{ value: "RJ", label: "Rajasthan (High Solar)" },
{ value: "GJ", label: "Gujarat (High Solar)" },
{ value: "AP", label: "Andhra Pradesh" },
{ value: "TN", label: "Tamil Nadu" },
{ value: "MP", label: "Madhya Pradesh" },
{ value: "KA", label: "Karnataka" },
];
const STEP_LABELS = [
"Project Info",
"Solar",
"Wind",
"BESS",
"Solver",
"Review",
];
interface WizardState {
name: string;
cod_date: string;
// Solar
solar_enabled: boolean;
solar_location: string;
solar_ac_mw: number;
solar_dc_ac_ratio: number;
solar_dc_mwp: number;
// Wind
wind_enabled: boolean;
wind_location: string;
wind_mw: number;
// BESS
bess_enabled: boolean;
bess_mwh: number;
bess_mw: number;
// Solver
solver_mode: "solve_tariff" | "fixed_tariff";
target_irr: number;
fixed_tariff: number;
}
const DEFAULT_STATE: WizardState = {
name: "",
cod_date: "2027-04-01",
solar_enabled: true,
solar_location: "RJ",
solar_ac_mw: 100,
solar_dc_ac_ratio: 1.4,
solar_dc_mwp: 140,
wind_enabled: false,
wind_location: "RJ",
wind_mw: 50,
bess_enabled: false,
bess_mwh: 200,
bess_mw: 50,
solver_mode: "solve_tariff",
target_irr: 0.18,
fixed_tariff: 3.5,
};
function Field({
label,
children,
}: {
label: string;
children: React.ReactNode;
}) {
return (
{children}
);
}
function Input({
value,
onChange,
type = "text",
step,
min,
max,
readOnly,
className = "",
}: {
value: string | number;
onChange: (v: string) => void;
type?: string;
step?: number;
min?: number;
max?: number;
readOnly?: boolean;
className?: string;
}) {
return (
onChange(e.target.value)}
className={`border rounded px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-primary bg-background ${readOnly ? "bg-muted cursor-default" : ""} ${className}`}
/>
);
}
function Select({
value,
onChange,
options,
}: {
value: string;
onChange: (v: string) => void;
options: { value: string; label: string }[];
}) {
return (
);
}
function Toggle({
label,
checked,
onChange,
}: {
label: string;
checked: boolean;
onChange: (v: boolean) => void;
}) {
return (
);
}
// ---------------------------------------------------------------------------
// Step components
// ---------------------------------------------------------------------------
function StepProjectInfo({
state,
set,
}: {
state: WizardState;
set: (k: keyof WizardState, v: WizardState[keyof WizardState]) => void;
}) {
return (
Project Information
set("name", v)}
/>
set("cod_date", v)}
/>
);
}
function StepSolar({
state,
set,
}: {
state: WizardState;
set: (k: keyof WizardState, v: WizardState[keyof WizardState]) => void;
}) {
return (
Solar Generation
set("solar_enabled", v)}
/>
{state.solar_enabled && (
<>
set("solar_ac_mw", Number(v))}
/>
{
const ratio = Number(v);
set("solar_dc_ac_ratio", ratio);
set("solar_dc_mwp", parseFloat((state.solar_ac_mw * ratio).toFixed(3)));
}}
/>
{}}
/>
>
)}
);
}
function StepWind({
state,
set,
}: {
state: WizardState;
set: (k: keyof WizardState, v: WizardState[keyof WizardState]) => void;
}) {
return (
Wind Generation
set("wind_enabled", v)}
/>
{state.wind_enabled && (
<>
set("wind_mw", Number(v))}
/>
>
)}
);
}
function StepBess({
state,
set,
}: {
state: WizardState;
set: (k: keyof WizardState, v: WizardState[keyof WizardState]) => void;
}) {
return (
BESS (Battery Storage)
set("bess_enabled", v)}
/>
{state.bess_enabled && (
<>
set("bess_mwh", Number(v))}
/>
set("bess_mw", Number(v))}
/>
>
)}
);
}
function StepSolver({
state,
set,
}: {
state: WizardState;
set: (k: keyof WizardState, v: WizardState[keyof WizardState]) => void;
}) {
return (
Solver & Tariff
{state.solver_mode === "solve_tariff" ? (
set("target_irr", Number(v))}
/>
) : (
set("fixed_tariff", Number(v))}
/>
)}
);
}
function StepReview({ state }: { state: WizardState }) {
return (
Review & Submit
- Name
- {state.name || "—"}
- COD
- {state.cod_date}
{state.solar_enabled && (
<>
- Solar
-
{state.solar_ac_mw} MW AC / {state.solar_dc_mwp} MWp DC ({state.solar_location}, {state.solar_dc_ac_ratio}x)
>
)}
{state.wind_enabled && (
<>
- Wind
-
{state.wind_mw} MW ({state.wind_location})
>
)}
{state.bess_enabled && (
<>
- BESS
-
{state.bess_mwh} MWh / {state.bess_mw} MW
>
)}
- Solver
-
{state.solver_mode === "solve_tariff"
? `Solve tariff @ IRR ${(state.target_irr * 100).toFixed(0)}%`
: `Fixed ₹${state.fixed_tariff}/kWh`}
);
}
// ---------------------------------------------------------------------------
// Main wizard
// ---------------------------------------------------------------------------
interface ScenarioWizardProps {
onSubmit: (name: string, inputs: ScenarioInputPayload) => Promise;
onCancel: () => void;
}
export function ScenarioWizard({ onSubmit, onCancel }: ScenarioWizardProps) {
const [step, setStep] = useState(0);
const [state, setState] = useState(DEFAULT_STATE);
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState(null);
function set(k: K, v: WizardState[K]) {
setState((prev) => ({ ...prev, [k]: v }));
}
const steps = [
,
,
,
,
,
,
];
function buildInputs(): ScenarioInputPayload {
return {
project: {
name: state.name,
capacity_solar_mwp: state.solar_enabled ? state.solar_dc_mwp : 0,
capacity_wind_mw: state.wind_enabled ? state.wind_mw : 0,
capacity_bess_mwh: state.bess_enabled ? state.bess_mwh : 0,
capacity_bess_mw: state.bess_enabled ? state.bess_mw : 0,
cod_date: state.cod_date,
},
solar: state.solar_enabled
? {
location_id: state.solar_location,
capacity_ac_mw: state.solar_ac_mw,
dc_ac_ratio: state.solar_dc_ac_ratio,
capacity_dc_mwp: state.solar_dc_mwp,
}
: null,
wind: state.wind_enabled
? { location_id: state.wind_location, capacity_mw: state.wind_mw }
: null,
solver:
state.solver_mode === "fixed_tariff"
? {
mode: "fixed_tariff",
fixed_tariff: state.fixed_tariff,
}
: {
mode: "solve_tariff",
target_equity_irr: state.target_irr,
},
};
}
async function handleSubmit() {
if (!state.name.trim()) {
setError("Please enter a scenario name.");
return;
}
setSubmitting(true);
setError(null);
try {
await onSubmit(state.name, buildInputs());
} catch (e) {
setError(e instanceof Error ? e.message : "Unknown error");
setSubmitting(false);
}
}
const isLast = step === steps.length - 1;
return (
{/* Progress bar */}
{STEP_LABELS.map((label, i) => (
))}
{/* Step content */}
{steps[step]}
{error && (
{error}
)}
{/* Navigation */}
{step > 0 && (
)}
{isLast ? (
) : (
)}
);
}