sortiereAuftraege stellt unter den laufenden Aufträgen die mit Rückfrage (braucheInfo) nach vorn, danach wie bisher die jüngste Aktivität, dann die Reihenfolge des Servers. Erledigte und Stornierte bleiben dahinter. Tests: Reihenfolge mit Rückfragen und Gruppierung (die Rückfrage mit der ältesten Aktivität bleibt sichtbar). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
166 lines
11 KiB
JavaScript
166 lines
11 KiB
JavaScript
// „Was wir zuletzt erledigt haben“ als Zeitleiste (Variante C, Pilot-Feedback 17.10.): reine Logik (Fortschritt, Reihenfolge, Gruppierung, Kürzen).
|
|
// Antwortform von GET /api/activity laut server/routes/customerData.js und services/customerProjectView.js. Der DOM-Bau steht in knAuftragsZeitleisteDom.test.js.
|
|
import { test } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import {
|
|
MAX_AUFTRAEGE, MAX_ZEILEN, STUFEN, fortschritt, braucheInfo, letzteAktivitaet, sortiereAuftraege, gruppiereAuftraege, kuerze,
|
|
eintragsKurztext, datumKurz, zeitleistenZeilen,
|
|
} from '../public/neu/auftrags-zeitleiste.js'
|
|
import { AUFTRAG_SCHRITTE } from '../public/neu/website-details.js'
|
|
|
|
const JETZT = new Date('2026-10-16T09:00:00Z')
|
|
const ST = {
|
|
neu: { label: 'Eingegangen', tone: 'new', step: 1 },
|
|
arbeit: { label: 'In Arbeit', tone: 'active', step: 2 },
|
|
wartet: { label: 'Wartet auf deine Rückmeldung', tone: 'waiting', step: 2 },
|
|
pruefung: { label: 'Wird geprüft', tone: 'review', step: 3 },
|
|
fertig: { label: 'Erledigt', tone: 'done', step: 4 },
|
|
pause: { label: 'Pausiert', tone: 'paused', step: 0 },
|
|
storniert: { label: 'Storniert', tone: 'stopped', step: 0 },
|
|
}
|
|
const auftrag = (nr, st, createdAt, entries = [], topic = `Thema ${nr}`) => ({ number: String(nr), topic, status: ST[st], createdAt, entries })
|
|
const eintrag = (date, text, over = {}) => ({ kind: 'online', label: 'Online erledigt', date, minutes: 15, person: 'Lena Vogel', text, ...over })
|
|
const nummern = (liste) => liste.map((a) => a.number)
|
|
|
|
/* ------------------------------------------------------------------ reine Logik --- */
|
|
|
|
test('Stufen: dieselben vier Namen wie der Fortschritt der Projektdetails', () => {
|
|
assert.deepEqual(STUFEN, ['Eingegangen', 'In Arbeit', 'Prüfung', 'Erledigt'])
|
|
assert.deepEqual(STUFEN, AUFTRAG_SCHRITTE)
|
|
})
|
|
|
|
test('Fortschritt: erreichte Abschnitte bis zum aktuellen, Stufe 0 ist leer', () => {
|
|
const stand = (step) => fortschritt({ step }).map((s) => [s.erreicht, s.aktuell])
|
|
assert.deepEqual(stand(1), [[true, true], [false, false], [false, false], [false, false]])
|
|
assert.deepEqual(stand(2), [[true, false], [true, true], [false, false], [false, false]])
|
|
assert.deepEqual(stand(3), [[true, false], [true, false], [true, true], [false, false]])
|
|
assert.deepEqual(stand(4), [[true, false], [true, false], [true, false], [true, true]])
|
|
assert.deepEqual(fortschritt({ step: 4 }).map((s) => s.name), STUFEN)
|
|
for (const leer of [0, undefined, null, 'x', -1, 9]) assert.deepEqual(fortschritt({ step: leer }).map((s) => s.erreicht), [false, false, false, false], `step ${String(leer)}`)
|
|
assert.deepEqual(fortschritt(null).map((s) => s.erreicht), [false, false, false, false])
|
|
})
|
|
|
|
test('Info vom Kunden nötig: genau der Ton „waiting“ (Status „awaiting“ des Servers)', () => {
|
|
assert.equal(braucheInfo(auftrag(1, 'wartet', '2026-10-01T00:00:00Z')), true)
|
|
for (const st of ['neu', 'arbeit', 'pruefung', 'fertig', 'pause', 'storniert']) assert.equal(braucheInfo(auftrag(1, st, '2026-10-01T00:00:00Z')), false, st)
|
|
assert.equal(braucheInfo({}), false); assert.equal(braucheInfo(null), false)
|
|
})
|
|
|
|
test('Letzte Aktivität: der jüngste Eintrag oder die Anlage des Auftrags, je nachdem, was später ist', () => {
|
|
const a = auftrag(1, 'arbeit', '2026-10-01T10:00:00Z', [eintrag('2026-10-05', 'x'), eintrag('2026-10-09', 'y'), eintrag('', 'ohne Datum')])
|
|
assert.equal(letzteAktivitaet(a), Date.parse('2026-10-09T00:00:00Z'))
|
|
assert.equal(letzteAktivitaet(auftrag(2, 'arbeit', '2026-10-12T10:00:00Z', [eintrag('2026-10-05', 'x')])), Date.parse('2026-10-12T10:00:00Z'))
|
|
assert.equal(letzteAktivitaet(auftrag(3, 'arbeit', '', [])), 0)
|
|
assert.equal(letzteAktivitaet({ entries: 'kaputt', createdAt: 'nie' }), 0)
|
|
})
|
|
|
|
test('Reihenfolge: laufende zuerst (letzte Aktivität), dann erledigte (neueste zuerst), Storniertes ganz hinten', () => {
|
|
const liste = [
|
|
auftrag(1, 'fertig', '2026-09-01T00:00:00Z', [eintrag('2026-10-14', 'fertig, aber jüngster Eintrag')]),
|
|
auftrag(2, 'arbeit', '2026-10-01T00:00:00Z', [eintrag('2026-10-03', 'alt')]),
|
|
auftrag(3, 'storniert', '2026-10-15T00:00:00Z'),
|
|
auftrag(4, 'wartet', '2026-10-02T00:00:00Z', [eintrag('2026-10-13', 'neuer')]),
|
|
auftrag(5, 'fertig', '2026-09-20T00:00:00Z', [eintrag('2026-09-25', 'älter')]),
|
|
auftrag(6, 'pause', '2026-10-04T00:00:00Z'),
|
|
auftrag(7, 'pruefung', '2026-10-05T00:00:00Z'),
|
|
auftrag(8, 'neu', '2026-10-12T00:00:00Z'),
|
|
]
|
|
assert.deepEqual(nummern(sortiereAuftraege(liste)), ['4', '8', '7', '6', '2', '1', '5', '3'])
|
|
assert.deepEqual(nummern(liste), ['1', '2', '3', '4', '5', '6', '7', '8'], 'die Eingabe bleibt unverändert')
|
|
})
|
|
|
|
test('Reihenfolge: Aufträge mit Rückfrage stehen vor den übrigen laufenden (auch wenn sie am ältesten sind), unter sich nach Aktivität; erledigte bleiben dahinter', () => {
|
|
const liste = [
|
|
auftrag(1, 'arbeit', '2026-10-14T00:00:00Z'),
|
|
auftrag(2, 'wartet', '2026-09-01T00:00:00Z', [eintrag('2026-09-05', 'alt')]),
|
|
auftrag(3, 'neu', '2026-10-13T00:00:00Z'),
|
|
auftrag(4, 'fertig', '2026-10-15T00:00:00Z'),
|
|
auftrag(5, 'wartet', '2026-09-20T00:00:00Z', [eintrag('2026-10-02', 'etwas neuer')]),
|
|
auftrag(6, 'pause', '2026-10-12T00:00:00Z'),
|
|
auftrag(7, 'pruefung', '2026-10-11T00:00:00Z'),
|
|
auftrag(8, 'storniert', '2026-10-16T00:00:00Z'),
|
|
auftrag(9, 'arbeit', '2026-10-10T00:00:00Z'),
|
|
]
|
|
assert.deepEqual(nummern(sortiereAuftraege(liste)), ['5', '2', '1', '3', '6', '7', '9', '4', '8'])
|
|
// ohne Rückfrage ändert sich nichts an der alten Regel (Aktivität, dann Serverreihenfolge)
|
|
assert.deepEqual(nummern(sortiereAuftraege(liste.filter((a) => !braucheInfo(a)))), ['1', '3', '6', '7', '9', '4', '8'])
|
|
})
|
|
|
|
test('Gruppierung: eine Rückfrage verschwindet nie hinter „Ältere Aufträge anzeigen“, auch wenn sie die älteste Aktivität hat', () => {
|
|
const laufend = Array.from({ length: 6 }, (_, i) => auftrag(200 + i, 'arbeit', `2026-10-0${i + 3}T00:00:00Z`))
|
|
const rueckfrage = auftrag(300, 'wartet', '2026-08-01T00:00:00Z', [eintrag('2026-08-02', 'sehr alt')])
|
|
const zweite = auftrag(301, 'wartet', '2026-07-01T00:00:00Z')
|
|
const g = gruppiereAuftraege([...laufend, rueckfrage, zweite])
|
|
assert.deepEqual(nummern(g.sichtbar.slice(0, 2)), ['300', '301'])
|
|
assert.equal(g.sichtbar.length, 5)
|
|
assert.ok(nummern(g.sichtbar).includes('300') && nummern(g.sichtbar).includes('301'))
|
|
assert.ok(g.aeltere.every((a) => !braucheInfo(a)))
|
|
// mehr Rückfragen als Plätze: die mit der jüngsten Aktivität stehen vorn, der Rest gehört in „älter“ (kein Auftrag geht verloren)
|
|
const viele = Array.from({ length: 7 }, (_, i) => auftrag(400 + i, 'wartet', `2026-10-0${i + 1}T00:00:00Z`))
|
|
const g2 = gruppiereAuftraege(viele)
|
|
assert.deepEqual(nummern(g2.sichtbar), ['406', '405', '404', '403', '402'])
|
|
assert.deepEqual(nummern(g2.aeltere), ['401', '400'])
|
|
})
|
|
|
|
test('Reihenfolge: bei gleicher Aktivität bleibt die Reihenfolge des Servers', () => {
|
|
const liste = ['9', '8', '7'].map((n) => auftrag(n, 'arbeit', '2026-10-10T00:00:00Z'))
|
|
assert.deepEqual(nummern(sortiereAuftraege(liste)), ['9', '8', '7'])
|
|
})
|
|
|
|
test('Gruppierung: höchstens fünf Aufträge sichtbar, der Rest ist „älter“', () => {
|
|
assert.equal(MAX_AUFTRAEGE, 5)
|
|
const liste = Array.from({ length: 7 }, (_, i) => auftrag(100 + i, 'arbeit', `2026-10-0${i + 1}T00:00:00Z`))
|
|
const g = gruppiereAuftraege(liste)
|
|
assert.deepEqual(nummern(g.sichtbar), ['106', '105', '104', '103', '102']); assert.deepEqual(nummern(g.aeltere), ['101', '100'])
|
|
const fuenf = gruppiereAuftraege(liste.slice(0, 5)); assert.equal(fuenf.sichtbar.length, 5); assert.deepEqual(fuenf.aeltere, [])
|
|
assert.deepEqual(gruppiereAuftraege([]), { sichtbar: [], aeltere: [] }); assert.deepEqual(gruppiereAuftraege(undefined), { sichtbar: [], aeltere: [] })
|
|
assert.equal(gruppiereAuftraege(liste, 2).aeltere.length, 5)
|
|
})
|
|
|
|
test('Gruppierung: ein erledigter Auftrag rutscht hinter laufende, auch wenn er neuer ist', () => {
|
|
const liste = [auftrag(1, 'fertig', '2026-10-15T00:00:00Z'), ...Array.from({ length: 5 }, (_, i) => auftrag(10 + i, 'arbeit', `2026-09-0${i + 1}T00:00:00Z`))]
|
|
const g = gruppiereAuftraege(liste)
|
|
assert.deepEqual(nummern(g.aeltere), ['1'])
|
|
})
|
|
|
|
test('Kürzen: die ersten n sichtbar, der Rest getrennt; Eingabe bleibt', () => {
|
|
assert.equal(MAX_ZEILEN, 3)
|
|
const a = [1, 2, 3, 4, 5]
|
|
assert.deepEqual(kuerze(a, 3), { sichtbar: [1, 2, 3], rest: [4, 5] })
|
|
assert.deepEqual(kuerze([1, 2], 3), { sichtbar: [1, 2], rest: [] })
|
|
assert.deepEqual(kuerze(undefined, 3), { sichtbar: [], rest: [] })
|
|
assert.deepEqual(a, [1, 2, 3, 4, 5])
|
|
})
|
|
|
|
test('Datum: TT.MM., mit Jahr nur wenn es nicht das laufende ist', () => {
|
|
assert.equal(datumKurz('2026-10-02', JETZT), '02.10.')
|
|
assert.equal(datumKurz('2025-12-24', JETZT), '24.12.25')
|
|
assert.equal(datumKurz('2026-10-02T08:00:00.000Z', JETZT), '02.10.')
|
|
for (const kaputt of ['', null, undefined, 'gestern', '2026-13']) assert.equal(datumKurz(kaputt, JETZT), '', String(kaputt))
|
|
})
|
|
|
|
test('Kurztext: Änderungen, freier Text oder Art der Arbeit; nie lang', () => {
|
|
assert.equal(eintragsKurztext({ kind: 'update', label: 'Website aktualisiert', changes: ['Menü angepasst'] }), 'Menü angepasst')
|
|
assert.equal(eintragsKurztext({ kind: 'update', label: 'Website aktualisiert', changes: ['Menü angepasst', 'Bilder getauscht'] }), 'Menü angepasst und 1 weitere Änderung')
|
|
assert.equal(eintragsKurztext({ kind: 'update', label: 'Website aktualisiert', changes: ['A', 'B', 'C', 'D'] }), 'A und 3 weitere Änderungen')
|
|
assert.equal(eintragsKurztext({ kind: 'online', label: 'Online erledigt', text: 'Neue Öffnungszeiten eingetragen.' }), 'Neue Öffnungszeiten eingetragen.')
|
|
assert.equal(eintragsKurztext({ kind: 'onsite', label: 'Vor Ort bei dir', text: '' }), 'Vor Ort bei dir')
|
|
assert.equal(eintragsKurztext({ kind: 'work', label: '', text: '' }), 'Arbeit am Projekt')
|
|
assert.equal(eintragsKurztext({ kind: 'online', label: 'Online erledigt', text: 'Erste Zeile\n\nZweite Zeile mit Lücken' }), 'Erste Zeile')
|
|
assert.equal(eintragsKurztext({ kind: 'online', label: 'x', text: ' viel Leerraum ' }), 'viel Leerraum')
|
|
const lang = eintragsKurztext({ kind: 'online', label: 'x', text: 'Wort '.repeat(60) })
|
|
assert.ok(lang.length <= 91 && lang.endsWith('…') && !lang.includes(' '), lang)
|
|
assert.equal(eintragsKurztext(null), 'Arbeit am Projekt')
|
|
})
|
|
|
|
test('Zeitleiste: neueste zuerst, Datum TT.MM., ohne Datum ganz unten, keine Namen und Minuten', () => {
|
|
const a = auftrag(1, 'arbeit', '2026-10-01T00:00:00Z', [
|
|
eintrag('2026-10-03', 'Alt'), eintrag('2026-10-12', 'Neu', { person: 'Kenso', minutes: 90 }), eintrag('', 'Ohne Datum'), eintrag('2026-10-07', 'Mitte'),
|
|
])
|
|
const zeilen = zeitleistenZeilen(a, JETZT)
|
|
assert.deepEqual(zeilen.map((z) => [z.datum, z.text]), [['12.10.', 'Neu'], ['07.10.', 'Mitte'], ['03.10.', 'Alt'], ['', 'Ohne Datum']])
|
|
assert.deepEqual(Object.keys(zeilen[0]).sort(), ['datum', 'iso', 'text'])
|
|
assert.equal(zeilen[0].iso, '2026-10-12')
|
|
assert.deepEqual(zeitleistenZeilen({ entries: null }, JETZT), []); assert.deepEqual(zeitleistenZeilen(null, JETZT), [])
|
|
})
|