95 lines
3.4 KiB
TypeScript
95 lines
3.4 KiB
TypeScript
// In file: src/app/page.tsx some basic changes in order to test gitea
|
|
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import dynamic from "next/dynamic";
|
|
import { getCommands, getLatestGPS, DeviceCommand, TelemetryPost, DeviceCommandType, postReceipt } from "~/lib/api"; // Assuming postCommand will be added later
|
|
|
|
import { CommandManager } from "~/app/_components/CommandManager";
|
|
|
|
|
|
// Dynamically import the map component with SSR turned OFF to prevent "window is not defined" errors
|
|
const DeviceMap = dynamic(
|
|
() => import("~/app/_components/DynamicDeviceMap").then((mod) => mod.DeviceMap),
|
|
{
|
|
ssr: false,
|
|
loading: () => <p>Loading map...</p>,
|
|
}
|
|
);
|
|
|
|
const DEFAULT_IMEI = "sim7080g-01"; // Or make this dynamic later
|
|
|
|
export default function Dashboard() {
|
|
const [selectedImei, setSelectedImei] = useState<string>(DEFAULT_IMEI);
|
|
const [telemetry, setTelemetry] = useState<TelemetryPost | null>(null);
|
|
const [commands, setCommands] = useState<DeviceCommand[]>([]);
|
|
const [fence, setFence] = useState<Array<{ lat: number; lng: number }>>([]);
|
|
const [commandType, setCommandType] = useState<DeviceCommandType>("sleep");
|
|
const [payload, setPayload] = useState<string>("");
|
|
|
|
useEffect(() => {
|
|
const pollData = async () => {
|
|
try {
|
|
const latest = await getLatestGPS(selectedImei);
|
|
setTelemetry(latest);
|
|
const cmds = await getCommands(selectedImei);
|
|
setCommands(cmds);
|
|
} catch (e) {
|
|
console.error("Poll error:", e);
|
|
}
|
|
};
|
|
|
|
pollData(); // Initial fetch
|
|
const interval = setInterval(pollData, 5000);
|
|
return () => clearInterval(interval);
|
|
}, [selectedImei]);
|
|
|
|
return (
|
|
<main className="flex min-h-screen flex-col gap-4 bg-gray-900 p-4 text-white">
|
|
<h1 className="text-2xl font-bold">Device Dashboard ({selectedImei})</h1>
|
|
<section className="grid grid-cols-1 gap-4 mt-4 md:grid-cols-2">
|
|
<div>
|
|
<h2 className="text-xl font-bold">Latest Telemetry</h2>
|
|
{telemetry ? (
|
|
<pre className="p-2 mt-2 text-sm text-white rounded-md bg-white/10">{JSON.stringify(telemetry, null, 2)}</pre>
|
|
) : (
|
|
<p>Loading telemetry...</p>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<h2 className="text-xl font-bold">Map & Geo-Fence</h2>
|
|
<div className="mt-2 h-[300px] rounded-md overflow-hidden bg-gray-800 flex items-center justify-center">
|
|
{telemetry ? (
|
|
<DeviceMap latestGPS={telemetry} onFenceChange={setFence} />
|
|
) : (
|
|
<p className="text-gray-400">Waiting for GPS data...</p>
|
|
)}
|
|
</div>
|
|
<p>Geo-fence points: {fence.length}</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="mt-6">
|
|
<h2 className="text-xl font-bold">Queued Commands</h2>
|
|
<pre className="p-2 mt-2 text-sm text-white rounded-md bg-white/10">{commands.length > 0 ? JSON.stringify(commands, null, 2) : "No commands queued."}</pre>
|
|
{commands.length > 0 && (
|
|
<button
|
|
className="px-4 py-2 mt-2 text-white bg-green-600 rounded-md hover:bg-green-700"
|
|
onClick={() => postReceipt(selectedImei, { command_id: commands[0]?.id ?? 0, result: "ok" })}
|
|
>
|
|
Acknowledge First Command
|
|
</button>
|
|
)}
|
|
</section>
|
|
|
|
<section className="mt-6">
|
|
<h2 className="text-xl font-bold">Command Center</h2>
|
|
<div className="mt-2">
|
|
<CommandManager selectedImei={selectedImei} /> {/* <-- ADD THE NEW COMPONENT HERE */}
|
|
</div>
|
|
</section>
|
|
</main>
|
|
);
|
|
|
|
}
|