L13 Planung: Menüpunkt Planung mit Plantafel und Live-Lage

Plantafel: Standard heute + nächste 4 Werktage (Kolonnen als Zeilen, heutige Spalte mit Live-Status), Heute mit Kolonnen als parallelen Spalten (6–20 Uhr), Woche/nächste Woche, Auslastung, Konflikte/Hinweise, Verzugs- und Gefährdungs-Badges, Drag & Drop (@dnd-kit/core) mit Bestätigungs-Popover und Tastatur-Alternative, ungeplante Aufträge mit Vorschlägen, früher fertig mit Vorziehen, Kolonnenkapazität-Popup. Live-Lage: Leaflet/OSM-Karte mit einem Marker je Kolonne, Liste, Polling 30 s, Hinweis keine GPS-Ortung; CSP img-src für Kacheln. Dashboard-Kacheln Planung heute und Konflikte diese Woche, Einsatz-Vorschläge im Auftragsdetail, Navigation mit Unterpunkten, Smoke-Prüfungen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 10:13:50 +02:00
co-authored by Claude Opus 5
parent 5e10523df6
commit ee17134863
25 changed files with 2557 additions and 3 deletions
+3
View File
@@ -1,6 +1,9 @@
{
"dashboard": "Dashboard",
"workOrders": "Aufträge",
"planning": "Planung",
"planningBoard": "Plantafel",
"planningLive": "Live-Lage",
"emergencyReview": "Notdienst-Prüfung",
"imports": "Import",
"customers": "Kunden",
+199
View File
@@ -0,0 +1,199 @@
{
"crumb": "Planung",
"weekday": { "0": "Mo", "1": "Di", "2": "Mi", "3": "Do", "4": "Fr", "5": "Sa", "6": "So" },
"board": {
"title": "Plantafel",
"sub": "Aufträge je Team und Tag einplanen – Auslastung und Überschneidungen auf einen Blick.",
"readOnly": "Nur Lesezugriff – Sie sehen Ihre eigenen Teams.",
"viewLabel": "Ansicht",
"view": { "today": "Heute", "days": "5 Tage", "week": "Woche", "nextWeek": "Nächste Woche" },
"navLabel": "Zeitraum wechseln",
"prevWeek": "Vorige Woche",
"nextWeek": "Nächste Woche",
"prevDay": "Voriger Tag",
"nextDay": "Nächster Tag",
"prevDays": "Vorige 5 Werktage",
"nextDays": "Nächste 5 Werktage",
"todayLabel": "Heute · {day}",
"time": "Uhrzeit",
"today": "Heute",
"range": "{from} – {to}",
"team": "Team",
"noTeams": "Keine aktiven Teams in dieser Auswahl.",
"showMembers": "Mitglieder von {team} anzeigen",
"hideMembers": "Mitglieder von {team} ausblenden",
"memberHint": "Einzeln zugewiesen",
"load": "{planned} / {capacity} · {percent} %",
"loadNoCapacity": "{planned} · keine Kapazität",
"notWorkingDay": "Kein Arbeitstag",
"allDay": "Ganztägig",
"free": "frei",
"narrowHint": "Die Plantafel ist für Bildschirme ab 1024 px ausgelegt. Hier sehen Sie eine einfache Liste.",
"capacity": "Kapazität",
"capacityOf": "Kapazität von {team} bearbeiten",
"move": "Verschieben …",
"schedule": "Einplanen …",
"dragHint": "Karten mit der Maus auf einen Tag ziehen oder über „Einplanen …“ bzw. „Verschieben …“ per Tastatur einplanen.",
"open": "Auftrag öffnen",
"saved": "{number} eingeplant: {team}, {day}.",
"savedWithConflicts": "{number} eingeplant: {team}, {day}. Achtung: {count} Konflikt(e) am Zieltag.",
"priority": "Priorität: {priority}",
"emergency": "Notdienst",
"multiDay": "mehrtägig",
"duration": "Dauer {duration}",
"listDayEmpty": "Nichts geplant",
"crewCapacity": "Kolonne · {capacity} je Arbeitstag · {count} Personen",
"delayRisk": "Verzug droht · {percent} %",
"delayOverrun": "Dauer überschritten · {percent} %",
"atRisk": "gefährdet durch Verzug {number}"
},
"freed": {
"title": "Früher fertig",
"badge": "{team} · {minutes} früher fertig",
"available": "{minutes} frei ab {time}",
"pullForward": "Vorziehen",
"kindPullForward": "Geplanter Auftrag von {team}",
"kindNearby": "Ungeplant, {distance} entfernt",
"noSuggestions": "Kein passender Auftrag für die freie Zeit.",
"hint": "Nur Hinweis – nichts wird automatisch verschoben."
},
"conflicts": {
"label": "Konflikt",
"count": "{count, plural, =0 {Keine Konflikte} one {# Konflikt} other {# Konflikte}}",
"overbooked": "Überbucht um {over}",
"overlap": "Überschneidung: {a} und {b}",
"assignee_double_booked": "{user} doppelt eingeplant: {a} und {b}",
"outside_working_days": "Außerhalb der Arbeitstage",
"crew_incomplete": "Kolonne unvollständig: {count} aktive Person(en)",
"hintLabel": "Hinweis",
"short": {
"overbooked": "Überbucht",
"overlap": "Überschneidung",
"assignee_double_booked": "Doppelt eingeplant",
"outside_working_days": "Kein Arbeitstag",
"crew_incomplete": "Kolonne unvollständig"
}
},
"filters": {
"title": "Filter",
"team": "Team",
"orderType": "Auftragsart",
"priority": "Priorität",
"any": "Alle",
"apply": "Anwenden",
"reset": "Zurücksetzen"
},
"unplanned": {
"title": "Ungeplante Aufträge",
"empty": "Keine ungeplanten Aufträge.",
"noTeam": "ohne Team",
"noDate": "ohne Termin",
"noLocation": "ohne Ortsangabe",
"suggestions": "Vorschläge"
},
"dialog": {
"title": "Auftrag einplanen",
"team": "Team",
"date": "Datum",
"time": "Beginn",
"noTime": "Ohne Uhrzeit (ganztägig)",
"duration": "Dauer in Minuten",
"durationHint": "Vorbelegt aus Auftrag bzw. Auftragsart ({duration}).",
"save": "Speichern",
"saving": "Speichert …",
"cancel": "Abbrechen",
"close": "Schließen"
},
"errors": {
"conflict": "Auftrag wurde zwischenzeitlich geändert. Die Ansicht wurde aktualisiert – bitte erneut einplanen.",
"forbidden": "Dafür fehlt Ihnen die Berechtigung.",
"not_found": "Auftrag nicht gefunden.",
"invalid": "Bitte die Eingaben prüfen.",
"planned_end_before_start": "Das Ende liegt vor dem Beginn.",
"not_schedulable": "Dieser Auftrag läuft bereits und wird hier nicht verschoben.",
"team_not_found": "Team nicht gefunden oder nicht aktiv.",
"not_editable": "Dieser Auftrag kann nicht mehr geändert werden.",
"network": "Keine Verbindung. Bitte erneut versuchen.",
"generic": "Speichern war nicht möglich. Bitte erneut versuchen."
},
"recommend": {
"title": "Vorschläge",
"sub": "Nur Vorschläge nach Luftlinie und Terminlage – die Zuweisung entscheiden Sie.",
"loading": "Vorschläge werden berechnet …",
"reason": "{distance} von {number} · {team} hat am {day} noch {free} frei",
"reasonTight": "{distance} von {number} · {team} am {day} knapp: {free} frei, {required} nötig",
"card": "{day} · {team} · {distance} von {number} · {free} frei",
"tight": "knapp",
"take": "Übernehmen",
"noCoordinates": "Adresse nicht verortet – die Verortung wurde angestoßen. Bitte später erneut öffnen.",
"addressNotFound": "Adresse nicht verortet – die Adresse wurde nicht gefunden. Bitte am Objekt prüfen.",
"notSchedulable": "Dieser Auftrag ist bereits in Ausführung.",
"none": "Kein Team hat in den nächsten {days} Tagen einen Auftrag im Umkreis von {radius} km mit freier Kapazität.",
"nearbyTitle": "In der Nähe, ebenfalls ungeplant",
"nearbyQuestion": "Diese Aufträge liegen im Umkreis von {radius} km – zusammen einplanen?",
"nearbyEmpty": "Keine weiteren ungeplanten Aufträge in der Nähe.",
"planTogether": "Gemeinsam einplanen",
"close": "Vorschläge schließen",
"failed": "Vorschläge konnten nicht geladen werden.",
"sectionTitle": "Einsatz-Vorschläge",
"openBoard": "Zur Plantafel"
},
"live": {
"title": "Live-Lage",
"sub": "Wer ist gerade in welchem Einsatz?",
"privacy": "Standort = Einsatzort des laufenden Auftrags, keine GPS-Ortung.",
"status": { "en_route": "Unterwegs", "working": "In Arbeit", "paused": "Pause", "free": "Frei" },
"freeHint": "kein laufender Einsatz",
"since": "seit {time}",
"updated": "Zuletzt aktualisiert: {time}",
"refresh": "Aktualisieren",
"refreshing": "Aktualisiert …",
"updateFailed": "Aktualisierung fehlgeschlagen – der letzte Stand wird angezeigt.",
"tabsLabel": "Darstellung",
"tabs": { "map": "Karte", "list": "Liste" },
"filterTeam": "Team",
"filterStatus": "Status",
"all": "Alle",
"noLocation": "ohne Ortsangabe",
"hiddenOrder": "Einsatz außerhalb Ihres Bereichs",
"next": "Heute noch geplant",
"nothingNext": "Heute nichts weiter geplant",
"attentionTitle": "Heute ohne Beginn",
"attentionEmpty": "Alle heutigen Aufträge sind begonnen oder später geplant.",
"reason": { "not_accepted": "Nicht angenommen", "overdue": "Beginn überschritten" },
"empty": "Keine Monteure in dieser Auswahl.",
"markerCount": "{count} Personen an diesem Ort",
"mapLabel": "Karte der laufenden Einsätze",
"mapLoading": "Karte wird geladen …",
"withoutLocation": "{count} laufende Einsätze ohne Ortsangabe (nur in der Liste)",
"noTeam": "ohne Team",
"crewsTitle": "Kolonnen",
"soloTitle": "Ohne Team",
"crewMembers": "Mitglieder",
"memberAt": "bei {number}",
"delayRisk": "Verzug droht · {percent} %",
"delayOverrun": "Dauer überschritten · {percent} %",
"worked": "{worked} von {planned} erfasst"
},
"capacity": {
"title": "Teamkapazität",
"sub": "Grundlage für Auslastung und Vorschläge",
"minutes": "Arbeitstag der Kolonne in Minuten",
"minutesHint": "Die Kolonne arbeitet gemeinsam: Kapazität je Arbeitstag, nicht je Person.",
"days": "Arbeitstage",
"save": "Speichern",
"saving": "Speichert …",
"errors": {
"invalid": "Bitte die Eingaben prüfen.",
"forbidden": "Dafür fehlt Ihnen die Berechtigung.",
"not_found": "Team nicht gefunden.",
"generic": "Speichern war nicht möglich."
}
},
"dashboardTile": {
"title": "Konflikte diese Woche",
"hint": "Überbuchung, Überschneidung, Doppelbuchung",
"todayTitle": "Planung heute",
"todayHint": "Teams im Einsatz · {conflicts} Konflikt(e) · {unplanned} ungeplante Aufträge"
}
}