refactor: S06.1 reorganize frontend routes into subdirectories (media/tools/admin)

This commit is contained in:
Gan, Jimmy
2026-07-10 23:06:10 +08:00
parent 30a8196c12
commit eece551ea9
11 changed files with 26 additions and 26 deletions
@@ -0,0 +1,208 @@
<script>
import { onMount, onDestroy } from "svelte";
import KanbanBoard from "../../components/opc/KanbanBoard.svelte";
import TaskModal from "../../components/opc/TaskModal.svelte";
import AgentPanel from "../../components/opc/AgentPanel.svelte";
import { getTasks, getAgents } from "../../lib/opc-api.js";
import * as opcWs from "../../lib/opc-ws.js";
let tasks = $state([]);
let agents = $state([]);
let loading = $state(true);
let showTaskModal = $state(false);
let editingTask = $state(null);
let showAgentPanel = $state(
typeof localStorage !== 'undefined'
? localStorage.getItem('opc_showAgentPanel') !== 'false'
: true
);
let unsubscribe = null;
// Persist agent panel visibility
$effect(() => {
if (typeof localStorage !== 'undefined') {
localStorage.setItem('opc_showAgentPanel', showAgentPanel);
}
});
async function loadData() {
loading = true;
try {
const [tasksRes, agentsRes] = await Promise.all([
getTasks(),
getAgents()
]);
tasks = tasksRes.items || [];
agents = agentsRes.items || [];
} catch (e) {
console.error("Failed to load data:", e);
} finally {
loading = false;
}
}
function handleWebSocketMessage(message) {
if (message.type === "task_update") {
const { task_id, action, data } = message;
if (action === "created") {
tasks = [...tasks, data];
} else if (action === "updated" || action === "moved") {
tasks = tasks.map(t => t.id === task_id ? data : t);
} else if (action === "deleted") {
tasks = tasks.filter(t => t.id !== task_id);
}
} else if (message.type === "agent_execution") {
// Could show agent status in UI
console.log("Agent execution update:", message);
}
}
function handleCreateTask() {
editingTask = null;
showTaskModal = true;
}
function handleEditTask(task) {
editingTask = task;
showTaskModal = true;
}
function handleDeleteTask(taskId) {
tasks = tasks.filter(t => t.id !== taskId);
}
function handleAssignTask(taskId, assignedTo, assignedType) {
const task = tasks.find(t => t.id === taskId);
if (task) {
task.assigned_to = assignedTo;
task.assigned_type = assignedType;
tasks = [...tasks];
}
}
function handleTaskMoved(taskId, newStatus) {
const task = tasks.find(t => t.id === taskId);
if (task) {
task.status = newStatus;
tasks = [...tasks];
}
}
function handleModalClose() {
showTaskModal = false;
editingTask = null;
}
function handleModalSave() {
loadData();
}
onMount(() => {
loadData();
// Connect WebSocket
opcWs.connect();
unsubscribe = opcWs.subscribe(handleWebSocketMessage);
});
onDestroy(() => {
if (unsubscribe) {
unsubscribe();
}
opcWs.disconnect();
});
</script>
<div class="space-y-6 h-full flex flex-col">
<!-- Header -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-surface-900 dark:text-surface-100">
OPC Management
</h1>
<p class="text-surface-600 dark:text-surface-400 mt-1">
Manage your tasks with AI agents
</p>
</div>
<div class="flex gap-2">
<button
onclick={() => showAgentPanel = !showAgentPanel}
class="px-4 py-2 bg-surface-200 dark:bg-surface-700 text-surface-900 dark:text-surface-100 rounded-lg hover:bg-surface-300 dark:hover:bg-surface-600 flex items-center gap-2"
>
<span>🤖</span>
<span>{showAgentPanel ? "Hide" : "Show"} Agents</span>
</button>
<button
onclick={handleCreateTask}
class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 flex items-center gap-2"
>
<span></span>
<span>New Task</span>
</button>
</div>
</div>
<!-- Stats -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div class="bg-white dark:bg-surface-800 rounded-lg p-4 border border-surface-200 dark:border-surface-700">
<div class="text-sm text-surface-600 dark:text-surface-400">Total Tasks</div>
<div class="text-2xl font-bold text-surface-900 dark:text-surface-100 mt-1">
{tasks.length}
</div>
</div>
<div class="bg-white dark:bg-surface-800 rounded-lg p-4 border border-surface-200 dark:border-surface-700">
<div class="text-sm text-surface-600 dark:text-surface-400">Parking Lot</div>
<div class="text-2xl font-bold text-slate-600 dark:text-slate-400 mt-1">
{tasks.filter(t => t.status === "parking_lot").length}
</div>
</div>
<div class="bg-white dark:bg-surface-800 rounded-lg p-4 border border-surface-200 dark:border-surface-700">
<div class="text-sm text-surface-600 dark:text-surface-400">In Progress</div>
<div class="text-2xl font-bold text-blue-600 dark:text-blue-400 mt-1">
{tasks.filter(t => t.status === "in_progress").length}
</div>
</div>
<div class="bg-white dark:bg-surface-800 rounded-lg p-4 border border-surface-200 dark:border-surface-700">
<div class="text-sm text-surface-600 dark:text-surface-400">Done</div>
<div class="text-2xl font-bold text-emerald-600 dark:text-emerald-400 mt-1">
{tasks.filter(t => t.status === "done").length}
</div>
</div>
</div>
<!-- Kanban Board -->
{#if loading}
<div class="flex-1 flex items-center justify-center">
<div class="text-surface-600 dark:text-surface-400">Loading...</div>
</div>
{:else}
<div class="flex-1 overflow-hidden">
<KanbanBoard
{tasks}
{agents}
onEdit={handleEditTask}
onDelete={handleDeleteTask}
onAssign={handleAssignTask}
onTaskMoved={handleTaskMoved}
/>
</div>
{/if}
<!-- Agent Panel -->
{#if showAgentPanel}
<div class="mt-6">
<AgentPanel />
</div>
{/if}
</div>
<!-- Task Modal -->
{#if showTaskModal}
<TaskModal
task={editingTask}
{agents}
onClose={handleModalClose}
onSave={handleModalSave}
/>
{/if}