Anwesenheit auch in "Zeit im Team heute" anzeigen
teamToday liefert pro Person checkedInAt/checkedOutAt aus dem heutigen day_entry mit; die Team-Karte zeigt daneben "Anwesend: …" (live, bzw. eingefroren nach Feierabend). Reine Anzeige, kein Eingriff ins Tracking. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
687325ad0d
commit
e0fbc386fa
2 files changed
+41
-7
No files matched your search
@@ -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,
|
||||
};
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() {
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
{presenceSecondsOf(it) !== null && (
|
||||
<span title="Anwesenheit: Lock-in bis Feierabend (bzw. jetzt) — unabhängig vom Tracking.">
|
||||
{' · Anwesend: '}
|
||||
<span className="font-mono">
|
||||
{formatDurationCompact(presenceSecondsOf(it)!)}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user