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": "Ready for billing",
"emergency_new": "New emergencies",
"missing_signatures": "Missing signatures",
"sync_conflicts": "Sync conflicts"
"sync_conflicts": "Sync conflicts",
"time_approvals": "Time approvals"
},
"hints": {
"open": "Not yet billed or cancelled",
@@ -29,7 +30,8 @@
"billing": "Ready to invoice",
"emergency_new": "New or in review",
"missing_signatures": "Signature pending",
"sync_conflicts": "Review offline changes"
"sync_conflicts": "Review offline changes",
"time_approvals": "Review added time and corrections"
},
"open": "Show"
}
+148 -6
View File
@@ -5,7 +5,8 @@
"orders": "Orders",
"emergency": "Emergency",
"sync": "Sync",
"profile": "Profile"
"profile": "Profile",
"approvalsBadge": "{count} time entries to approve"
},
"connection": {
"online": "Online",
@@ -35,7 +36,13 @@
"noDate": "No date",
"emergency": "Emergency",
"urgent": "Urgent",
"high": "High"
"high": "High",
"running": "Time running",
"paused": "Paused",
"enRoute": "On the way",
"startWork": "Start work",
"pause": "Pause",
"resume": "Resume"
},
"statusGroup": {
"new": "New",
@@ -78,7 +85,17 @@
"saving": "Saving …",
"blocked": "Still missing before completion:",
"reportNext": "Report and signature follow in the next step.",
"nothing": "No action required."
"nothing": "No action required.",
"stop_day": "Stop for today",
"confirmStopDay": "Stop for today now",
"stopDayHint": "Time recording ends, the order stays open and can be continued tomorrow.",
"completeLink": "Complete order …",
"segment": {
"return_travel": "Start return trip",
"material_procurement": "Fetch material",
"work": "Back to work"
},
"currentSegment": "Current: {type}"
},
"blocker": {
"checklist_item": "Checklist: {label}",
@@ -264,7 +281,8 @@
"en_route": "On the way",
"running": "Running",
"paused": "Paused",
"ended": "Ended"
"ended": "Ended",
"manual": "Recorded later"
},
"running": "running",
"offlineStarted": "started offline",
@@ -277,7 +295,10 @@
"save": "Save correction",
"saved": "Correction saved.",
"empty": "No time recorded yet.",
"totalWork": "Total working time: {duration}"
"totalWork": "Total working time: {duration}",
"propose": "Propose correction",
"totalPending": "Awaiting approval: {duration}",
"ownEntryHint": "Correct your own time via “Propose correction”."
},
"profile": {
"title": "Profile",
@@ -287,12 +308,133 @@
"roles": "Role",
"language": "Language",
"logout": "Sign out",
"backoffice": "Go to office area"
"backoffice": "Go to office area",
"myTime": "My time",
"approvals": "Approve time"
},
"sync": {
"title": "Synchronisation",
"immediate": "Changes are sent to the office immediately.",
"offlineHint": "Without a connection, entries cannot be buffered yet – the offline queue will follow.",
"status": "Connection"
},
"clock": {
"label": "Running time recording",
"running": "Running",
"paused": "Paused",
"enRoute": "Travel",
"openOrder": "Open order {number}",
"pause": "Pause",
"resume": "Resume",
"stopDay": "Stop for today",
"stopDayConfirm": "Stop?",
"myTime": "My time",
"offline": "offline"
},
"switch": {
"title": "Another time recording is running",
"text": "{from} is still running. Pause it and start {to}?",
"textUnknown": "Time is still running on another order. Pause it and start {to}?",
"confirm": "Switch",
"cancel": "Cancel"
},
"myTime": {
"title": "My time",
"sub": "Your time per day across all orders.",
"today": "Today",
"yesterday": "Yesterday",
"days": "Choose day",
"empty": "No time recorded on this day.",
"approved": "Approved",
"pending": "Awaiting approval",
"dayTotal": "Day total",
"totalApproved": "Approved: {duration}",
"totalPending": "Open: {duration}",
"add": "Add time",
"running": "running",
"badge": {
"manual": "Manual",
"pending": "Awaiting approval",
"rejected": "Rejected: {reason}",
"correctionRequested": "Correction requested",
"correctionRejected": "Correction rejected: {reason}",
"corrected": "Corrected"
},
"proposed": "Proposal: {from}–{to} · {type}",
"localPending": "Waiting to be sent",
"localTitle": "Not sent yet",
"form": {
"title": "Add time",
"order": "Order",
"orderPlaceholder": "Choose order",
"noOrders": "No orders from the last 7 days available.",
"forUser": "For",
"me": "Myself",
"type": "Type",
"date": "Date",
"mode": "Entry",
"modeRange": "From – to",
"modeDuration": "Duration",
"from": "From",
"to": "To",
"duration": "Duration in minutes",
"quick": "{minutes} min",
"reason": "Reason",
"reasonHint": "Required, at least 3 characters.",
"note": "Note (optional)",
"approvalHint": "Counts once approved by the team lead or office.",
"directHint": "Time recorded for team members is approved immediately.",
"save": "Save entry",
"saved": "Entry saved – awaiting approval.",
"savedDirect": "Time saved.",
"queued": "Saved offline – will be sent once connected.",
"cancel": "Cancel"
},
"correction": {
"title": "Propose correction",
"hint": "The current time stays valid until approved.",
"save": "Send proposal",
"saved": "Correction requested."
},
"reasons": {
"forgotStart": "Forgot to start",
"noNetwork": "No network",
"recordedLater": "Recorded afterwards"
},
"errors": {
"overlap": "The period overlaps with time already recorded.",
"too_old": "Time can only be added for the last 7 days.",
"in_future": "The time must not be in the future.",
"too_long": "An entry may be at most 16 hours long.",
"end_before_start": "The end must be after the start.",
"work_order_status": "Time can no longer be recorded for this order.",
"not_own_entry": "You can only correct your own time.",
"own_entry": "You cannot approve your own time.",
"entry_running": "The time is still running."
}
},
"approvals": {
"title": "Approve time",
"sub": "Added entries and corrections of your team.",
"empty": "No open entries or corrections.",
"link": "Approve time",
"count": "{count, plural, =0 {Nothing open} one {# open} other {# open}}",
"manual": "Added entry",
"correction": "Correction",
"reason": "Reason",
"note": "Note",
"old": "Before",
"new": "New",
"approve": "Approve",
"reject": "Reject",
"rejectReason": "Reason for rejection",
"rejectConfirm": "Send rejection",
"cancel": "Cancel",
"approved": "Approved.",
"rejected": "Rejected."
},
"pendingTime": {
"report": "Note: {duration} of added time is not approved yet and missing from the report.",
"proposals": "Note: {count, plural, one {# time correction is} other {# time corrections are}} awaiting approval."
}
}
+2 -1
View File
@@ -16,5 +16,6 @@
"lotse": "Lotse (AI)",
"admin": "Admin console",
"openMenu": "Open menu",
"closeMenu": "Close menu"
"closeMenu": "Close menu",
"timeApprovals": "Time approvals"
}
+84 -18
View File
@@ -45,26 +45,92 @@
"emergency_completed": "Emergency call-out completed",
"import_ready_for_review": "Import ready for review",
"import_failed": "Document processing failed",
"sync_failed": "Sync failed"
"sync_failed": "Sync failed",
"time_approval_requested": "Time to approve",
"time_approved": "Time approved",
"time_rejected": "Time rejected"
},
"events": {
"work_order_assigned": { "title": "New work order {number}", "message": "{title} – {customer} was assigned to your team." },
"work_order_changed": { "title": "Work order {number} changed", "message": "{title} – {customer}: details were updated." },
"work_order_cancelled": { "title": "Work order {number} cancelled", "message": "{title} – {customer} was cancelled." },
"work_order_started": { "title": "Work order {number} started", "message": "{actor} started work at {customer}." },
"work_order_daily_report_created": { "title": "Daily report for {number}", "message": "{actor} created a daily report for {customer}." },
"work_order_technically_completed": { "title": "Work order {number} technically completed", "message": "{title} – {customer}: on-site work finished." },
"work_order_signature_missing": { "title": "Signature missing: {number}", "message": "{title} – {customer}: no customer signature." },
"work_order_missing_required": { "title": "Required information missing: {number}", "message": "{title} – {customer}: documentation incomplete." },
"work_order_released_for_billing": { "title": "Work order {number} ready for billing", "message": "{title} – {customer} is ready for billing." },
"report_submitted": { "title": "Report for review: {number}", "message": "{actor} submitted a report for {title}." },
"report_approved": { "title": "Report approved: {number}", "message": "The report for {title} was approved." },
"report_rejected": { "title": "Report rejected: {number}", "message": "The report for {title} needs a correction." },
"emergency_created": { "title": "Emergency call-out {number} created", "message": "{actor} created an emergency call-out at {customer}." },
"emergency_completed": { "title": "Emergency call-out {number} completed", "message": "{customer}: ready for review and billing." },
"import_ready_for_review": { "title": "Import ready for review", "message": "The data from {fileName} is ready for review." },
"import_failed": { "title": "Document processing failed", "message": "{fileName} could not be processed." },
"sync_failed": { "title": "Sync failed", "message": "A change could not be applied ({reason})." }
"work_order_assigned": {
"title": "New work order {number}",
"message": "{title} – {customer} was assigned to your team."
},
"work_order_changed": {
"title": "Work order {number} changed",
"message": "{title} – {customer}: details were updated."
},
"work_order_cancelled": {
"title": "Work order {number} cancelled",
"message": "{title} – {customer} was cancelled."
},
"work_order_started": {
"title": "Work order {number} started",
"message": "{actor} started work at {customer}."
},
"work_order_daily_report_created": {
"title": "Daily report for {number}",
"message": "{actor} created a daily report for {customer}."
},
"work_order_technically_completed": {
"title": "Work order {number} technically completed",
"message": "{title} – {customer}: on-site work finished."
},
"work_order_signature_missing": {
"title": "Signature missing: {number}",
"message": "{title} – {customer}: no customer signature."
},
"work_order_missing_required": {
"title": "Required information missing: {number}",
"message": "{title} – {customer}: documentation incomplete."
},
"work_order_released_for_billing": {
"title": "Work order {number} ready for billing",
"message": "{title} – {customer} is ready for billing."
},
"report_submitted": {
"title": "Report for review: {number}",
"message": "{actor} submitted a report for {title}."
},
"report_approved": {
"title": "Report approved: {number}",
"message": "The report for {title} was approved."
},
"report_rejected": {
"title": "Report rejected: {number}",
"message": "The report for {title} needs a correction."
},
"emergency_created": {
"title": "Emergency call-out {number} created",
"message": "{actor} created an emergency call-out at {customer}."
},
"emergency_completed": {
"title": "Emergency call-out {number} completed",
"message": "{customer}: ready for review and billing."
},
"import_ready_for_review": {
"title": "Import ready for review",
"message": "The data from {fileName} is ready for review."
},
"import_failed": {
"title": "Document processing failed",
"message": "{fileName} could not be processed."
},
"sync_failed": {
"title": "Sync failed",
"message": "A change could not be applied ({reason})."
},
"time_approval_requested": {
"title": "Time to approve: {number}",
"message": "{actor} added time or requested a correction for {number}."
},
"time_approved": {
"title": "Time approved: {number}",
"message": "{actor} approved your time for {number}."
},
"time_rejected": {
"title": "Time rejected: {number}",
"message": "{actor} rejected your time for {number}: {reason}"
}
},
"fallback": {
"unknown": "unknown",
+4 -1
View File
@@ -89,7 +89,10 @@
"upload": "The file was not accepted (format or size).",
"unauthorized": "Your session expired – please sign in again.",
"network": "No connection – will retry automatically.",
"internal": "Technical error – will retry automatically."
"internal": "Technical error – will retry automatically.",
"timeOverlap": "The added time overlaps with time already recorded and was not accepted.",
"timeWindow": "The added time is outside the allowed 7 days, in the future or too long and was not accepted.",
"otherSession": "Time was still running on another order – the start was not accepted. Please start again and switch."
},
"view": {
"title": "Offline view",
+44 -3
View File
@@ -237,7 +237,21 @@
"material_procurement": "Material procurement",
"return_travel": "Return travel",
"interruption": "Interruption"
}
},
"pendingMinutes": "Awaiting approval: {minutes} min",
"approvedMinutes": "Approved",
"badgeManual": "Manual",
"badgePending": "Awaiting approval",
"badgeRejected": "Rejected: {reason}",
"badgeCorrection": "Correction requested: {from}–{to}",
"reason": "Reason: {reason}",
"manualSession": "Recorded later",
"approve": "Approve",
"reject": "Reject",
"rejectReason": "Reason",
"approved": "Approved.",
"rejected": "Rejected.",
"openApprovals": "All open approvals"
},
"photos": {
"empty": "No photos yet.",
@@ -460,8 +474,35 @@
"running_session": "A work session is still running",
"field": {
"signature": "Signature missing",
"approved_completion_report": "Completion report not approved yet"
"approved_completion_report": "Completion report not approved yet",
"pending_time_entries": "Time entries are still awaiting approval"
}
},
"saved": "Saved."
"saved": "Saved.",
"timeApprovals": {
"crumb": "Work orders",
"title": "Time approvals",
"sub": "Added time and corrections by technicians. They count for reports and billing only after approval.",
"empty": "No open entries or corrections.",
"employee": "Employee",
"order": "Order",
"date": "Date",
"range": "From–to",
"duration": "Duration",
"type": "Type",
"reason": "Reason",
"kindManual": "Added entry",
"kindCorrection": "Correction",
"oldNew": "{old} → {new}",
"select": "Select",
"selectAll": "Select all",
"approveSelected": "Approve selected",
"approve": "Approve",
"reject": "Reject",
"rejectReason": "Reason for rejection",
"approved": "Approved.",
"bulkApproved": "{ok} approved, {failed} not possible.",
"rejected": "Rejected.",
"count": "{count, plural, one {# open entry} other {# open entries}}"
}
}