"use client";
import { useEffect, useState, useCallback } from "react";
import { toast } from "sonner";
import { fetchReportSummary, fetchKpis, fetchAlarmStats, fetchEnergyReport, reportExportUrl, type ReportSummary, type KpiData, type AlarmStats, type EnergyReport } from "@/lib/api";
import { PageShell } from "@/components/layout/page-shell";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import {
Zap, Thermometer, Bell, AlertTriangle, CheckCircle2, Clock,
Download, Wind, Battery, RefreshCw, Activity, DollarSign,
} from "lucide-react";
import {
AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer,
} from "recharts";
import { cn } from "@/lib/utils";
const SITE_ID = "sg-01";
function UptimeBar({ pct }: { pct: number }) {
const color = pct >= 99 ? "bg-green-500" : pct >= 95 ? "bg-amber-500" : "bg-destructive";
return (
= 99 ? "text-green-400" : pct >= 95 ? "text-amber-400" : "text-destructive"
)}>
{pct.toFixed(1)}%
);
}
function ExportCard({ hours, setHours }: { hours: number; setHours: (h: number) => void }) {
const exports: { label: string; type: "power" | "temperature" | "alarms"; icon: React.ElementType }[] = [
{ label: "Power History", type: "power", icon: Zap },
{ label: "Temperature History", type: "temperature", icon: Thermometer },
{ label: "Alarm Log", type: "alarms", icon: Bell },
];
return (
Export Data
{([24, 48, 168] as const).map((h) => (
))}
{exports.map(({ label, type, icon: Icon }) => (
{label}
{type !== "alarms" && (
(last {hours === 168 ? "7 days" : `${hours}h`})
)}
))}
CSV format — 5-minute bucketed averages
);
}
const RANGE_HOURS: Record<"24h" | "7d" | "30d", number> = { "24h": 24, "7d": 168, "30d": 720 };
const RANGE_DAYS: Record<"24h" | "7d" | "30d", number> = { "24h": 1, "7d": 7, "30d": 30 };
export default function ReportsPage() {
const [summary, setSummary] = useState(null);
const [kpis, setKpis] = useState(null);
const [alarmStats, setAlarmStats] = useState(null);
const [energy, setEnergy] = useState(null);
const [loading, setLoading] = useState(true);
const [exportHours, setExportHours] = useState(720);
const [dateRange, setDateRange] = useState<"24h" | "7d" | "30d">("30d");
const load = useCallback(async () => {
try {
const [s, k, a, e] = await Promise.all([
fetchReportSummary(SITE_ID),
fetchKpis(SITE_ID),
fetchAlarmStats(SITE_ID),
fetchEnergyReport(SITE_ID, RANGE_DAYS[dateRange]).catch(() => null),
]);
setSummary(s);
setKpis(k);
setAlarmStats(a);
setEnergy(e);
} catch { toast.error("Failed to load report data"); }
finally { setLoading(false); }
}, [dateRange]);
useEffect(() => { load(); }, [load]);
function handleRangeChange(r: "24h" | "7d" | "30d") {
setDateRange(r);
setExportHours(RANGE_HOURS[r]);
}
return (
Reports
Singapore DC01 — site summary & data exports
{/* Date range picker */}
{(["24h", "7d", "30d"] as const).map((r) => (
))}
{/* Export always visible at top */}
{loading ? (
{Array.from({ length: 4 }).map((_, i) => )}
) : !summary ? (
Unable to load report data.
) : (
<>
Generated {new Date(summary.generated_at).toLocaleString()} · Showing last {dateRange}
{/* KPI snapshot — expanded */}
Site KPI Snapshot
Total Power
{summary.kpis.total_power_kw} kW
PUE
1.5 ? "text-amber-400" : ""
)}>
{kpis?.pue.toFixed(2) ?? "—"}
target <1.4
Avg Temp
= 28 ? "text-destructive" :
summary.kpis.avg_temperature >= 25 ? "text-amber-400" : ""
)}>
{summary.kpis.avg_temperature}°C
Active Alarms
0 ? "text-destructive" : "")}>
{alarmStats?.active ?? "—"}
Critical
0 ? "text-destructive" : "")}>
{alarmStats?.critical ?? "—"}
Resolved
{alarmStats?.resolved ?? "—"}
{/* Alarm breakdown */}
Alarm Breakdown
{[
{ label: "Active", value: summary.alarm_stats.active, icon: AlertTriangle, color: "text-destructive" },
{ label: "Acknowledged", value: summary.alarm_stats.acknowledged, icon: Clock, color: "text-amber-400" },
{ label: "Resolved", value: summary.alarm_stats.resolved, icon: CheckCircle2, color: "text-green-400" },
].map(({ label, value, icon: Icon, color }) => (
0 && label === "Active" ? "text-destructive" : "")}>{value}
{label}
))}
By severity:
{summary.alarm_stats.critical} critical
{summary.alarm_stats.warning} warning
{/* CRAC uptime */}
CRAC Uptime (last 24h)
{summary.crac_uptime.map((crac) => (
{crac.crac_id.toUpperCase()}
{crac.room_id}
))}
{summary.crac_uptime.length === 0 && (
No CRAC data available
)}
{/* UPS uptime */}
UPS Uptime (last 24h)
{summary.ups_uptime.map((ups) => (
{ups.ups_id.toUpperCase()}
))}
{summary.ups_uptime.length === 0 && (
No UPS data available
)}
{/* Energy cost section */}
{energy && (
Energy Cost — Last {energy.period_days} Days
Total kWh
{energy.kwh_total.toFixed(0)}
{energy.from_date} → {energy.to_date}
Cost ({energy.currency})
${energy.cost_sgd.toLocaleString()}
@ ${energy.tariff_sgd_kwh}/kWh
Est. Annual kWh
{(energy.pue_trend.length > 0 ? energy.kwh_total / energy.period_days * 365 : 0).toFixed(0)}
at current pace
PUE (estimated)
1.5 ? "text-amber-400" : "")}>{energy.pue_estimated.toFixed(2)}
target < 1.4
{(() => {
const trend = energy.pue_trend ?? [];
const thisWeek = trend.slice(-7);
const lastWeek = trend.slice(-14, -7);
const thisKwh = thisWeek.reduce((s, d) => s + d.avg_it_kw * 24, 0);
const lastKwh = lastWeek.reduce((s, d) => s + d.avg_it_kw * 24, 0);
const kwhDelta = lastKwh > 0 ? ((thisKwh - lastKwh) / lastKwh * 100) : 0;
const thisAvgKw = thisWeek.length > 0 ? thisWeek.reduce((s, d) => s + d.avg_it_kw, 0) / thisWeek.length : 0;
const lastAvgKw = lastWeek.length > 0 ? lastWeek.reduce((s, d) => s + d.avg_it_kw, 0) / lastWeek.length : 0;
const kwDelta = lastAvgKw > 0 ? ((thisAvgKw - lastAvgKw) / lastAvgKw * 100) : 0;
if (thisWeek.length === 0 || lastWeek.length === 0) return null;
return (
This week vs last week:
kWh:
{thisKwh.toFixed(0)}
5 ? "text-destructive" : kwhDelta < -5 ? "text-green-400" : "text-muted-foreground"
)}>
({kwhDelta > 0 ? "+" : ""}{kwhDelta.toFixed(1)}%)
Avg IT load:
{thisAvgKw.toFixed(1)} kW
5 ? "text-amber-400" : kwDelta < -5 ? "text-green-400" : "text-muted-foreground"
)}>
({kwDelta > 0 ? "+" : ""}{kwDelta.toFixed(1)}%)
based on last {thisWeek.length + lastWeek.length} days of data
);
})()}
{energy.pue_trend.length > 0 && (
<>
Daily IT Load (kW)
new Date(v).toLocaleDateString([], { month: "short", day: "numeric" })} />
[`${Number(v).toFixed(1)} kW`, "Avg IT Load"]}
labelFormatter={(l) => new Date(l).toLocaleDateString()}
/>
>
)}
)}
>
)}
);
}