prisma migration and fix dropdowns - working

This commit is contained in:
2025-09-11 17:13:25 +10:00
parent 168cc87447
commit d80284ff15
2 changed files with 83 additions and 73 deletions

Binary file not shown.

View File

@ -1,9 +1,21 @@
// In file: src/app/_components/CommandManager.tsx
"use client";
import { useState } from "react";
import { useState, useEffect } from "react";
import { api } from "~/trpc/react";
import { postCommand, type DeviceCommandType } from "~/lib/api"; // Your Laravel API helper
import { postCommand, type DeviceCommandType } from "~/lib/api";
const predefinedPayloads: Record<DeviceCommandType, object> = {
lights: { on: true },
camera: { on: true },
sleep: { interval_sec: 300 },
telemetry_sec: { interval: 60 },
poll_sec: { interval: 120 },
wifi: { target: "home", ssid: "YourSSID", password: "YourPassword" },
ring_fence: { enabled: true, points: [{ lat: 0, lng: 0 }] },
reboot: {},
ota: { url: "http://example.com/firmware.bin" },
};
interface CommandManagerProps {
selectedImei: string;
@ -11,23 +23,28 @@ interface CommandManagerProps {
export function CommandManager({ selectedImei }: CommandManagerProps) {
const [name, setName] = useState("");
const [type, setType] = useState<DeviceCommandType>("reboot");
const [payload, setPayload] = useState(""); // Stored as a string for the textarea
const [type, setType] = useState<DeviceCommandType>("lights");
const [payload, setPayload] = useState("");
// This useEffect hook updates the payload whenever the command type changes
useEffect(() => {
const newPayload = predefinedPayloads[type];
setPayload(JSON.stringify(newPayload, null, 2)); // Pretty-print the JSON
}, [type]); // Dependency array: this runs only when 'type' changes
const utils = api.useUtils();
const templatesQuery = api.commandTemplate.getAll.useQuery();
const createTemplateMutation = api.commandTemplate.create.useMutation({
onSuccess: () => {
utils.commandTemplate.getAll.invalidate(); // Invalidate cache to refetch list
utils.commandTemplate.getAll.invalidate();
setName("");
setPayload("");
},
});
const deleteTemplateMutation = api.commandTemplate.delete.useMutation({
onSuccess: () => {
utils.commandTemplate.getAll.invalidate(); // Refetch list after deleting
utils.commandTemplate.getAll.invalidate();
},
});
@ -40,23 +57,23 @@ export function CommandManager({ selectedImei }: CommandManagerProps) {
}
};
const handleSendCommand = async (template: { type: string; payload: any }) => {
try {
await postCommand(selectedImei, {
type: template.type as DeviceCommandType,
payload: template.payload,
});
alert(`Command "${template.type}" sent to ${selectedImei}!`);
} catch (e) {
alert(`Failed to send command: ${e instanceof Error ? e.message : "Unknown error"}`);
}
const handleSendCommand = async (template: { name: string, type: string; payload: any }) => {
try {
await postCommand(selectedImei, {
type: template.type as DeviceCommandType,
payload: template.payload,
});
alert(`Command "${template.name}" sent to ${selectedImei}!`);
} catch (e) {
alert(`Failed to send command: ${e instanceof Error ? e.message : "Unknown error"}`);
}
};
return (
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
{/* Form to add new templates */}
<div className="flex flex-col gap-2 p-4 rounded-md bg-white/10">
<h3 className="text-lg font-bold">Create New Command Template</h3>
<h3 className="text-lg font-bold">Create/Edit Command</h3>
<input
type="text"
placeholder="Template Name (e.g., Lights On)"
@ -64,74 +81,67 @@ export function CommandManager({ selectedImei }: CommandManagerProps) {
onChange={(e) => setName(e.target.value)}
className="p-2 text-white rounded-md bg-gray-800 border border-gray-600"
/>
<select
value={type} // This value is tied to the 'type' state
onChange={(e) => setType(e.target.value as DeviceCommandType)} // This updates the state when you select an option
className="p-2 text-white rounded-md bg-gray-800 border border-gray-600"
>
<option value="reboot" className="bg-gray-800 text-white">Reboot Device</option>
<option value="lights" className="bg-gray-800 text-white">Lights On/Off</option>
<option value="camera" className="bg-gray-800 text-white">Camera On/Off</option>
<option value="sleep" className="bg-gray-800 text-white">Set Sleep Interval</option>
<option value="telemetry_sec" className="bg-gray-800 text-white">Set Telemetry Interval</option>
<option value="poll_sec" className="bg-gray-800 text-white">Set Poll Interval</option>
<option value="wifi" className="bg-gray-800 text-white">Set WiFi Credentials</option>
<option value="ring_fence" className="bg-gray-800 text-white">Set Geo-Fence</option>
<option value="ota" className="bg-gray-800 text-white">Firmware OTA</option>
</select>
<textarea
placeholder='JSON Payload, e.g., {"on": true}'
<select
value={type}
onChange={(e) => setType(e.target.value as DeviceCommandType)}
className="p-2 text-white rounded-md bg-gray-800 border border-gray-600"
>
<option value="lights" className="bg-gray-800 text-white">Lights On/Off</option>
<option value="camera" className="bg-gray-800 text-white">Camera On/Off</option>
<option value="sleep" className="bg-gray-800 text-white">Set Sleep Interval</option>
<option value="telemetry_sec" className="bg-gray-800 text-white">Set Telemetry Interval</option>
<option value="poll_sec" className="bg-gray-800 text-white">Set Poll Interval</option>
<option value="wifi" className="bg-gray-800 text-white">Set WiFi Credentials</option>
<option value="ring_fence" className="bg-gray-800 text-white">Set Geo-Fence</option>
<option value="reboot" className="bg-gray-800 text-white">Reboot Device</option>
<option value="ota" className="bg-gray-800 text-white">Firmware OTA</option>
</select>
<textarea
placeholder="JSON Payload"
value={payload}
onChange={(e) => setPayload(e.target.value)}
className="p-2 text-white rounded-md bg-white/10"
rows={3}
className="p-2 text-white rounded-md bg-gray-800 border border-gray-600 font-mono"
rows={5}
/>
<button
onClick={handleCreateTemplate}
className="px-4 py-2 text-white bg-blue-600 rounded-md hover:bg-blue-700"
disabled={createTemplateMutation.isPending}
>
{createTemplateMutation.isPending ? "Saving..." : "Save Template"}
{createTemplateMutation.isPending ? "Saving..." : "Save as Template"}
</button>
</div>
{/* List of saved templates */}
// In file: src/app/_components/CommandManager.tsx
{/* List of saved templates */}
<div className="flex flex-col gap-2 p-4 rounded-md bg-white/10">
<h3 className="text-lg font-bold">Saved Commands</h3>
{templatesQuery.isLoading && <p>Loading templates...</p>}
{/* ADD THIS CHECK for an empty list */}
{templatesQuery.data && templatesQuery.data.length === 0 && (
<p className="text-gray-400">No templates saved. Create one on the left!</p>
)}
{templatesQuery.data?.map((template) => (
<div key={template.id} className="flex items-center justify-between p-2 rounded-md bg-black/20">
<div>
<p className="font-semibold">{template.name}</p>
<p className="text-sm text-gray-400">Type: {template.type}</p>
<div className="flex flex-col gap-2 p-4 rounded-md bg-white/10">
<h3 className="text-lg font-bold">Saved Templates</h3>
{templatesQuery.isLoading && <p>Loading templates...</p>}
{templatesQuery.data && templatesQuery.data.length === 0 && (
<p className="text-gray-400">No templates saved. Create one on the left!</p>
)}
{templatesQuery.data?.map((template) => (
<div key={template.id} className="flex items-center justify-between p-2 rounded-md bg-black/20">
<div>
<p className="font-semibold">{template.name}</p>
<p className="text-sm text-gray-400">Type: {template.type}</p>
</div>
<div className="flex gap-2">
<button
onClick={() => handleSendCommand(template)}
className="px-3 py-1 text-white bg-green-600 rounded-md hover:bg-green-700"
>
Send
</button>
<button
onClick={() => deleteTemplateMutation.mutate({ id: template.id })}
className="px-3 py-1 text-white bg-red-600 rounded-md hover:bg-red-700"
>
Delete
</button>
</div>
</div>
))}
</div>
<div className="flex gap-2">
<button
onClick={() => handleSendCommand(template)}
className="px-3 py-1 text-white bg-green-600 rounded-md hover:bg-green-700"
>
Send
</button>
<button
onClick={() => deleteTemplateMutation.mutate({ id: template.id })}
className="px-3 py-1 text-white bg-red-600 rounded-md hover:bg-red-700"
>
Delete
</button>
</div>
</div>
))}
</div>
</div>
);
}