diff --git a/apps/api/src/routers/time.ts b/apps/api/src/routers/time.ts index 2818f59..62940d2 100644 --- a/apps/api/src/routers/time.ts +++ b/apps/api/src/routers/time.ts @@ -341,14 +341,30 @@ export const timeRouter = router({ perUser.set(r.userId, bucket); } + // Anwesenheit (Lock-in → Feierabend) pro Person — reine Anzeige neben dem Tracking. + const dayRows = await ctx.db + .select({ + userId: dayEntry.userId, + checkedInAt: dayEntry.checkedInAt, + checkedOutAt: dayEntry.checkedOutAt, + }) + .from(dayEntry) + .where(eq(dayEntry.date, today)); + const presenceByUser = new Map(dayRows.map((d) => [d.userId, d])); + // „Gesamt" pro Person = Wall-Clock (Vereinigung der Intervalle). - return Array.from(perUser.values()).map((b) => ({ - userId: b.userId, - totalSeconds: unionSeconds(b.intervals), - trackedSeconds: b.trackedSeconds, - byCategory: b.byCategory, - active: b.active, - })); + return Array.from(perUser.values()).map((b) => { + const p = presenceByUser.get(b.userId); + return { + userId: b.userId, + totalSeconds: unionSeconds(b.intervals), + trackedSeconds: b.trackedSeconds, + byCategory: b.byCategory, + active: b.active, + checkedInAt: p?.checkedInAt ? p.checkedInAt.toISOString() : null, + checkedOutAt: p?.checkedOutAt ? p.checkedOutAt.toISOString() : null, + }; + }); }), /** diff --git a/apps/web/src/components/time/TeamTimeSection.tsx b/apps/web/src/components/time/TeamTimeSection.tsx index 330a7f6..15717de 100644 --- a/apps/web/src/components/time/TeamTimeSection.tsx +++ b/apps/web/src/components/time/TeamTimeSection.tsx @@ -28,6 +28,16 @@ function initials(name: string) { .join(''); } +/** Anwesenheit (Lock-in → Feierabend bzw. jetzt) — reine Anzeige, kein Tracking. */ +function presenceSecondsOf(it: { + checkedInAt: string | null; + checkedOutAt: string | null; +}): number | null { + if (!it.checkedInAt) return null; + const endMs = it.checkedOutAt ? new Date(it.checkedOutAt).getTime() : Date.now(); + return Math.max(0, Math.round((endMs - new Date(it.checkedInAt).getTime()) / 1000)); +} + export function TeamTimeSection() { const team = trpc.time.teamToday.useQuery(undefined, { refetchInterval: 30_000 }); const users = trpc.user.list.useQuery(); @@ -90,6 +100,14 @@ export function TeamTimeSection() { )} + {presenceSecondsOf(it) !== null && ( + + {' · Anwesend: '} + + {formatDurationCompact(presenceSecondsOf(it)!)} + + + )}