feat: web frontend; middleware; serde (WIP?)

This commit is contained in:
2025-11-30 09:41:37 +08:00
parent be35040e26
commit 531ac029af
45 changed files with 6806 additions and 82 deletions

View File

@@ -0,0 +1,83 @@
import { Component, createResource, For, Show } from 'solid-js';
import { commandsApi } from '../api/client';
const CommandQueue: Component = () => {
const [commands, { refetch }] = createResource(commandsApi.list);
const updateCommandStatus = async (id: number, status: string) => {
try {
await commandsApi.updateStatus(id, { status });
refetch();
} catch (err) {
console.error('Failed to update command:', err);
alert(`Error: ${err}`);
}
};
const formatDate = (dateStr: string) => {
return new Date(dateStr).toLocaleString();
};
return (
<div class="space-y-4">
<h2 class="text-2xl font-semibold">Command Queue</h2>
<div class="bg-white shadow rounded-lg overflow-hidden">
<Show when={!commands.loading} fallback={<div class="p-4">Loading...</div>}>
<For each={commands()} fallback={
<div class="p-8 text-center text-gray-500">
No commands in queue
</div>
}>
{(cmd) => (
<div class="border-b p-4 hover:bg-gray-50">
<div class="flex justify-between items-start">
<div class="flex-1">
<div class="flex items-center gap-2 mb-2">
<span class={`px-2 py-1 rounded-full text-xs font-semibold ${
cmd.status === 'verified' ? 'bg-green-100 text-green-800' :
cmd.status === 'rejected' ? 'bg-red-100 text-red-800' :
'bg-yellow-100 text-yellow-800'
}`}>
{cmd.status}
</span>
<span class="text-sm text-gray-500">
{formatDate(cmd.received_at)}
</span>
</div>
<pre class="text-sm bg-gray-100 p-3 rounded overflow-x-auto">
{JSON.stringify(cmd.command, null, 2)}
</pre>
<Show when={cmd.notes}>
<div class="mt-2 text-sm text-gray-600">
<strong>Notes:</strong> {cmd.notes}
</div>
</Show>
</div>
<Show when={cmd.status === 'unverified'}>
<div class="ml-4 flex flex-col gap-2">
<button
onClick={() => updateCommandStatus(cmd.id, 'verified')}
class="px-3 py-1 bg-green-600 text-white rounded text-sm hover:bg-green-700"
>
Verify
</button>
<button
onClick={() => updateCommandStatus(cmd.id, 'rejected')}
class="px-3 py-1 bg-red-600 text-white rounded text-sm hover:bg-red-700"
>
Reject
</button>
</div>
</Show>
</div>
</div>
)}
</For>
</Show>
</div>
</div>
);
};
export default CommandQueue;