Files
factorio-signal-exporter/web/app/page.tsx

95 lines
2.8 KiB
TypeScript

'use client';
import { useSearchParams } from 'next/navigation';
import { useEffect, useState, Suspense } from 'react';
import type { LocaleMap } from '@/lib/localization';
import type { AlertConfig } from '@/lib/types';
import AlertPanel from '@/components/AlertPanel';
import Dashboard from '@/components/Dashboard';
import TimeRangeSelector from '@/components/TimeRangeSelector';
import { setToken, fetchAlerts } from '@/lib/api';
import { AppProvider, useApp } from '@/lib/context';
import { getLocaleMap } from '@/lib/localization';
function AppShell({ alerts }: { alerts: AlertConfig[] }) {
const { triggeredAlerts } = useApp();
const [alertPanelOpen, setAlertPanelOpen] = useState(false);
return (
<div className="min-h-screen bg-gray-950 text-gray-100">
<header className="flex items-center justify-between px-4 py-2 border-b border-gray-800 bg-gray-900 sticky top-0 z-20">
<span className="font-bold text-indigo-400 tracking-wide">Factorio Dashboard</span>
<div className="flex items-center gap-4">
<TimeRangeSelector />
<button
onClick={() => setAlertPanelOpen(true)}
className="relative text-sm text-gray-300 hover:text-white px-3 py-1 rounded hover:bg-gray-800"
>
🔔 Alerts
{triggeredAlerts.length > 0 && (
<span className="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-4 h-4 flex items-center justify-center">
{triggeredAlerts.length}
</span>
)}
</button>
</div>
</header>
<main className="p-4">
<Dashboard alerts={alerts} />
</main>
<AlertPanel open={alertPanelOpen} onClose={() => setAlertPanelOpen(false)} />
</div>
);
}
function DashboardApp() {
const searchParams = useSearchParams();
const token = searchParams.get('token') ?? '';
const [ready, setReady] = useState(false);
const [alerts, setAlerts] = useState<AlertConfig[]>([]);
const [localeMap, setLocaleMap] = useState<LocaleMap>(new Map());
useEffect(() => {
if (!token) return;
setToken(token);
Promise.all([getLocaleMap(), fetchAlerts()]).then(([lm, al]) => {
setLocaleMap(lm);
setAlerts(al);
setReady(true);
});
}, [token]);
if (!token) {
return (
<div className="flex min-h-screen items-center justify-center text-gray-400">
Missing <code className="mx-1 bg-gray-800 px-1 rounded">?token=</code> in URL
</div>
);
}
if (!ready) {
return (
<div className="flex min-h-screen items-center justify-center text-gray-400">Loading</div>
);
}
return (
<AppProvider token={token} localeMap={localeMap}>
<AppShell alerts={alerts} />
</AppProvider>
);
}
export default function Page() {
return (
<Suspense>
<DashboardApp />
</Suspense>
);
}