L12 Zeiterfassung: Mobile Uhr-Leiste, Meine Zeiten, Freigaben und Backoffice-Liste

Laufende-Uhr-Leiste auf allen /m-Seiten, Start/Pause direkt auf den Auftragskarten,
Auto-Wechsel-Dialog, Auftragsdetail mit Pause / Für heute beenden / Abschließen-Link
und Segmentwechseln, /m/time (Tagesübersicht, Nachtragen, Korrektur vorschlagen),
/m/approvals für Teamleiter, /work-orders/time-approvals mit Sammelfreigabe,
Zeiten-Tab mit Badges und Inline-Freigabe, Dashboard-Kachel, Texte de/en.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 09:29:38 +02:00
co-authored by Claude Opus 5
parent 0e94eb0d9d
commit fe3eba6e44
42 changed files with 2319 additions and 166 deletions
+4 -2
View File
@@ -16,7 +16,8 @@
"billing": "Zur Abrechnung",
"emergency_new": "Neue Notdienste",
"missing_signatures": "Fehlende Unterschriften",
"sync_conflicts": "Sync-Konflikte"
"sync_conflicts": "Sync-Konflikte",
"time_approvals": "Zeiten zur Freigabe"
},
"hints": {
"open": "Noch nicht abgerechnet oder storniert",
@@ -29,7 +30,8 @@
"billing": "Bereit zur Abrechnung",
"emergency_new": "Neu oder zur Prüfung",
"missing_signatures": "Unterschrift ausstehend",
"sync_conflicts": "Offline-Änderungen prüfen"
"sync_conflicts": "Offline-Änderungen prüfen",
"time_approvals": "Nachträge und Korrekturen prüfen"
},
"open": "Anzeigen"
}
+148 -6
View File
@@ -5,7 +5,8 @@
"orders": "Aufträge",
"emergency": "Notdienst",
"sync": "Sync",
"profile": "Profil"
"profile": "Profil",
"approvalsBadge": "{count} Zeiten zur Freigabe"
},
"connection": {
"online": "Online",
@@ -35,7 +36,13 @@
"noDate": "Ohne Termin",
"emergency": "Notdienst",
"urgent": "Dringend",
"high": "Hoch"
"high": "Hoch",
"running": "Zeit läuft",
"paused": "Pausiert",
"enRoute": "In Anfahrt",
"startWork": "Arbeit starten",
"pause": "Pause",
"resume": "Weiter"
},
"statusGroup": {
"new": "Neu",
@@ -78,7 +85,17 @@
"saving": "Wird gespeichert …",
"blocked": "Vor dem Abschluss noch offen:",
"reportNext": "Bericht und Unterschrift folgen im nächsten Schritt.",
"nothing": "Keine Aktion erforderlich."
"nothing": "Keine Aktion erforderlich.",
"stop_day": "Für heute beenden",
"confirmStopDay": "Jetzt für heute beenden",
"stopDayHint": "Die Zeiterfassung endet, der Auftrag bleibt offen und kann morgen fortgesetzt werden.",
"completeLink": "Auftrag abschließen …",
"segment": {
"return_travel": "Rückfahrt starten",
"material_procurement": "Material holen",
"work": "Zurück zur Arbeit"
},
"currentSegment": "Aktuell: {type}"
},
"blocker": {
"checklist_item": "Checkliste: {label}",
@@ -264,7 +281,8 @@
"en_route": "In Anfahrt",
"running": "Läuft",
"paused": "Pausiert",
"ended": "Beendet"
"ended": "Beendet",
"manual": "Nachgetragen"
},
"running": "läuft",
"offlineStarted": "offline gestartet",
@@ -277,7 +295,10 @@
"save": "Korrektur speichern",
"saved": "Korrektur gespeichert.",
"empty": "Noch keine Zeiten erfasst.",
"totalWork": "Arbeitszeit gesamt: {duration}"
"totalWork": "Arbeitszeit gesamt: {duration}",
"propose": "Korrektur vorschlagen",
"totalPending": "Zur Freigabe offen: {duration}",
"ownEntryHint": "Eigene Zeiten korrigieren Sie über „Korrektur vorschlagen“."
},
"profile": {
"title": "Profil",
@@ -287,12 +308,133 @@
"roles": "Rolle",
"language": "Sprache",
"logout": "Abmelden",
"backoffice": "Zum Büro-Bereich"
"backoffice": "Zum Büro-Bereich",
"myTime": "Meine Zeiten",
"approvals": "Zeiten freigeben"
},
"sync": {
"title": "Synchronisation",
"immediate": "Änderungen werden sofort an das Büro übertragen.",
"offlineHint": "Ohne Verbindung können Eingaben noch nicht zwischengespeichert werden – die Offline-Warteschlange folgt.",
"status": "Verbindung"
},
"clock": {
"label": "Laufende Zeiterfassung",
"running": "Läuft",
"paused": "Pausiert",
"enRoute": "Anfahrt",
"openOrder": "Auftrag {number} öffnen",
"pause": "Pause",
"resume": "Weiter",
"stopDay": "Für heute beenden",
"stopDayConfirm": "Beenden?",
"myTime": "Meine Zeiten",
"offline": "offline"
},
"switch": {
"title": "Andere Zeiterfassung läuft",
"text": "{from} läuft noch. Pausieren und {to} starten?",
"textUnknown": "Auf einem anderen Auftrag läuft noch eine Zeiterfassung. Pausieren und {to} starten?",
"confirm": "Wechseln",
"cancel": "Abbrechen"
},
"myTime": {
"title": "Meine Zeiten",
"sub": "Ihre Zeiten je Tag über alle Aufträge.",
"today": "Heute",
"yesterday": "Gestern",
"days": "Tag wählen",
"empty": "An diesem Tag sind keine Zeiten erfasst.",
"approved": "Freigegeben",
"pending": "Zur Freigabe",
"dayTotal": "Tagessumme",
"totalApproved": "Freigegeben: {duration}",
"totalPending": "Offen: {duration}",
"add": "Zeit nachtragen",
"running": "läuft",
"badge": {
"manual": "Manuell",
"pending": "Zur Freigabe",
"rejected": "Abgelehnt: {reason}",
"correctionRequested": "Korrektur beantragt",
"correctionRejected": "Korrektur abgelehnt: {reason}",
"corrected": "Korrigiert"
},
"proposed": "Vorschlag: {from}–{to} · {type}",
"localPending": "Wartet auf Übertragung",
"localTitle": "Noch nicht übertragen",
"form": {
"title": "Zeit nachtragen",
"order": "Auftrag",
"orderPlaceholder": "Auftrag wählen",
"noOrders": "Keine Aufträge der letzten 7 Tage verfügbar.",
"forUser": "Für",
"me": "Mich selbst",
"type": "Art",
"date": "Datum",
"mode": "Erfassung",
"modeRange": "Von – Bis",
"modeDuration": "Dauer",
"from": "Von",
"to": "Bis",
"duration": "Dauer in Minuten",
"quick": "{minutes} min",
"reason": "Begründung",
"reasonHint": "Pflichtangabe, mindestens 3 Zeichen.",
"note": "Notiz (optional)",
"approvalHint": "Wird nach Freigabe durch Teamleiter oder Büro gezählt.",
"directHint": "Für Teammitglieder erfasste Zeiten gelten sofort als freigegeben.",
"save": "Nachtrag speichern",
"saved": "Nachtrag gespeichert – wartet auf Freigabe.",
"savedDirect": "Zeit gespeichert.",
"queued": "Offline gespeichert – wird übertragen, sobald Verbindung besteht.",
"cancel": "Abbrechen"
},
"correction": {
"title": "Korrektur vorschlagen",
"hint": "Bis zur Freigabe gilt die bisherige Zeit.",
"save": "Vorschlag senden",
"saved": "Korrektur beantragt."
},
"reasons": {
"forgotStart": "Start vergessen",
"noNetwork": "Kein Netz",
"recordedLater": "Nachträglich erfasst"
},
"errors": {
"overlap": "Der Zeitraum überschneidet sich mit einer bereits erfassten Zeit.",
"too_old": "Nachtragen ist nur für die letzten 7 Tage möglich.",
"in_future": "Die Zeit darf nicht in der Zukunft liegen.",
"too_long": "Ein Eintrag darf höchstens 16 Stunden lang sein.",
"end_before_start": "Das Ende muss nach dem Beginn liegen.",
"work_order_status": "Für diesen Auftrag können keine Zeiten mehr erfasst werden.",
"not_own_entry": "Sie können nur eigene Zeiten korrigieren.",
"own_entry": "Eigene Zeiten können Sie nicht selbst freigeben.",
"entry_running": "Die Zeit läuft noch."
}
},
"approvals": {
"title": "Zeiten freigeben",
"sub": "Nachträge und Korrekturen Ihres Teams.",
"empty": "Keine offenen Nachträge oder Korrekturen.",
"link": "Zeiten freigeben",
"count": "{count, plural, =0 {Nichts offen} one {# offen} other {# offen}}",
"manual": "Nachtrag",
"correction": "Korrektur",
"reason": "Begründung",
"note": "Notiz",
"old": "Bisher",
"new": "Neu",
"approve": "Freigeben",
"reject": "Ablehnen",
"rejectReason": "Grund der Ablehnung",
"rejectConfirm": "Ablehnung senden",
"cancel": "Abbrechen",
"approved": "Freigegeben.",
"rejected": "Abgelehnt."
},
"pendingTime": {
"report": "Hinweis: {duration} nachgetragene Zeit ist noch nicht freigegeben und fehlt im Bericht.",
"proposals": "Hinweis: {count, plural, one {# Zeitkorrektur wartet} other {# Zeitkorrekturen warten}} auf Freigabe."
}
}
+2 -1
View File
@@ -16,5 +16,6 @@
"lotse": "Lotse (KI)",
"admin": "Admin-Konsole",
"openMenu": "Menü öffnen",
"closeMenu": "Menü schließen"
"closeMenu": "Menü schließen",
"timeApprovals": "Zeiten zur Freigabe"
}
+84 -18
View File
@@ -45,26 +45,92 @@
"emergency_completed": "Notdiensteinsatz abgeschlossen",
"import_ready_for_review": "Import zur Prüfung",
"import_failed": "Fehler bei Dokumentverarbeitung",
"sync_failed": "Synchronisationsfehler"
"sync_failed": "Synchronisationsfehler",
"time_approval_requested": "Zeit zur Freigabe",
"time_approved": "Zeit freigegeben",
"time_rejected": "Zeit abgelehnt"
},
"events": {
"work_order_assigned": { "title": "Neuer Auftrag {number}", "message": "{title} – {customer} wurde Ihrem Team zugewiesen." },
"work_order_changed": { "title": "Auftrag {number} geändert", "message": "{title} – {customer}: Angaben wurden aktualisiert." },
"work_order_cancelled": { "title": "Auftrag {number} storniert", "message": "{title} – {customer} wurde storniert." },
"work_order_started": { "title": "Auftrag {number} gestartet", "message": "{actor} hat den Einsatz bei {customer} begonnen." },
"work_order_daily_report_created": { "title": "Tagesbericht zu {number}", "message": "{actor} hat einen Tagesbericht für {customer} erstellt." },
"work_order_technically_completed": { "title": "Auftrag {number} technisch abgeschlossen", "message": "{title} – {customer}: Einsatz vor Ort beendet." },
"work_order_signature_missing": { "title": "Unterschrift fehlt: {number}", "message": "{title} – {customer}: Kundenunterschrift liegt nicht vor." },
"work_order_missing_required": { "title": "Pflichtangaben fehlen: {number}", "message": "{title} – {customer}: Dokumentation unvollständig." },
"work_order_released_for_billing": { "title": "Auftrag {number} zur Abrechnung", "message": "{title} – {customer} ist bereit zur Abrechnung." },
"report_submitted": { "title": "Bericht zur Prüfung: {number}", "message": "{actor} hat einen Bericht zu {title} eingereicht." },
"report_approved": { "title": "Bericht freigegeben: {number}", "message": "Der Bericht zu {title} wurde freigegeben." },
"report_rejected": { "title": "Bericht zurückgewiesen: {number}", "message": "Der Bericht zu {title} braucht eine Korrektur." },
"emergency_created": { "title": "Notdiensteinsatz {number} erstellt", "message": "{actor} hat einen Notdiensteinsatz bei {customer} angelegt." },
"emergency_completed": { "title": "Notdiensteinsatz {number} abgeschlossen", "message": "{customer}: zur Prüfung und Abrechnung." },
"import_ready_for_review": { "title": "Import zur Prüfung bereit", "message": "Die Daten aus {fileName} liegen zur Prüfung vor." },
"import_failed": { "title": "Fehler bei Dokumentverarbeitung", "message": "{fileName} konnte nicht verarbeitet werden." },
"sync_failed": { "title": "Synchronisation fehlgeschlagen", "message": "Eine Änderung konnte nicht übernommen werden ({reason})." }
"work_order_assigned": {
"title": "Neuer Auftrag {number}",
"message": "{title} – {customer} wurde Ihrem Team zugewiesen."
},
"work_order_changed": {
"title": "Auftrag {number} geändert",
"message": "{title} – {customer}: Angaben wurden aktualisiert."
},
"work_order_cancelled": {
"title": "Auftrag {number} storniert",
"message": "{title} – {customer} wurde storniert."
},
"work_order_started": {
"title": "Auftrag {number} gestartet",
"message": "{actor} hat den Einsatz bei {customer} begonnen."
},
"work_order_daily_report_created": {
"title": "Tagesbericht zu {number}",
"message": "{actor} hat einen Tagesbericht für {customer} erstellt."
},
"work_order_technically_completed": {
"title": "Auftrag {number} technisch abgeschlossen",
"message": "{title} – {customer}: Einsatz vor Ort beendet."
},
"work_order_signature_missing": {
"title": "Unterschrift fehlt: {number}",
"message": "{title} – {customer}: Kundenunterschrift liegt nicht vor."
},
"work_order_missing_required": {
"title": "Pflichtangaben fehlen: {number}",
"message": "{title} – {customer}: Dokumentation unvollständig."
},
"work_order_released_for_billing": {
"title": "Auftrag {number} zur Abrechnung",
"message": "{title} – {customer} ist bereit zur Abrechnung."
},
"report_submitted": {
"title": "Bericht zur Prüfung: {number}",
"message": "{actor} hat einen Bericht zu {title} eingereicht."
},
"report_approved": {
"title": "Bericht freigegeben: {number}",
"message": "Der Bericht zu {title} wurde freigegeben."
},
"report_rejected": {
"title": "Bericht zurückgewiesen: {number}",
"message": "Der Bericht zu {title} braucht eine Korrektur."
},
"emergency_created": {
"title": "Notdiensteinsatz {number} erstellt",
"message": "{actor} hat einen Notdiensteinsatz bei {customer} angelegt."
},
"emergency_completed": {
"title": "Notdiensteinsatz {number} abgeschlossen",
"message": "{customer}: zur Prüfung und Abrechnung."
},
"import_ready_for_review": {
"title": "Import zur Prüfung bereit",
"message": "Die Daten aus {fileName} liegen zur Prüfung vor."
},
"import_failed": {
"title": "Fehler bei Dokumentverarbeitung",
"message": "{fileName} konnte nicht verarbeitet werden."
},
"sync_failed": {
"title": "Synchronisation fehlgeschlagen",
"message": "Eine Änderung konnte nicht übernommen werden ({reason})."
},
"time_approval_requested": {
"title": "Zeit zur Freigabe: {number}",
"message": "{actor} hat eine Zeit zu {number} nachgetragen oder eine Korrektur beantragt."
},
"time_approved": {
"title": "Zeit freigegeben: {number}",
"message": "{actor} hat Ihre Zeit zu {number} freigegeben."
},
"time_rejected": {
"title": "Zeit abgelehnt: {number}",
"message": "{actor} hat Ihre Zeit zu {number} abgelehnt: {reason}"
}
},
"fallback": {
"unknown": "unbekannt",
+4 -1
View File
@@ -89,7 +89,10 @@
"upload": "Die Datei wurde nicht angenommen (Format oder Größe).",
"unauthorized": "Anmeldung abgelaufen – bitte neu anmelden.",
"network": "Keine Verbindung – wird automatisch erneut versucht.",
"internal": "Technischer Fehler – wird automatisch erneut versucht."
"internal": "Technischer Fehler – wird automatisch erneut versucht.",
"timeOverlap": "Der Zeitnachtrag überschneidet sich mit einer bereits erfassten Zeit und wurde nicht übernommen.",
"timeWindow": "Der Zeitnachtrag liegt außerhalb der erlaubten 7 Tage, in der Zukunft oder ist zu lang und wurde nicht übernommen.",
"otherSession": "Auf einem anderen Auftrag lief noch eine Zeiterfassung – der Start wurde nicht übernommen. Bitte erneut starten und wechseln."
},
"view": {
"title": "Offline-Ansicht",
+44 -3
View File
@@ -237,7 +237,21 @@
"material_procurement": "Materialbeschaffung",
"return_travel": "Rückfahrt",
"interruption": "Unterbrechung"
}
},
"pendingMinutes": "Zur Freigabe offen: {minutes} min",
"approvedMinutes": "Freigegeben",
"badgeManual": "Manuell",
"badgePending": "Zur Freigabe",
"badgeRejected": "Abgelehnt: {reason}",
"badgeCorrection": "Korrektur beantragt: {from}–{to}",
"reason": "Begründung: {reason}",
"manualSession": "Nachgetragen",
"approve": "Freigeben",
"reject": "Ablehnen",
"rejectReason": "Grund",
"approved": "Freigegeben.",
"rejected": "Abgelehnt.",
"openApprovals": "Alle offenen Freigaben"
},
"photos": {
"empty": "Noch keine Fotos.",
@@ -460,8 +474,35 @@
"running_session": "Ein Einsatz läuft noch",
"field": {
"signature": "Unterschrift fehlt",
"approved_completion_report": "Abschlussbericht ist noch nicht freigegeben"
"approved_completion_report": "Abschlussbericht ist noch nicht freigegeben",
"pending_time_entries": "Zeiten warten noch auf Freigabe"
}
},
"saved": "Gespeichert."
"saved": "Gespeichert.",
"timeApprovals": {
"crumb": "Aufträge",
"title": "Zeiten zur Freigabe",
"sub": "Nachträge und Korrekturen der Monteure. Erst nach Freigabe zählen sie für Bericht und Abrechnung.",
"empty": "Keine offenen Nachträge oder Korrekturen.",
"employee": "Mitarbeiter",
"order": "Auftrag",
"date": "Datum",
"range": "Von–Bis",
"duration": "Dauer",
"type": "Art",
"reason": "Begründung",
"kindManual": "Nachtrag",
"kindCorrection": "Korrektur",
"oldNew": "{old} → {new}",
"select": "Auswählen",
"selectAll": "Alle auswählen",
"approveSelected": "Ausgewählte freigeben",
"approve": "Freigeben",
"reject": "Ablehnen",
"rejectReason": "Grund der Ablehnung",
"approved": "Freigegeben.",
"bulkApproved": "{ok} freigegeben, {failed} nicht möglich.",
"rejected": "Abgelehnt.",
"count": "{count, plural, one {# offener Eintrag} other {# offene Einträge}}"
}
}