diff --git a/src/pages/PlanboardPage.jsx b/src/pages/PlanboardPage.jsx index 11ad858..354f431 100644 --- a/src/pages/PlanboardPage.jsx +++ b/src/pages/PlanboardPage.jsx @@ -1,26 +1,19 @@ import { useState, useMemo } from 'react' -import { useNavigate } from 'react-router-dom' -import { format, addDays, startOfWeek, isSameDay } from 'date-fns' -import { de } from 'date-fns/locale' -import { FaChevronLeft, FaChevronRight } from 'react-icons/fa6' -import { PageHeader, Card, Button } from '../components/ui' +import { Link } from 'react-router-dom' +import { addDays, startOfWeek, isSameDay } from 'date-fns' +import { PageHeader, Button } from '../components/ui' import { useWorkorders } from '../hooks/useWorkorders' import { useEmployees } from '../hooks/useEmployees' import { groupAppointmentsByDay, countInRange, dayKey } from '../lib/planboard' -import { ticketPath } from '../lib/ticketRoutes' +import { ticketPath, ticketRef } from '../lib/ticketRoutes' +import { + planboardSubtitle, dayLabels, appointmentClass, appointmentTimeLabel, appointmentOwnerLabel, appointmentTitle, +} from '../lib/planboardView' /* Termine haengen an den Tickets (startDate/startTime/endTime/assignedTo); - die Aufbereitung steckt in src/lib/planboard.js. */ - -// Farben nur ueber Tokens (P3 baut die Seite um) -const TYPE_COLORS = { - akquise: { bg: 'var(--appt-akquise)', fg: 'var(--orange-fg)' }, - webpage: { bg: 'var(--bg-hover)', fg: 'var(--text-2)' }, - project: { bg: 'var(--bg-hover)', fg: 'var(--text-2)' }, -} + die Aufbereitung steckt in src/lib/planboard.js, die Texte in src/lib/planboardView.js. */ export default function PlanboardPage() { - const navigate = useNavigate() const [currentWeek, setCurrentWeek] = useState(startOfWeek(new Date(), { weekStartsOn: 1 })) const { workorders, loading } = useWorkorders({ limit: 500 }) const { employees } = useEmployees() @@ -36,87 +29,52 @@ export default function PlanboardPage() {
- - - {format(currentWeek, 'dd.MM.yyyy')} - {format(addDays(currentWeek, 6), 'dd.MM.yyyy')} - - - -
+ <> + + + + } /> -
-
- {weekDays.map((day) => { - const entries = byDay[dayKey(day)] || [] - const today = isSameDay(day, new Date()) - return ( - -
- {format(day, 'EEEE', { locale: de })} - {format(day, 'dd.MM')} -
+
+ {weekDays.map((day, index) => { + const entries = byDay[dayKey(day)] || [] + const today = isSameDay(day, new Date()) + const labels = dayLabels(day) + return ( +
= 5 ? ' weekend' : ''}`} + role="group" + aria-label={labels.full} + aria-current={today ? 'date' : undefined} + > +
+ {labels.weekday} + {labels.number} +
-
- {entries.length === 0 && ( -

Keine Termine

- )} + {entries.length === 0 &&
Keine Termine
} - {entries.map((entry) => { - const color = TYPE_COLORS[String(entry.type).toLowerCase()] - || { bg: 'var(--bg-subtle)', fg: 'var(--line-strong)' } - return ( - - ) - })} -
- - ) - })} -
+ {entries.map((entry) => ( + +
{appointmentTimeLabel(entry)}
+
{entry.customerName || entry.topic}
+ {entry.customerName &&
{entry.topic}
} +
{appointmentOwnerLabel(entry)}
+ + ))} +
+ ) + })}
) diff --git a/src/styles/global.css b/src/styles/global.css index 734708c..5c52950 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -557,9 +557,6 @@ a.list-row:hover .cell-title { color: var(--accent-ink); } .wrap { flex-wrap: wrap; } .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; } .text-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -/* Planboard (bis P3): die 7-Spalten-Woche scrollt waagerecht im Container, nicht die Seite */ -.planboard-scroll { overflow-x: auto; } - /* Neues Ticket: Datum + Uhrzeit nebeneinander (heller Kalender, kein color-scheme: dark) */ .datetime-pair { display: flex; gap: 8px; } .datetime-pair .form-control, .datetime-pair .input { flex: 1; min-width: 0; } @@ -1012,6 +1009,35 @@ button.link { font-weight: 500; } /* font: inherit setzt sonst das Gewicht von . } /* ===== ENDE Tickets (P2) ===== */ +/* ===== Planboard (P3) ===== */ +/* Woche ohne Karten: sieben Tagesspalten mit feinen Trennlinien, Wochenende hell abgesetzt, heute als schwarze Datumspille */ +.week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); border-top: 1px solid var(--line); min-height: 420px; } +.day { border-left: 1px solid var(--line); padding: 10px 8px 16px; min-width: 0; } +.day:first-child { border-left: 0; padding-left: 0; } +.day-head { display: flex; align-items: baseline; gap: 6px; margin-bottom: 12px; padding-left: 4px; flex-wrap: wrap; row-gap: 0; } +.day-head .wd { font-size: 12px; color: var(--text-2); font-weight: 500; text-transform: uppercase; letter-spacing: 0.04em; } +.day-head .dn { font-size: 18px; font-weight: 600; font-variant-numeric: tabular-nums; } +.day.today .wd { color: var(--text); } +.day.today .dn { background: var(--accent); color: #fff; border-radius: 99px; padding: 0 8px; } +.day.weekend { background: var(--bg-subtle); } +.appt { display: block; padding: 8px 10px; border-radius: var(--radius); background: var(--bg-hover); margin-bottom: 6px; font-size: 13px; line-height: 18px; overflow-wrap: anywhere; } +.appt:hover { background: var(--appt-hover); } +.appt .tm { font-weight: 600; font-variant-numeric: tabular-nums; } +.appt .tt { color: var(--text); margin-top: 2px; } +.appt .cu { color: var(--text-2); margin-top: 2px; } +.appt.akquise { background: var(--appt-akquise); } +.appt.akquise:hover { background: var(--appt-akquise-hover); } +.day.weekend .appt:not(.akquise) { background: var(--bg); } +.day.weekend .appt:not(.akquise):hover { background: var(--appt-hover); } +.day .none { font-size: 12px; color: var(--text-3); padding-left: 4px; } +@media (max-width: 860px) { + .week { grid-template-columns: 1fr; border-top: 0; min-height: 0; } + .day { border-left: 0; padding: 12px 0; border-top: 1px solid var(--line); } + .day.weekend { background: none; } + .day.weekend .appt:not(.akquise) { background: var(--bg-hover); } +} +/* ===== ENDE Planboard (P3) ===== */ + /* ===== KOMPAT (bis P10) ===== */ /* Uebergangsschicht: alte Klassen im neuen Look, damit Seiten und Dialoge, die noch nicht umgebaut sind, weiss, kastenlos */ /* und lesbar bleiben. Steht am Dateiende und gewinnt dort gegen die Altregeln und gegen die anderen Stylesheets (global.css */ @@ -1122,7 +1148,7 @@ div.row { display: flex; flex-wrap: wrap; margin: 0 -8px; } .fin-link:hover, .legal-files a:hover, .credit-cols a:hover, .area-rows a:hover { text-decoration-color: currentColor; } /* Abschnitte (Card =
) als Spalten nebeneinander: der Abstand .page section + section gilt nur fuer untereinander stehende Abschnitte */ -.planboard-scroll section + section, .legal-grid > section + section, .fin-cols > section + section, +.legal-grid > section + section, .fin-cols > section + section, .page div[style*="display: grid"] > section + section { margin-top: 0; } /* Altformulare mit (Kunde, Projekt zuweisen, Neuer Lead): Zeilenabstand wie frueher; neue Container bestimmen ihren Abstand selbst */