82 lines
3.4 KiB
React
82 lines
3.4 KiB
React
import { useState, useMemo } from 'react'
|
|
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, 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, die Texte in src/lib/planboardView.js. */
|
|
|
|
export default function PlanboardPage() {
|
|
const [currentWeek, setCurrentWeek] = useState(startOfWeek(new Date(), { weekStartsOn: 1 }))
|
|
const { workorders, loading } = useWorkorders({ limit: 500 })
|
|
const { employees } = useEmployees()
|
|
|
|
const weekDays = Array.from({ length: 7 }, (_, i) => addDays(currentWeek, i))
|
|
const navigateWeek = (dir) => setCurrentWeek((prev) => addDays(prev, dir * 7))
|
|
const goToday = () => setCurrentWeek(startOfWeek(new Date(), { weekStartsOn: 1 }))
|
|
|
|
const byDay = useMemo(() => groupAppointmentsByDay(workorders || [], employees || []), [workorders, employees])
|
|
const weekCount = countInRange(byDay, weekDays)
|
|
|
|
return (
|
|
<div className="page">
|
|
<PageHeader
|
|
title="Planboard"
|
|
subtitle={planboardSubtitle({ weekStart: currentWeek, count: weekCount, loading })}
|
|
actions={
|
|
<>
|
|
<Button variant="quiet" aria-label="Vorherige Woche" onClick={() => navigateWeek(-1)}>←</Button>
|
|
<Button onClick={goToday}>Heute</Button>
|
|
<Button variant="quiet" aria-label="Nächste Woche" onClick={() => navigateWeek(1)}>→</Button>
|
|
</>
|
|
}
|
|
/>
|
|
|
|
<div className="week">
|
|
{weekDays.map((day, index) => {
|
|
const entries = byDay[dayKey(day)] || []
|
|
const today = isSameDay(day, new Date())
|
|
const labels = dayLabels(day)
|
|
return (
|
|
<div
|
|
key={day.toISOString()}
|
|
className={`day${today ? ' today' : ''}${index >= 5 ? ' weekend' : ''}`}
|
|
role="group"
|
|
aria-label={labels.full}
|
|
aria-current={today ? 'date' : undefined}
|
|
>
|
|
<div className="day-head">
|
|
<span className="wd">{labels.weekday}</span>
|
|
<span className="dn">{labels.number}</span>
|
|
</div>
|
|
|
|
{entries.length === 0 && <div className="none">Keine Termine</div>}
|
|
|
|
{entries.map((entry) => (
|
|
<Link
|
|
key={entry.id}
|
|
className={appointmentClass(entry.type)}
|
|
to={ticketPath(ticketRef({ woid: entry.woid, $id: entry.id }))}
|
|
title={appointmentTitle(entry)}
|
|
>
|
|
<div className="tm">{appointmentTimeLabel(entry)}</div>
|
|
<div className="tt">{entry.customerName || entry.topic}</div>
|
|
{entry.customerName && <div className="cu">{entry.topic}</div>}
|
|
<div className="cu">{appointmentOwnerLabel(entry)}</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|