feat(reflexion): ToDo-Titel inline editierbar
ToDos in der Reflexion lassen sich per Klick auf den Titel korrigieren (z. B. Tippfehler), Enter speichert / Escape bricht ab. Neuer Endpoint dayEntry.editTodoTitle — nur eigene ToDos vom heutigen Berlin-Tag, ändert den Titel in-place (Status/Reihenfolge/IDs bleiben). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
804b7637e7
commit
8933169ba9
3 files changed
+124
-2
No files matched your search
@@ -7,7 +7,7 @@ import { Button } from '../components/ui/Button';
|
||||
import { SkeletonCard } from '../components/ui/SkeletonCard';
|
||||
import { toast } from 'sonner';
|
||||
import { clsx } from 'clsx';
|
||||
import { Check, X, Minus, TrendingUp, TrendingDown, ArrowRight } from 'lucide-react';
|
||||
import { Check, X, Minus, TrendingUp, TrendingDown, ArrowRight, Pencil } from 'lucide-react';
|
||||
|
||||
type Ampel = 'green' | 'amber' | 'red';
|
||||
type Trend = 'besser' | 'gleich' | 'schlechter';
|
||||
@@ -128,7 +128,7 @@ function ReflectForm({
|
||||
const status = todoStates[t.id]?.status ?? 'open';
|
||||
return (
|
||||
<li key={t.id} className="flex items-center gap-2">
|
||||
<span className="flex-1 text-sm">{t.title}</span>
|
||||
<EditableTodoTitle id={t.id} title={t.title} />
|
||||
<div className="flex gap-1">
|
||||
<StatusBtn
|
||||
active={status === 'done'}
|
||||
@@ -269,6 +269,91 @@ function ReflectForm({
|
||||
);
|
||||
}
|
||||
|
||||
function EditableTodoTitle({ id, title }: { id: string; title: string }) {
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [draft, setDraft] = useState(title);
|
||||
const utils = trpc.useUtils();
|
||||
const edit = trpc.dayEntry.editTodoTitle.useMutation({
|
||||
onSuccess: async () => {
|
||||
await utils.dayEntry.today.invalidate();
|
||||
setEditing(false);
|
||||
},
|
||||
});
|
||||
|
||||
function save() {
|
||||
const v = draft.trim();
|
||||
if (!v || v === title) {
|
||||
setEditing(false);
|
||||
return;
|
||||
}
|
||||
edit.mutate({ todoId: id, title: v });
|
||||
}
|
||||
|
||||
if (editing) {
|
||||
return (
|
||||
<div className="flex flex-1 items-center gap-1">
|
||||
<input
|
||||
ref={(el) => el?.focus()}
|
||||
value={draft}
|
||||
onChange={(e) => setDraft(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
save();
|
||||
} else if (e.key === 'Escape') {
|
||||
setDraft(title);
|
||||
setEditing(false);
|
||||
}
|
||||
}}
|
||||
maxLength={200}
|
||||
aria-label="ToDo-Titel bearbeiten"
|
||||
className="flex-1 rounded-[var(--radius-md)] border border-[var(--color-border)] bg-white px-2 py-1 text-sm focus:border-[var(--color-ink)] focus:outline-none"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={save}
|
||||
disabled={edit.isPending}
|
||||
aria-label="Titel speichern"
|
||||
title="Speichern"
|
||||
className="inline-flex h-7 w-7 items-center justify-center rounded-[var(--radius-md)] border border-[var(--color-ink)] bg-[var(--color-ink)] text-[var(--color-brand-fg)]"
|
||||
>
|
||||
<Check size={14} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setDraft(title);
|
||||
setEditing(false);
|
||||
}}
|
||||
aria-label="Bearbeiten abbrechen"
|
||||
title="Abbrechen"
|
||||
className="inline-flex h-7 w-7 items-center justify-center rounded-[var(--radius-md)] border border-[var(--color-border)] text-[var(--color-ink-muted)] hover:border-[var(--color-border-strong)]"
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setDraft(title);
|
||||
setEditing(true);
|
||||
}}
|
||||
title="Zum Bearbeiten klicken"
|
||||
className="group flex flex-1 items-center gap-1.5 text-left text-sm"
|
||||
>
|
||||
<span>{title}</span>
|
||||
<Pencil
|
||||
size={12}
|
||||
className="text-[var(--color-ink-faint)] opacity-0 transition-opacity group-hover:opacity-100"
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function StatusBtn({
|
||||
active,
|
||||
onClick,
|
||||
|
||||
Reference in new issue
Block a user