"use client";
import { useState, useCallback } from "react";
import {
BarChart,
Bar,
XAxis,
YAxis,
Tooltip,
ResponsiveContainer,
ReferenceLine,
LineChart,
Line,
CartesianGrid,
Legend,
} from "recharts";
import {
getAvailableProfiles,
getProfileStats,
getSolarProfile,
getWindProfile,
profileCsvUrl,
type ProfileStats,
} from "@/lib/api";
const LOCATIONS = [
{ id: "GJ", name: "Gujarat" },
{ id: "KA", name: "Karnataka" },
{ id: "RJ", name: "Rajasthan" },
];
const SOLAR_COLOR = "#f97316";
const WIND_COLOR = "#3b82f6";
interface StatsCardProps {
label: string;
value: string;
color?: string;
}
function StatsCard({ label, value, color }: StatsCardProps) {
return (
);
}
function CustomTooltip({ active, payload, label }: { active?: boolean; payload?: { value: number; name: string }[]; label?: string }) {
if (!active || !payload?.length) return null;
return (
{label}
{payload.map((entry, i) => (
{entry.name}:
{(entry.value * 100).toFixed(2)}%
))}
);
}
export function ProfileViewer() {
const [isOpen, setIsOpen] = useState(false);
const [activeTab, setActiveTab] = useState<"solar" | "wind" | "compare">("solar");
const [uploadedProfiles, setUploadedProfiles] = useState<{
solar?: number[];
wind?: number[];
filename?: string;
}>({});
const [isUploading, setIsUploading] = useState(false);
const [uploadError, setUploadError] = useState(null);
const [selectedFile, setSelectedFile] = useState<{
name: string;
path: string;
data: string[][];
} | null>(null);
const [isLoadingFile, setIsLoadingFile] = useState(false);
const API_BASE = typeof window !== "undefined"
? (typeof window !== "undefined" && (window as { location?: { hostname?: string } }).location?.hostname === "localhost"
? "http://localhost:8000"
: (window as { env?: { NEXT_PUBLIC_API_URL?: string } }).env?.NEXT_PUBLIC_API_URL || "/")
: "http://localhost:8000";
async function fetchAndDisplayFile(path: string, name: string) {
console.log("fetchAndDisplayFile called:", path, name);
setIsLoadingFile(true);
try {
const url = `${API_BASE}${path}?as_csv=true`;
console.log("Fetching:", url);
const res = await fetch(url);
console.log("Response status:", res.status);
const text = await res.text();
console.log("Response length:", text.length);
const lines = text.trim().split("\n");
console.log("Lines count:", lines.length);
const data = lines.slice(1).map((line) => line.split(","));
console.log("Data rows:", data.length);
setSelectedFile({ name, path, data });
} catch (err) {
console.error("Failed to fetch file:", err);
} finally {
setIsLoadingFile(false);
}
}
const calculateStats = (data: number[]) => {
const sorted = [...data].sort((a, b) => a - b);
const sum = data.reduce((a, b) => a + b, 0);
return {
avg: sum / data.length,
max: Math.max(...data),
min: Math.min(...data),
median: sorted[Math.floor(sorted.length / 2)],
};
};
const handleFileUpload = useCallback(async (event: React.ChangeEvent) => {
const file = event.target.files?.[0];
if (!file) return;
setIsUploading(true);
setUploadError(null);
try {
const text = await file.text();
const lines = text.trim().split("\n");
if (lines.length < 2) {
throw new Error("File must have at least a header row and one data row");
}
const header = lines[0].toLowerCase();
let parsedData: number[] = [];
if (header.includes("%_generation")) {
const cols = header.split(",");
const colIdx = cols.indexOf("%_generation");
if (colIdx === -1) throw new Error("Could not find %_generation column");
parsedData = lines.slice(1).map((line) => {
const vals = line.split(",");
let val = parseFloat(vals[colIdx]);
if (isNaN(val)) return 0;
if (val > 1) val = val / 100;
return val;
});
} else if (header.includes("irradiance") || header.includes("wind_speed") || header.includes("e_grid")) {
parsedData = lines.slice(1).map((line) => {
const vals = line.split(",");
let val = parseFloat(vals[1]);
if (isNaN(val)) return 0;
if (val > 1) val = val / 1000;
if (val > 1) val = val / 100;
return val;
});
} else {
parsedData = lines.slice(1).map((line) => {
const vals = line.split(",");
let val = parseFloat(vals[0]);
if (isNaN(val)) return 0;
return val;
});
}
if (parsedData.length !== 8760) {
throw new Error(`Expected 8760 values, got ${parsedData.length}`);
}
const isSolar = parsedData.reduce((a, b, i) => {
const hourOfDay = i % 24;
if (hourOfDay >= 6 && hourOfDay <= 18) return a + b;
return a;
}, 0) / parsedData.reduce((a, b) => a + b, 0) > 0.7;
setUploadedProfiles({
solar: isSolar ? parsedData : undefined,
wind: !isSolar ? parsedData : undefined,
filename: file.name,
});
} catch (err) {
setUploadError(err instanceof Error ? err.message : "Failed to parse file");
} finally {
setIsUploading(false);
}
}, []);
const getMonthlyAverages = (data: number[]) => {
const MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const monthNames = ["Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan", "Feb", "Mar"];
const monthPositions = [4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3];
return monthNames.map((name, i) => {
const month = monthPositions[i];
const start = MONTH_DAYS.slice(0, month - 1).reduce((a, d) => a + d, 0) * 24;
const days = MONTH_DAYS[month - 1];
const monthData = data.slice(start, start + days * 24);
const avg = monthData.reduce((a, b) => a + b, 0) / monthData.length;
return { name, avg: avg * 100 };
});
};
return (
<>
{isOpen && (
setIsOpen(false)} />
File Reference Library
{uploadError &&
{uploadError}}
{isLoadingFile ? (
) : selectedFile ? (
{selectedFile.name.includes("Solar") ? "☀️" : "💨"}
{selectedFile.name}
{selectedFile.data.length} rows of data
Download CSV
| Hour |
Date |
Month |
Day |
Hour of Day |
% Generation |
{selectedFile.data.map((row: string[], i: number) => (
| {row[0]} |
{row[1]} |
{row[2]} |
{row[3]} |
{row[4]} |
{row[5]} |
))}
) : (
<>
📂 Click on any file below to view its contents:
{[
{ name: "Solar - Gujarat", path: "/api/profiles/solar/GJ", type: "solar" },
{ name: "Solar - Karnataka", path: "/api/profiles/solar/KA", type: "solar" },
{ name: "Solar - Rajasthan", path: "/api/profiles/solar/RJ", type: "solar" },
{ name: "Wind - Gujarat", path: "/api/profiles/wind/GJ", type: "wind" },
{ name: "Wind - Karnataka", path: "/api/profiles/wind/KA", type: "wind" },
{ name: "Wind - Rajasthan", path: "/api/profiles/wind/RJ", type: "wind" },
].map((profile) => (
))}
{activeTab === "solar" && (
Monthly Average Solar Irradiance
} />
{uploadedProfiles.solar && (
Your Uploaded Solar Profile
{(() => {
const stats = calculateStats(uploadedProfiles.solar);
return (
<>
>
);
})()}
)}
)}
{activeTab === "wind" && (
Monthly Average Wind Speed (m/s)
{uploadedProfiles.wind && (
Your Uploaded Wind Profile
{(() => {
const stats = calculateStats(uploadedProfiles.wind);
return (
<>
>
);
})()}
)}
)}
{activeTab === "compare" && (
Solar vs Wind CUF (Typical Daily Pattern)
`${(v * 100).toFixed(0)}%`} />
} />
)}
>
)}
)}
>
);
}