refactor: S06.1 reorganize frontend routes into subdirectories (media/tools/admin)
This commit is contained in:
@@ -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}
|
||||
Reference in New Issue
Block a user