feat: task detail panel with progress notes

This commit is contained in:
2026-01-28 23:46:07 +00:00
parent 9a99b612ba
commit aea54516c9
2 changed files with 41 additions and 1 deletions

View File

@@ -1,11 +1,12 @@
import { useState, useMemo } from "react";
import { useTasks } from "./hooks/useTasks";
import { TaskCard } from "./components/TaskCard";
import { TaskDetailPanel } from "./components/TaskDetailPanel";
import { CreateTaskModal } from "./components/CreateTaskModal";
import { LoginPage } from "./components/LoginPage";
import { useSession, signOut } from "./lib/auth-client";
import { updateTask, reorderTasks, createTask } from "./lib/api";
import type { TaskStatus } from "./lib/types";
import type { Task, TaskStatus } from "./lib/types";
// Token stored in localStorage for bearer-token admin operations
function getToken(): string {
@@ -16,6 +17,7 @@ function Dashboard() {
const { tasks, loading, error, refresh } = useTasks(5000);
const [showCreate, setShowCreate] = useState(false);
const [showCompleted, setShowCompleted] = useState(false);
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
const [tokenInput, setTokenInput] = useState("");
const [showTokenInput, setShowTokenInput] = useState(false);
const session = useSession();
@@ -23,6 +25,12 @@ function Dashboard() {
const token = getToken();
const hasToken = !!token;
// Keep selected task in sync with refreshed data
const selectedTaskData = useMemo(() => {
if (!selectedTask) return null;
return tasks.find((t) => t.id === selectedTask.id) || null;
}, [tasks, selectedTask]);
const activeTasks = useMemo(() => tasks.filter((t) => t.status === "active"), [tasks]);
const queuedTasks = useMemo(() => tasks.filter((t) => t.status === "queued"), [tasks]);
const blockedTasks = useMemo(() => tasks.filter((t) => t.status === "blocked"), [tasks]);
@@ -195,6 +203,7 @@ function Dashboard() {
task={task}
onStatusChange={handleStatusChange}
isActive
onClick={() => setSelectedTask(task)}
/>
))}
</div>
@@ -213,6 +222,7 @@ function Dashboard() {
key={task.id}
task={task}
onStatusChange={handleStatusChange}
onClick={() => setSelectedTask(task)}
/>
))}
</div>
@@ -239,6 +249,7 @@ function Dashboard() {
onMoveDown={() => handleMoveDown(i)}
isFirst={i === 0}
isLast={i === queuedTasks.length - 1}
onClick={() => setSelectedTask(task)}
/>
))}
</div>
@@ -260,6 +271,7 @@ function Dashboard() {
key={task.id}
task={task}
onStatusChange={handleStatusChange}
onClick={() => setSelectedTask(task)}
/>
))}
</div>
@@ -267,6 +279,19 @@ function Dashboard() {
</section>
</main>
{/* Task Detail Panel */}
{selectedTaskData && (
<TaskDetailPanel
task={selectedTaskData}
onClose={() => setSelectedTask(null)}
onStatusChange={(id, status) => {
handleStatusChange(id, status);
setSelectedTask(null);
}}
hasToken={hasToken}
/>
)}
{/* Footer */}
<footer className="text-center text-xs text-gray-300 py-4">
Hammer Queue v0.1 · Auto-refreshes every 5s

View File

@@ -1 +1,16 @@
@import "tailwindcss";
@keyframes slide-in-right {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.animate-slide-in-right {
animation: slide-in-right 0.25s ease-out;
}