Add carryover management for overtime, update weekday naming to German, and enhance reset functionality.
This commit is contained in:
1 parent
21ed1e7744
commit
ceb18d1d8d
2 files changed
+47
-19
No files matched your search
+47
-18
@@ -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,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'
|
||||||
|
|||||||
Reference in new issue
Block a user