feat: task detail panel with progress notes
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user