Add project and todo management
This commit is contained in:
1 parent
2792051a16
commit
3da436423f
12 files changed
+1865
-43
No files matched your search
@@ -0,0 +1,102 @@
|
||||
import { ClipboardList } from "lucide-react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { HelpLink } from "@/components/HelpLink";
|
||||
import { TodoPanel } from "@/components/TodoPanel";
|
||||
import { getTodos } from "../api";
|
||||
import type { TicketTodo, TodoStatus } from "../types";
|
||||
|
||||
type TodosPageProps = {
|
||||
onNavigate: (to: string) => void;
|
||||
};
|
||||
|
||||
export function TodosPage({ onNavigate }: TodosPageProps) {
|
||||
const [todos, setTodos] = useState<TicketTodo[]>([]);
|
||||
const [status, setStatus] = useState<TodoStatus | "all">("open");
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
async function load() {
|
||||
setLoading(true);
|
||||
try {
|
||||
const result = await getTodos(status);
|
||||
setTodos(result.todos);
|
||||
} catch (error) {
|
||||
toast.error("ToDos konnten nicht geladen werden", {
|
||||
description: error instanceof Error ? error.message : "Unbekannter Fehler"
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
void load();
|
||||
}, [status]);
|
||||
|
||||
const counts = useMemo(
|
||||
() => ({
|
||||
open: todos.filter((todo) => todo.status === "open").length,
|
||||
done: todos.filter((todo) => todo.status === "done").length,
|
||||
urgent: todos.filter((todo) => todo.status === "open" && todo.priority === "urgent").length
|
||||
}),
|
||||
[todos]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold tracking-normal sm:text-2xl">ToDos</h2>
|
||||
<p className="text-sm text-muted-foreground">Offene Aufgaben aus Projekten und normalen Tickets an einem Ort.</p>
|
||||
</div>
|
||||
<div className="grid gap-2 text-sm sm:grid-cols-3">
|
||||
<div className="rounded-md border bg-muted/20 px-3 py-2">
|
||||
<span className="block text-muted-foreground">Offen</span>
|
||||
<span className="font-semibold">{counts.open}</span>
|
||||
</div>
|
||||
<div className="rounded-md border bg-muted/20 px-3 py-2">
|
||||
<span className="block text-muted-foreground">Dringend</span>
|
||||
<span className="font-semibold">{counts.urgent}</span>
|
||||
</div>
|
||||
<div className="rounded-md border bg-muted/20 px-3 py-2">
|
||||
<span className="block text-muted-foreground">Erledigt</span>
|
||||
<span className="font-semibold">{counts.done}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="p-4">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<ClipboardList className="size-5 text-muted-foreground" />
|
||||
Aufgabenliste
|
||||
</CardTitle>
|
||||
<CardDescription>ToDos werden im jeweiligen Ticket angelegt und hier gesammelt angezeigt.</CardDescription>
|
||||
</div>
|
||||
<HelpLink anchor="todos" label="Hilfe zu ToDos" />
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2 pt-2">
|
||||
{(["open", "all", "done"] as const).map((value) => (
|
||||
<button
|
||||
key={value}
|
||||
type="button"
|
||||
className={`rounded-md border px-3 py-1.5 text-sm ${status === value ? "bg-primary text-primary-foreground" : "bg-background hover:bg-muted"}`}
|
||||
onClick={() => setStatus(value)}
|
||||
>
|
||||
{value === "open" ? "Offen" : value === "done" ? "Erledigt" : "Alle"}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="px-4 pb-4">
|
||||
<TodoPanel todos={todos} showCreate={false} showTicketMeta onNavigate={onNavigate} onChanged={load} />
|
||||
{!loading && todos.length === 0 ? null : null}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user