Merge branch 'reisezeit'
This commit is contained in:
@@ -48,10 +48,16 @@ und liefern 401 ohne Session.
|
||||
## Datenmodell & Berechnungen
|
||||
|
||||
Tabelle `arbeitszeit`: `datum`, `ort` (Kunde FFM | Homeoffice | Andrena), `beginn`, `ende`
|
||||
(je HH:MM), `reisezeit`, `vertriebsunterstuetzung` (default 2), `kommentar` (max. 500).
|
||||
(je HH:MM), `reise_beginn`, `reise_ende` (je HH:MM, optional), `pause` (dezimale Stunden),
|
||||
`vertriebsunterstuetzung` (default 2), `kommentar` (max. 500).
|
||||
|
||||
- `Arbeitszeit = Ende − Beginn` (dezimale Stunden)
|
||||
- `Reisezeit = Reise-Ende − Reise-Beginn`: im Formular werden Start/Ende eingegeben, die
|
||||
Reisezeit wird wie Mehrarbeit zur Laufzeit berechnet (nicht gespeichert) und in Vorschau
|
||||
und Liste angezeigt. Beim Speichern koppelt sie nur die Vertriebsunterstützung.
|
||||
- `Mehrarbeit = Arbeitszeit − 8 − Pause`
|
||||
- Die Spalte „Arbeitszeit Kunde" ist fix 8 h (`ARBEITSZEIT_KUNDE_FIX`).
|
||||
- Die **Pause** ist eine globale Einstellung in der `settings`-Tabelle (Tab „Einstellungen").
|
||||
Mehrarbeit wird zur Laufzeit berechnet, nicht gespeichert.
|
||||
- Die **Pause** wird pro Eintrag gespeichert (Spalte `pause`, dezimale Stunden; Eingabe im
|
||||
Formular in Minuten zwischen Beginn und Ende). Standard ist die globale Pause-Einstellung
|
||||
in der `settings`-Tabelle (Tab „Einstellungen"). Mehrarbeit wird je Eintrag aus dieser
|
||||
Pause zur Laufzeit berechnet, nicht gespeichert.
|
||||
|
||||
+1
-2
@@ -121,7 +121,6 @@ export default function MainClient({ username }: Props) {
|
||||
</button>
|
||||
</div>
|
||||
<ArbeitszeitList
|
||||
pause={pause}
|
||||
refreshKey={refreshKey}
|
||||
onEdit={handleEdit}
|
||||
limit={10}
|
||||
@@ -135,7 +134,7 @@ export default function MainClient({ username }: Props) {
|
||||
{/* Liste-Tab */}
|
||||
{activeTab === 'liste' && (
|
||||
<div className="border-2 border-gray-400 rounded-xl bg-white p-3 print:border-0 print:rounded-none print:p-0">
|
||||
<ArbeitszeitList pause={pause} refreshKey={refreshKey} onEdit={handleEdit} limit={100} />
|
||||
<ArbeitszeitList refreshKey={refreshKey} onEdit={handleEdit} limit={100} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -44,7 +44,12 @@ export default function ArbeitszeitForm({ editEntry, pause, onSaved, onCancel }:
|
||||
const [ort, setOrt] = useState<string>(editEntry?.ort ?? ORTE[0]);
|
||||
const [beginn, setBeginn] = useState(editEntry?.beginn ?? '');
|
||||
const [ende, setEnde] = useState(editEntry?.ende ?? '');
|
||||
const [reisezeit, setReisezeit] = useState(editEntry ? numToStr(editEntry.reisezeit) : '0');
|
||||
// Pause pro Eintrag in Minuten; Standard ist die globale Einstellung (Stunden → Minuten).
|
||||
const [pauseMin, setPauseMin] = useState(
|
||||
String(Math.round((editEntry?.pause ?? pause) * 60))
|
||||
);
|
||||
const [reiseBeginn, setReiseBeginn] = useState(editEntry?.reise_beginn ?? '');
|
||||
const [reiseEnde, setReiseEnde] = useState(editEntry?.reise_ende ?? '');
|
||||
const [vertrieb, setVertrieb] = useState(editEntry ? numToStr(editEntry.vertriebsunterstuetzung) : '0');
|
||||
const [kommentar, setKommentar] = useState(editEntry?.kommentar ?? '');
|
||||
const [saving, setSaving] = useState(false);
|
||||
@@ -53,21 +58,31 @@ export default function ArbeitszeitForm({ editEntry, pause, onSaved, onCancel }:
|
||||
|
||||
const zeiten = hatZeiten(beginn, ende);
|
||||
const arbeitszeit = toDecimalHours(beginn, ende);
|
||||
const mehr = mehrarbeit(arbeitszeit, pause);
|
||||
const vertriebFrei = vertriebEditierbar(toNum(reisezeit));
|
||||
const pauseStd = Math.max(0, toNum(pauseMin)) / 60;
|
||||
const mehr = mehrarbeit(arbeitszeit, pauseStd);
|
||||
const reisezeit = toDecimalHours(reiseBeginn, reiseEnde);
|
||||
const reiseZeiten = hatZeiten(reiseBeginn, reiseEnde);
|
||||
const vertriebFrei = vertriebEditierbar(reisezeit);
|
||||
|
||||
// Vertriebsunterstützung an die Reisezeit koppeln (siehe applyVertriebsregel):
|
||||
// Vertriebsunterstützung an die (aus den Reise-Zeiten berechnete) Reisezeit koppeln
|
||||
// (siehe applyVertriebsregel):
|
||||
// =0 ⇒ 0, <2 ⇒ = Reisezeit, ≥2 ⇒ 2 beim Überschreiten der Schwelle, danach frei änderbar.
|
||||
function handleReisezeit(raw: string) {
|
||||
const alt = toNum(reisezeit);
|
||||
const neu = toNum(raw);
|
||||
setReisezeit(raw);
|
||||
if (neu <= 0) setVertrieb('0');
|
||||
else if (neu < 2) setVertrieb(numToStr(neu));
|
||||
else if (alt < 2) setVertrieb('2'); // Schwelle 2h überschritten → Standard 2h
|
||||
function koppleVertrieb(altReise: number, neuReise: number) {
|
||||
if (neuReise <= 0) setVertrieb('0');
|
||||
else if (neuReise < 2) setVertrieb(numToStr(neuReise));
|
||||
else if (altReise < 2) setVertrieb('2'); // Schwelle 2h überschritten → Standard 2h
|
||||
// neu ≥ 2 und alt ≥ 2: bestehenden (ggf. geänderten) Wert beibehalten
|
||||
}
|
||||
|
||||
function handleReiseBeginn(raw: string) {
|
||||
koppleVertrieb(reisezeit, toDecimalHours(raw, reiseEnde));
|
||||
setReiseBeginn(raw);
|
||||
}
|
||||
function handleReiseEnde(raw: string) {
|
||||
koppleVertrieb(reisezeit, toDecimalHours(reiseBeginn, raw));
|
||||
setReiseEnde(raw);
|
||||
}
|
||||
|
||||
async function handleSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
@@ -84,6 +99,17 @@ export default function ArbeitszeitForm({ editEntry, pause, onSaved, onCancel }:
|
||||
return;
|
||||
}
|
||||
|
||||
const reiseBeginnLeer = reiseBeginn === '';
|
||||
const reiseEndeLeer = reiseEnde === '';
|
||||
if (reiseBeginnLeer !== reiseEndeLeer) {
|
||||
setError('Bitte Reise-Start und -Ende gemeinsam angeben oder beide leer lassen.');
|
||||
return;
|
||||
}
|
||||
if (!reiseBeginnLeer && !reiseEndeLeer && reisezeit <= 0) {
|
||||
setError('Reise-Ende muss nach dem Reise-Start liegen.');
|
||||
return;
|
||||
}
|
||||
|
||||
setSaving(true);
|
||||
|
||||
const body = {
|
||||
@@ -91,7 +117,9 @@ export default function ArbeitszeitForm({ editEntry, pause, onSaved, onCancel }:
|
||||
ort,
|
||||
beginn,
|
||||
ende,
|
||||
reisezeit: toNum(reisezeit),
|
||||
reise_beginn: reiseBeginn,
|
||||
reise_ende: reiseEnde,
|
||||
pause: pauseStd,
|
||||
vertriebsunterstuetzung: toNum(vertrieb),
|
||||
kommentar,
|
||||
};
|
||||
@@ -111,6 +139,18 @@ export default function ArbeitszeitForm({ editEntry, pause, onSaved, onCancel }:
|
||||
}
|
||||
setSuccess(true);
|
||||
setTimeout(() => setSuccess(false), 4000);
|
||||
// Beim Anlegen die Eingabefelder wieder auf die Startwerte zurücksetzen.
|
||||
if (!editEntry) {
|
||||
setDatum(todayDate());
|
||||
setOrt(ORTE[0]);
|
||||
setBeginn('');
|
||||
setEnde('');
|
||||
setPauseMin(String(Math.round(pause * 60)));
|
||||
setReiseBeginn('');
|
||||
setReiseEnde('');
|
||||
setVertrieb('0');
|
||||
setKommentar('');
|
||||
}
|
||||
onSaved();
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Fehler beim Speichern.');
|
||||
@@ -160,19 +200,27 @@ export default function ArbeitszeitForm({ editEntry, pause, onSaved, onCancel }:
|
||||
<label className={labelCls}>Beginn</label>
|
||||
<TimeInput value={beginn} onChange={setBeginn} className="w-24" />
|
||||
</div>
|
||||
<div className="shrink-0">
|
||||
<label className={labelCls}>Pause (min)</label>
|
||||
<input
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
value={pauseMin}
|
||||
onChange={(e) => setPauseMin(e.target.value.replace(/[^0-9]/g, ''))}
|
||||
className={numCls}
|
||||
/>
|
||||
</div>
|
||||
<div className="shrink-0">
|
||||
<label className={labelCls}>Ende</label>
|
||||
<TimeInput value={ende} onChange={setEnde} className="w-24" />
|
||||
</div>
|
||||
<div className="shrink-0">
|
||||
<label className={labelCls}>Reisezeit (h)</label>
|
||||
<input
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
value={reisezeit}
|
||||
onChange={(e) => handleReisezeit(e.target.value)}
|
||||
className={numCls}
|
||||
/>
|
||||
<label className={labelCls}>Reise-Beginn</label>
|
||||
<TimeInput value={reiseBeginn} onChange={handleReiseBeginn} className="w-24" />
|
||||
</div>
|
||||
<div className="shrink-0">
|
||||
<label className={labelCls}>Reise-Ende</label>
|
||||
<TimeInput value={reiseEnde} onChange={handleReiseEnde} className="w-24" />
|
||||
</div>
|
||||
<div className="shrink-0">
|
||||
<label className={labelCls}>Vertriebsunterst. (h)</label>
|
||||
@@ -212,7 +260,7 @@ export default function ArbeitszeitForm({ editEntry, pause, onSaved, onCancel }:
|
||||
Arbeitszeit Kunde: <strong>{zeiten ? `${formatHours(ARBEITSZEIT_KUNDE_FIX)} h` : '—'}</strong>
|
||||
</span>
|
||||
<span>
|
||||
Pause: <strong>{formatHours(pause)} h</strong>
|
||||
Pause: <strong>{formatHours(pauseStd)} h</strong>
|
||||
</span>
|
||||
<span>
|
||||
Mehrarbeit:{' '}
|
||||
@@ -220,6 +268,9 @@ export default function ArbeitszeitForm({ editEntry, pause, onSaved, onCancel }:
|
||||
{zeiten ? `${formatHours(mehr)} h` : '—'}
|
||||
</strong>
|
||||
</span>
|
||||
<span>
|
||||
Reisezeit: <strong>{reiseZeiten ? `${formatHours(reisezeit)} h` : '—'}</strong>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
|
||||
@@ -5,7 +5,6 @@ import { ARBEITSZEIT_KUNDE_FIX, type ArbeitszeitEintrag } from '@/types/arbeitsz
|
||||
import { toDecimalHours, mehrarbeit, weekdayName, formatHours, hatZeiten } from '@/lib/calc';
|
||||
|
||||
interface Props {
|
||||
pause: number;
|
||||
refreshKey: number;
|
||||
onEdit: (entry: ArbeitszeitEintrag) => void;
|
||||
limit?: number;
|
||||
@@ -36,7 +35,7 @@ function formatDate(d: string) {
|
||||
return m ? `${m[3]}.${m[2]}.${m[1]}` : d;
|
||||
}
|
||||
|
||||
export default function ArbeitszeitList({ pause, refreshKey, onEdit, limit = 15, compact = false }: Props) {
|
||||
export default function ArbeitszeitList({ refreshKey, onEdit, limit = 15, compact = false }: Props) {
|
||||
const [entries, setEntries] = useState<ArbeitszeitEintrag[]>([]);
|
||||
const [month, setMonth] = useState(compact ? '' : currentMonth());
|
||||
const [deleteId, setDeleteId] = useState<number | null>(null);
|
||||
@@ -132,7 +131,8 @@ export default function ArbeitszeitList({ pause, refreshKey, onEdit, limit = 15,
|
||||
</tr>
|
||||
) : entries.map((e) => {
|
||||
const zeiten = hatZeiten(e.beginn, e.ende);
|
||||
const mehr = mehrarbeit(toDecimalHours(e.beginn, e.ende), pause);
|
||||
const mehr = mehrarbeit(toDecimalHours(e.beginn, e.ende), e.pause);
|
||||
const reisezeit = toDecimalHours(e.reise_beginn, e.reise_ende);
|
||||
return (
|
||||
<tr key={e.id} className="hover:bg-gray-50">
|
||||
<td className={`${cell} whitespace-nowrap`}>{formatDate(e.datum)}</td>
|
||||
@@ -140,7 +140,7 @@ export default function ArbeitszeitList({ pause, refreshKey, onEdit, limit = 15,
|
||||
<td className={cell}>{e.ort}</td>
|
||||
<td className={`${cell} text-right`}>{zeiten ? formatHours(ARBEITSZEIT_KUNDE_FIX) : '—'}</td>
|
||||
<td className={`${cell} text-right ${zeiten && mehr < 0 ? 'text-red-600' : ''}`}>{zeiten ? formatHours(mehr) : '—'}</td>
|
||||
<td className={`${cell} text-right`}>{e.reisezeit ? formatHours(e.reisezeit) : '—'}</td>
|
||||
<td className={`${cell} text-right`}>{reisezeit ? formatHours(reisezeit) : '—'}</td>
|
||||
<td className={`${cell} text-right`}>{e.vertriebsunterstuetzung ? formatHours(e.vertriebsunterstuetzung) : '—'}</td>
|
||||
<td className={cell}>{e.kommentar}</td>
|
||||
<td className={`${cell} text-center whitespace-nowrap print:hidden`}>
|
||||
|
||||
@@ -25,7 +25,9 @@ export function getDb(): Database.Database {
|
||||
ort TEXT NOT NULL,
|
||||
beginn TEXT NOT NULL,
|
||||
ende TEXT NOT NULL,
|
||||
reisezeit REAL NOT NULL DEFAULT 0,
|
||||
reise_beginn TEXT NOT NULL DEFAULT '',
|
||||
reise_ende TEXT NOT NULL DEFAULT '',
|
||||
pause REAL NOT NULL DEFAULT 0.5,
|
||||
vertriebsunterstuetzung REAL NOT NULL DEFAULT 2,
|
||||
kommentar TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
@@ -49,11 +51,37 @@ export function getDb(): Database.Database {
|
||||
);
|
||||
`);
|
||||
|
||||
// Migration: Reise-Start/-Ende für bestehende Datenbanken ergänzen.
|
||||
const cols = (db.prepare(`PRAGMA table_info(arbeitszeit)`).all() as { name: string }[]).map(
|
||||
(c) => c.name
|
||||
);
|
||||
if (!cols.includes('reise_beginn')) {
|
||||
db.exec(`ALTER TABLE arbeitszeit ADD COLUMN reise_beginn TEXT NOT NULL DEFAULT ''`);
|
||||
}
|
||||
if (!cols.includes('reise_ende')) {
|
||||
db.exec(`ALTER TABLE arbeitszeit ADD COLUMN reise_ende TEXT NOT NULL DEFAULT ''`);
|
||||
}
|
||||
// Reisezeit wird zur Laufzeit aus reise_beginn/reise_ende berechnet, nicht mehr gespeichert.
|
||||
if (cols.includes('reisezeit')) {
|
||||
db.exec(`ALTER TABLE arbeitszeit DROP COLUMN reisezeit`);
|
||||
}
|
||||
|
||||
// Standard-Pause beim ersten Start setzen (0,5 h)
|
||||
db.prepare(
|
||||
`INSERT INTO settings (key, value) VALUES ('pause', '0.5')
|
||||
ON CONFLICT(key) DO NOTHING`
|
||||
).run();
|
||||
|
||||
// Migration: Pause pro Eintrag ergänzen. Bestehende Zeilen bekommen die aktuelle
|
||||
// globale Pause-Einstellung als Wert.
|
||||
if (!cols.includes('pause')) {
|
||||
const row = db.prepare(`SELECT value FROM settings WHERE key = 'pause'`).get() as
|
||||
| { value: string }
|
||||
| undefined;
|
||||
const globalPause = row ? parseFloat(row.value) : 0.5;
|
||||
const def = isNaN(globalPause) ? 0.5 : globalPause;
|
||||
db.exec(`ALTER TABLE arbeitszeit ADD COLUMN pause REAL NOT NULL DEFAULT ${def}`);
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
+4
-3
@@ -43,8 +43,8 @@ export function createArbeitszeit(input: ArbeitszeitInput): ArbeitszeitEintrag {
|
||||
const db = getDb();
|
||||
const info = db
|
||||
.prepare(
|
||||
`INSERT INTO arbeitszeit (datum, ort, beginn, ende, reisezeit, vertriebsunterstuetzung, kommentar)
|
||||
VALUES (@datum, @ort, @beginn, @ende, @reisezeit, @vertriebsunterstuetzung, @kommentar)`
|
||||
`INSERT INTO arbeitszeit (datum, ort, beginn, ende, reise_beginn, reise_ende, pause, vertriebsunterstuetzung, kommentar)
|
||||
VALUES (@datum, @ort, @beginn, @ende, @reise_beginn, @reise_ende, @pause, @vertriebsunterstuetzung, @kommentar)`
|
||||
)
|
||||
.run(input);
|
||||
return getArbeitszeit(Number(info.lastInsertRowid))!;
|
||||
@@ -58,7 +58,8 @@ export function updateArbeitszeit(
|
||||
db.prepare(
|
||||
`UPDATE arbeitszeit SET
|
||||
datum = @datum, ort = @ort, beginn = @beginn, ende = @ende,
|
||||
reisezeit = @reisezeit, vertriebsunterstuetzung = @vertriebsunterstuetzung,
|
||||
reise_beginn = @reise_beginn, reise_ende = @reise_ende,
|
||||
pause = @pause, vertriebsunterstuetzung = @vertriebsunterstuetzung,
|
||||
kommentar = @kommentar
|
||||
WHERE id = @id`
|
||||
).run({ ...input, id });
|
||||
|
||||
+21
-4
@@ -1,5 +1,6 @@
|
||||
import { ORTE, type ArbeitszeitInput, type Ort } from '@/types/arbeitszeit';
|
||||
import { parseHM, applyVertriebsregel } from './calc';
|
||||
import { parseHM, applyVertriebsregel, toDecimalHours } from './calc';
|
||||
import { getPause } from './repo';
|
||||
|
||||
/**
|
||||
* Validiert und normalisiert einen JSON-Body zu einem ArbeitszeitInput.
|
||||
@@ -24,9 +25,25 @@ export function parseArbeitszeitInput(body: unknown): ArbeitszeitInput {
|
||||
throw new Error('Bitte Start- und Endzeit gemeinsam angeben oder beide leer lassen.');
|
||||
}
|
||||
|
||||
const reisezeit = toNumber(b.reisezeit, 0);
|
||||
// Reise-Start/-Ende sind optional (analog Beginn/Ende). Wenn angegeben,
|
||||
// müssen sie gültig sein; entweder beide leer oder beide gesetzt.
|
||||
const reise_beginn = String(b.reise_beginn ?? '');
|
||||
const reise_ende = String(b.reise_ende ?? '');
|
||||
if (reise_beginn !== '' && parseHM(reise_beginn) === null)
|
||||
throw new Error('Ungültige Reise-Startzeit.');
|
||||
if (reise_ende !== '' && parseHM(reise_ende) === null)
|
||||
throw new Error('Ungültige Reise-Endzeit.');
|
||||
if ((reise_beginn === '') !== (reise_ende === '')) {
|
||||
throw new Error('Bitte Reise-Start und -Ende gemeinsam angeben oder beide leer lassen.');
|
||||
}
|
||||
|
||||
// Reisezeit wird zur Laufzeit aus den Reise-Zeiten berechnet (nicht gespeichert),
|
||||
// dient hier aber zur Kopplung der Vertriebsunterstützung.
|
||||
const reisezeit = toDecimalHours(reise_beginn, reise_ende);
|
||||
// Pause pro Eintrag (dezimale Stunden). Standard ist die globale Einstellung.
|
||||
const pause = toNumber(b.pause, getPause());
|
||||
let vertriebsunterstuetzung = toNumber(b.vertriebsunterstuetzung, 2);
|
||||
if (reisezeit < 0 || vertriebsunterstuetzung < 0) {
|
||||
if (pause < 0 || vertriebsunterstuetzung < 0) {
|
||||
throw new Error('Zeitwerte dürfen nicht negativ sein.');
|
||||
}
|
||||
// Regel: Vertriebsunterstützung an die Reisezeit koppeln.
|
||||
@@ -35,7 +52,7 @@ export function parseArbeitszeitInput(body: unknown): ArbeitszeitInput {
|
||||
|
||||
const kommentar = String(b.kommentar ?? '').slice(0, 500);
|
||||
|
||||
return { datum, ort, beginn, ende, reisezeit, vertriebsunterstuetzung, kommentar };
|
||||
return { datum, ort, beginn, ende, reise_beginn, reise_ende, pause, vertriebsunterstuetzung, kommentar };
|
||||
}
|
||||
|
||||
function toNumber(value: unknown, fallback: number): number {
|
||||
|
||||
Generated
+3
-26
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "arbeitszeit",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "arbeitszeit",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"dependencies": {
|
||||
"@simplewebauthn/browser": "^13.3.0",
|
||||
"@simplewebauthn/server": "^13.3.1",
|
||||
@@ -282,30 +282,6 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.2.1",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
|
||||
"integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||
@@ -3691,6 +3667,7 @@
|
||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.9",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "arbeitszeit",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -10,7 +10,9 @@ export interface ArbeitszeitEintrag {
|
||||
ort: Ort;
|
||||
beginn: string; // HH:MM
|
||||
ende: string; // HH:MM
|
||||
reisezeit: number; // dezimale Stunden
|
||||
reise_beginn: string; // HH:MM, leer wenn keine Reise
|
||||
reise_ende: string; // HH:MM, leer wenn keine Reise
|
||||
pause: number; // dezimale Stunden (Eingabe im Formular in Minuten)
|
||||
vertriebsunterstuetzung: number; // dezimale Stunden
|
||||
kommentar: string;
|
||||
created_at: string;
|
||||
|
||||
Reference in New Issue
Block a user