From a0149137ea2287ad5ca0d52fb5f5b69004a785d1 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 14:45:30 +0000 Subject: [PATCH] Planboard: Anzeigelogik fuer Kopfzeile, Tageskopf und Terminzeile mit Tests Co-Authored-By: Claude Sonnet 5.5 --- src/lib/planboardView.js | 41 +++++++++++++++++++++++++++++ tests/unit/planboardView.test.mjs | 43 +++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 src/lib/planboardView.js create mode 100644 tests/unit/planboardView.test.mjs diff --git a/src/lib/planboardView.js b/src/lib/planboardView.js new file mode 100644 index 0000000..d8dbb60 --- /dev/null +++ b/src/lib/planboardView.js @@ -0,0 +1,41 @@ +/* Planboard-Anzeigelogik: reine Texte und Klassen, die PlanboardPage braucht. + Die Terminlogik selbst (Datum lesen, nach Tag gruppieren) steckt in planboard.js. */ +import { format, getISOWeek, addDays } from 'date-fns' +import { de } from 'date-fns/locale' + +const NOBODY = 'niemand zugewiesen' + +// "Kalenderwoche 41 · 5. Oktober bis 11. Oktober · 13 Termine" +export function planboardSubtitle({ weekStart, count, loading = false }) { + const from = format(weekStart, 'd. MMMM', { locale: de }) + const to = format(addDays(weekStart, 6), 'd. MMMM', { locale: de }) + const rest = loading ? 'Wird geladen …' : `${count} ${count === 1 ? 'Termin' : 'Termine'}` + return `Kalenderwoche ${getISOWeek(weekStart)} · ${from} bis ${to} · ${rest}` +} + +export function dayLabels(day) { + return { + weekday: format(day, 'EEEE', { locale: de }), + number: format(day, 'dd.MM'), + full: format(day, 'EEEE, d. MMMM', { locale: de }), + } +} + +export function appointmentClass(type) { + return String(type || '').toLowerCase() === 'akquise' ? 'appt akquise' : 'appt' +} + +export function appointmentTimeLabel({ time, endTime }) { + if (!time) return 'ohne Uhrzeit' + return endTime ? `${time}–${endTime}` : time +} + +// Mit Ticketnummer "#100201 · KNSO", ohne Nummer nur der Mitarbeiter (kein leeres "#") +export function appointmentOwnerLabel({ woid, employeeName, employeeShort }) { + const owner = employeeName ? employeeShort || employeeName : NOBODY + return woid ? `#${woid} · ${owner}` : owner +} + +export function appointmentTitle({ woid }) { + return woid ? `Ticket #${woid} öffnen` : 'Ticket öffnen' +} diff --git a/tests/unit/planboardView.test.mjs b/tests/unit/planboardView.test.mjs new file mode 100644 index 0000000..01f81f1 --- /dev/null +++ b/tests/unit/planboardView.test.mjs @@ -0,0 +1,43 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { loadModule } from './load.mjs' + +const { planboardSubtitle, dayLabels, appointmentClass, appointmentTimeLabel, appointmentOwnerLabel, appointmentTitle } = + await loadModule('src/lib/planboardView.js') + +test('planboardSubtitle: Kalenderwoche, Zeitraum, Terminzahl', () => { + const mo = new Date(2026, 9, 5) + assert.equal(planboardSubtitle({ weekStart: mo, count: 13 }), 'Kalenderwoche 41 · 5. Oktober bis 11. Oktober · 13 Termine') + assert.equal(planboardSubtitle({ weekStart: mo, count: 1 }), 'Kalenderwoche 41 · 5. Oktober bis 11. Oktober · 1 Termin') + assert.equal(planboardSubtitle({ weekStart: mo, count: 0 }), 'Kalenderwoche 41 · 5. Oktober bis 11. Oktober · 0 Termine') + assert.equal(planboardSubtitle({ weekStart: mo, count: 0, loading: true }), 'Kalenderwoche 41 · 5. Oktober bis 11. Oktober · Wird geladen …') + assert.equal(planboardSubtitle({ weekStart: new Date(2026, 11, 28), count: 2 }), 'Kalenderwoche 53 · 28. Dezember bis 3. Januar · 2 Termine') + assert.equal(planboardSubtitle({ weekStart: new Date(2027, 0, 4), count: 2 }), 'Kalenderwoche 1 · 4. Januar bis 10. Januar · 2 Termine') +}) + +test('dayLabels: Wochentag, Datum, volles Datum', () => { + assert.deepEqual(dayLabels(new Date(2026, 9, 5)), { weekday: 'Montag', number: '05.10', full: 'Montag, 5. Oktober' }) + assert.deepEqual(dayLabels(new Date(2026, 9, 11)), { weekday: 'Sonntag', number: '11.10', full: 'Sonntag, 11. Oktober' }) +}) + +test('appointmentClass und appointmentTimeLabel', () => { + for (const t of ['Akquise', 'akquise', 'AKQUISE']) assert.equal(appointmentClass(t), 'appt akquise', t) + for (const t of ['Webpage', 'Project', 'Migration', '', undefined, null]) assert.equal(appointmentClass(t), 'appt', String(t)) + assert.equal(appointmentTimeLabel({ time: '08:00', endTime: '09:30' }), '08:00–09:30') + assert.equal(appointmentTimeLabel({ time: '08:00', endTime: '' }), '08:00') + assert.equal(appointmentTimeLabel({ time: '', endTime: '09:30' }), 'ohne Uhrzeit') + assert.equal(appointmentTimeLabel({ time: '', endTime: '' }), 'ohne Uhrzeit') +}) + +test('appointmentOwnerLabel: Nummer und Mitarbeiter', () => { + assert.equal(appointmentOwnerLabel({ woid: '100201', employeeName: 'Kenso Grimm', employeeShort: 'KNSO' }), '#100201 · KNSO') + assert.equal(appointmentOwnerLabel({ woid: '100201', employeeName: 'Ehemalige', employeeShort: '' }), '#100201 · Ehemalige') + assert.equal(appointmentOwnerLabel({ woid: '100201', employeeName: '', employeeShort: '' }), '#100201 · niemand zugewiesen') + assert.equal(appointmentOwnerLabel({ woid: '', employeeName: 'Kenso Grimm', employeeShort: 'KNSO' }), 'KNSO') + assert.equal(appointmentOwnerLabel({ woid: '', employeeName: '', employeeShort: '' }), 'niemand zugewiesen') +}) + +test('appointmentTitle: Tooltip mit und ohne woid', () => { + assert.equal(appointmentTitle({ woid: '100201' }), 'Ticket #100201 öffnen') + assert.equal(appointmentTitle({ woid: '' }), 'Ticket öffnen') +})