Initial scaffold: Hammer Queue task dashboard
- Backend: Elysia + Bun + Drizzle ORM + PostgreSQL - Frontend: React + Vite + TypeScript + Tailwind CSS - Task CRUD API with bearer token auth for writes - Public read-only dashboard with auto-refresh - Task states: active, queued, blocked, completed, cancelled - Reorder support for queue management - Progress notes per task - Docker Compose for local dev and Dokploy deployment
This commit is contained in:
62
frontend/src/lib/api.ts
Normal file
62
frontend/src/lib/api.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { Task } from "./types";
|
||||
|
||||
const BASE = "/api/tasks";
|
||||
|
||||
export async function fetchTasks(): Promise<Task[]> {
|
||||
const res = await fetch(BASE);
|
||||
if (!res.ok) throw new Error("Failed to fetch tasks");
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function updateTask(
|
||||
id: string,
|
||||
updates: Record<string, any>,
|
||||
token: string
|
||||
): Promise<Task> {
|
||||
const res = await fetch(`${BASE}/${id}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(updates),
|
||||
});
|
||||
if (!res.ok) throw new Error("Failed to update task");
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function reorderTasks(ids: string[], token: string): Promise<void> {
|
||||
const res = await fetch(`${BASE}/reorder`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({ ids }),
|
||||
});
|
||||
if (!res.ok) throw new Error("Failed to reorder tasks");
|
||||
}
|
||||
|
||||
export async function createTask(
|
||||
task: { title: string; description?: string; source?: string; priority?: string; status?: string },
|
||||
token: string
|
||||
): Promise<Task> {
|
||||
const res = await fetch(BASE, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(task),
|
||||
});
|
||||
if (!res.ok) throw new Error("Failed to create task");
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function deleteTask(id: string, token: string): Promise<void> {
|
||||
const res = await fetch(`${BASE}/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) throw new Error("Failed to delete task");
|
||||
}
|
||||
22
frontend/src/lib/types.ts
Normal file
22
frontend/src/lib/types.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export type TaskStatus = "active" | "queued" | "blocked" | "completed" | "cancelled";
|
||||
export type TaskPriority = "critical" | "high" | "medium" | "low";
|
||||
export type TaskSource = "donovan" | "david" | "hammer" | "heartbeat" | "cron" | "other";
|
||||
|
||||
export interface ProgressNote {
|
||||
timestamp: string;
|
||||
note: string;
|
||||
}
|
||||
|
||||
export interface Task {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
source: TaskSource;
|
||||
status: TaskStatus;
|
||||
priority: TaskPriority;
|
||||
position: number;
|
||||
progressNotes: ProgressNote[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
completedAt: string | null;
|
||||
}
|
||||
Reference in New Issue
Block a user