Add carryover management for overtime, update weekday naming to German, and enhance reset functionality.

This commit is contained in:
T.Landgraf committed 2026-08-14 13:26:02 +02:00
1 parent 21ed1e7744
commit ceb18d1d8d
2 files changed
+47 -19

No files matched your search

+47 -18
View File
@@ -1,8 +1,8 @@
import { SegmentedControl, Center, Stack, Button, Text, NumberInput } from '@mantine/core'; import { SegmentedControl, Center, Stack, Button, Text, NumberInput, Space } from '@mantine/core';
import { TimePicker } from '@mantine/dates'; import { TimePicker } from '@mantine/dates';
import * as React from 'react'; import * as React from 'react';
type Workday = 'Monday' | 'Tuesday' | 'Wednesday' | 'Thursday' | 'Friday'; type Workday = 'Montag' | 'Dienstag' | 'Mittwoch' | 'Donnerstag' | 'Freitag';
interface DayRecord { interface DayRecord {
start: string; start: string;
@@ -15,9 +15,12 @@ interface DayRecord {
type Records = Partial<Record<Workday, DayRecord>>; type Records = Partial<Record<Workday, DayRecord>>;
const STORAGE_KEY = 'workTrackerRecords'; const STORAGE_KEY = 'workTrackerRecords';
const CARRYOVER_KEY = 'workTrackerCarryover';
const DEFAULT_RECORD: DayRecord = { start: '', end: '', breakMins: 0, workedHrs: 0, overtimeMinutes: 0 }; const DEFAULT_RECORD: DayRecord = { start: '', end: '', breakMins: 0, workedHrs: 0, overtimeMinutes: 0 };
const WEEKDAYS: Workday[] = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag'];
const loadRecords = (): Records => { const loadRecords = (): Records => {
try { try {
const raw = localStorage.getItem(STORAGE_KEY); const raw = localStorage.getItem(STORAGE_KEY);
@@ -27,6 +30,15 @@ const loadRecords = (): Records => {
} }
}; };
const loadCarryover = (): number => {
try {
const val = Number(localStorage.getItem(CARRYOVER_KEY));
return Number.isFinite(val) ? val : 0;
} catch {
return 0;
}
};
const toMinutes = (time: string): number | null => { const toMinutes = (time: string): number | null => {
const match = /^(\d{2}):(\d{2})$/.exec(time); const match = /^(\d{2}):(\d{2})$/.exec(time);
if (!match) return null; if (!match) return null;
@@ -42,6 +54,7 @@ const fromMinutes = (mins: number): string => {
function App() { function App() {
const [selectedDay, setSelectedDay] = React.useState<Workday>('Monday'); const [selectedDay, setSelectedDay] = React.useState<Workday>('Monday');
const [records, setRecords] = React.useState<Records>(loadRecords); const [records, setRecords] = React.useState<Records>(loadRecords);
const [carryoverMinutes, setCarryoverMinutes] = React.useState<number>(loadCarryover);
const record = records[selectedDay] ?? DEFAULT_RECORD; const record = records[selectedDay] ?? DEFAULT_RECORD;
@@ -49,6 +62,10 @@ function App() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(records)); localStorage.setItem(STORAGE_KEY, JSON.stringify(records));
}, [records]); }, [records]);
React.useEffect(() => {
localStorage.setItem(CARRYOVER_KEY, String(carryoverMinutes));
}, [carryoverMinutes]);
const updateRecord = (patch: Partial<DayRecord>) => { const updateRecord = (patch: Partial<DayRecord>) => {
setRecords((prev) => ({ setRecords((prev) => ({
...prev, ...prev,
@@ -64,14 +81,14 @@ function App() {
} }
const getWeeklyBalance = () => { const getWeeklyBalance = () => {
const days: Workday[] = ['Monday', 'Tuesday', 'Wednesday', 'Thursday']; const days: Workday[] = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag'];
return days.reduce((sum, day) => sum + (records[day]?.overtimeMinutes ?? 0), 0); return days.reduce((sum, day) => sum + (records[day]?.overtimeMinutes ?? 0), 0) + carryoverMinutes;
} }
const getWorkEndTime = () => { const getWorkEndTime = () => {
const start = toMinutes(record.start); const start = toMinutes(record.start);
if (start === null) return '—'; if (start === null) return '-';
const balance = selectedDay === 'Friday' ? getWeeklyBalance() : 0; const balance = selectedDay === 'Freitag' ? getWeeklyBalance() : 0;
return fromMinutes(start + 8 * 60 - balance + record.breakMins); return fromMinutes(start + 8 * 60 - balance + record.breakMins);
} }
@@ -80,16 +97,21 @@ function App() {
updateRecord({ workedHrs: hours, overtimeMinutes: Math.round((hours - 8) * 60) }); updateRecord({ workedHrs: hours, overtimeMinutes: Math.round((hours - 8) * 60) });
} }
const handleReset = () => { const handleResetWorkdays = () => {
localStorage.removeItem(STORAGE_KEY); const totalOvertime = WEEKDAYS.reduce((sum, day) => sum + (records[day]?.overtimeMinutes ?? 0), 0) + carryoverMinutes;
setCarryoverMinutes(totalOvertime);
setRecords({}); setRecords({});
} }
const handleResetOvertime = () => {
setCarryoverMinutes(0);
}
return ( return (
<Center> <Center className={"my-auto"}>
<Stack> <Stack>
<SegmentedControl <SegmentedControl
data={['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']} data={['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag']}
onChange={(value) => setSelectedDay(value as Workday)} onChange={(value) => setSelectedDay(value as Workday)}
className={"mt-2"} className={"mt-2"}
/> />
@@ -117,26 +139,33 @@ function App() {
/> />
<Button onClick={handleSave}> <Button onClick={handleSave}>
Speichern Berechnen + Speichern
</Button> </Button>
<Text>Gearbeitete Stunden heute: {record.workedHrs.toFixed(2)}</Text> <Text>Gearbeitete Stunden heute: {record.workedHrs.toFixed(2)}</Text>
{selectedDay === 'Friday' && ( {carryoverMinutes !== 0 && (
<Text c={"green"}>Übertragen aus Montag bis Donnerstag: {getWeeklyBalance()} Minuten</Text> <Text c={"blue"}>Übernommene Überstunden aus Vorwoche: {carryoverMinutes} Minuten</Text>
)}
{selectedDay === 'Freitag' && (
<Text c={"green"}>Übertragen aus Montag bis Donnerstag (inkl. Vorwoche): {getWeeklyBalance()} Minuten</Text>
)} )}
<Text>Wann du gehen kannst für 8 Stunden: {getWorkEndTime()}</Text> <Text>Wann du gehen kannst für 8 Stunden: {getWorkEndTime()}</Text>
{record.overtimeMinutes === 0 && ( {record.start && record.end && record.overtimeMinutes === 0 && (
<Text>Du hast genau 8 Stunden gearbeitet.</Text> <Text>Du hast genau 8 Stunden gearbeitet.</Text>
)} )}
{record.overtimeMinutes > 0 && ( {record.start && record.end && record.overtimeMinutes > 0 && (
<Text c="yellow">Überstunden: {record.overtimeMinutes} Minuten</Text> <Text c="yellow">Überstunden: {record.overtimeMinutes} Minuten</Text>
)} )}
{record.overtimeMinutes < 0 && ( {record.start && record.end && record.overtimeMinutes < 0 && (
<Text c={"red"}>Fehlende Minuten für 8 Stunden: {-record.overtimeMinutes} Minuten</Text> <Text c={"red"}>Fehlende Minuten für 8 Stunden: {-record.overtimeMinutes} Minuten</Text>
)} )}
<Button color="red" variant="outline" onClick={handleReset}> <Space h={"xl"} />
Reset saved records <Button color="red" variant="outline" onClick={handleResetWorkdays}>
Arbeitstage zurücksetzen
</Button>
<Button color="red" variant="outline" onClick={handleResetOvertime}>
Überstunden zurücksetzen
</Button> </Button>
</Stack> </Stack>
</Center> </Center>
-1
View File
@@ -1,5 +1,4 @@
import '@mantine/core/styles.css'; import '@mantine/core/styles.css';
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import './index.css' import './index.css'
import App from './App.tsx' import App from './App.tsx'