refactor(zeit): Pausen nicht mehr buchen — Lücken zwischen Tasks = Pause
Pause-Feature entfernt: kein Pause-Button, kein break-Buchen mehr. Man bucht nur Tasks; stoppt man eine Task und es läuft keine andere, ist die Lücke automatisch eine Pause — im Kalender/Tagesansicht schlicht leer. Tasks bleiben pausierbar (Stop = Pause, erneut Start = Fortsetzen → mehrere Segmente auf dieselbe Task, Lücke dazwischen leer). Wall-Clock zählt Lücken ohnehin nicht. - time.pause-Endpoint entfernt; 'break' aus der angebotenen Kategorienliste raus (bleibt im Enum für Altdaten). - TimerPill: Pause-Button + Pause-Panel-Grund entfernt; Stop/Start je Task bleibt (= pausieren/fortsetzen). Heute-ToDo-Zeile entsprechend beschriftet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5bbb4abffa
commit
7f9a4506c5
4 files changed
+5
-69
No files matched your search
@@ -143,31 +143,6 @@ export const timeRouter = router({
|
||||
return { stopped };
|
||||
}),
|
||||
|
||||
/**
|
||||
* Pause: stoppt ALLE laufenden Tasks und startet eine Pause (break).
|
||||
*/
|
||||
pause: protectedProcedure
|
||||
.input(TimeEntrySchemas.pauseInput)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const now = new Date();
|
||||
const dayEntryId = await findCurrentDayEntryId(ctx.db, ctx.user.id);
|
||||
return ctx.db.transaction(async (tx) => {
|
||||
await stopAllActive(tx, ctx.user.id, now);
|
||||
const [brk] = await tx
|
||||
.insert(timeEntry)
|
||||
.values({
|
||||
userId: ctx.user.id,
|
||||
dayEntryId,
|
||||
category: 'break',
|
||||
description: input.description ?? null,
|
||||
startedAt: now,
|
||||
})
|
||||
.returning();
|
||||
if (!brk) throw new Error('Pause konnte nicht gestartet werden.');
|
||||
return brk;
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Alle Time-Entries des heutigen Tages plus aktiver.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Play, Square, Plus, Pause } from 'lucide-react';
|
||||
import { Play, Square, Plus } from 'lucide-react';
|
||||
import { clsx } from 'clsx';
|
||||
import { toast } from 'sonner';
|
||||
import { trpc } from '../../lib/trpc';
|
||||
@@ -85,15 +85,6 @@ export function TimerPill() {
|
||||
toast('Timer läuft.');
|
||||
},
|
||||
});
|
||||
const pauseMutation = trpc.time.pause.useMutation({
|
||||
onSuccess: async () => {
|
||||
await invalidate();
|
||||
setPickerOpen(false);
|
||||
setPanelOpen(false);
|
||||
toast('Pause läuft.');
|
||||
},
|
||||
});
|
||||
|
||||
const count = active.length;
|
||||
const longest = count > 0 ? Math.max(...active.map(elapsedOf)) : 0;
|
||||
|
||||
@@ -130,17 +121,6 @@ export function TimerPill() {
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => pauseMutation.mutate({})}
|
||||
disabled={pauseMutation.isPending}
|
||||
className="inline-flex items-center gap-1 rounded-full bg-black/5 px-2.5 py-1 text-xs font-medium text-[var(--color-ink-muted)] hover:bg-black/10 hover:text-[var(--color-ink)] disabled:opacity-50"
|
||||
title="Pause — stoppt alle laufenden Tasks und trägt eine Pause ein"
|
||||
>
|
||||
<Pause size={10} fill="currentColor" />
|
||||
<span>Pause</span>
|
||||
</button>
|
||||
|
||||
{pickerOpen && (
|
||||
<QuickPicker
|
||||
todos={(todayQuery.data?.todos ?? []).filter((t) => t.status === 'open')}
|
||||
@@ -174,7 +154,6 @@ function TimersPanel({
|
||||
onChanged: () => Promise<void>;
|
||||
}) {
|
||||
const stopMutation = trpc.time.stop.useMutation({ onSuccess: onChanged });
|
||||
const updateMutation = trpc.time.update.useMutation({ onSuccess: onChanged });
|
||||
return (
|
||||
<div className="absolute right-0 top-full z-40 mt-2 w-[320px] rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-surface-elevated)] p-3 shadow-[var(--shadow-lg)]">
|
||||
<p className="px-1 text-xs font-medium text-[var(--color-ink-muted)]">
|
||||
@@ -199,21 +178,6 @@ function TimersPanel({
|
||||
<Square size={12} fill="currentColor" />
|
||||
</button>
|
||||
</div>
|
||||
{a.category === 'break' && (
|
||||
<input
|
||||
type="text"
|
||||
defaultValue={a.description ?? ''}
|
||||
placeholder="Grund (optional) …"
|
||||
maxLength={200}
|
||||
onBlur={(e) => {
|
||||
const v = e.target.value.trim();
|
||||
if ((a.description ?? '') !== v) {
|
||||
updateMutation.mutate({ id: a.id, description: v || null });
|
||||
}
|
||||
}}
|
||||
className="mt-1 ml-4 w-[calc(100%-1rem)] rounded-[var(--radius-md)] border border-[var(--color-border)] bg-white px-2 py-1 text-xs"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -238,7 +238,8 @@ function TodoRow({
|
||||
? stopMutation.mutate({ id: activeHere.id })
|
||||
: startMutation.mutate({ todoId: todo.id, category: 'focus' })
|
||||
}
|
||||
aria-label={isRunningHere ? 'Timer stoppen' : 'Timer für diesen ToDo starten'}
|
||||
aria-label={isRunningHere ? 'Task pausieren' : 'Task starten / fortsetzen'}
|
||||
title={isRunningHere ? 'Pausieren' : 'Starten / fortsetzen'}
|
||||
className={
|
||||
isRunningHere
|
||||
? 'inline-flex h-7 items-center gap-1 rounded-full bg-[var(--color-ampel-red-bg)] px-2 text-xs font-medium text-[var(--color-ampel-red-fg)]'
|
||||
|
||||
@@ -15,9 +15,10 @@ export const TIME_CATEGORIES: { value: TimeCategory; label: string }[] = [
|
||||
{ value: 'meeting', label: 'Meeting' },
|
||||
{ value: 'admin', label: 'Admin' },
|
||||
{ value: 'learning', label: 'Lernen' },
|
||||
{ value: 'break', label: 'Pause' },
|
||||
{ value: 'other', label: 'Sonstiges' },
|
||||
];
|
||||
// 'break' bleibt im Enum (timeCategorySchema) für Altdaten, wird aber nicht mehr
|
||||
// angeboten — Pausen werden nicht gebucht (Lücken zwischen Tasks = Pause).
|
||||
|
||||
export const startTimeEntryInput = z.object({
|
||||
todoId: z.string().uuid().optional(),
|
||||
@@ -36,11 +37,6 @@ export const updateTimeEntryInput = z.object({
|
||||
});
|
||||
export type UpdateTimeEntryInput = z.infer<typeof updateTimeEntryInput>;
|
||||
|
||||
export const pauseInput = z.object({
|
||||
description: z.string().max(200).optional(),
|
||||
});
|
||||
export type PauseInput = z.infer<typeof pauseInput>;
|
||||
|
||||
export const teamTimelineInput = z.object({
|
||||
date: z
|
||||
.string()
|
||||
|
||||
Reference in new issue
Block a user