Workers P3-Endprüfung W5: Reiter Erledigt zeigt Ergebnis, Person und Zeitpunkt

Abgeschlossene Zeilen tragen jetzt ein Badge (Gesendet grün, Verworfen/Erledigt neutral) und die
Zeile "Gesendet von Anna · vor 2 Std." aus doneBy/doneAt statt des Alters ab Anlage. Menschen
(mensch:<id>) werden über die Mitarbeiterliste aufgelöst, ein Worker wie max über das
Rollen-Register als "Max (automatisch)" und nie als "Kollege". Offene Zeilen bleiben unverändert.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-18 10:44:21 +00:00
1 parent 62e7994508
commit 4ec3022df4
4 files changed
+116 -1

No files matched your search

+51
View File
@@ -185,6 +185,57 @@ test('rowModel: Mail-Link nur bei mail_antwort mit gültiger Adresse, Betreff "R
assert.equal(rowModel({ ...base, kind: 'plattform_antwort', prospect: { email: 'x@verein.example', name: 'Verein' } }, ctx).mailtoHref, '') // Plattform-Antwort geht über den Quell-Link
})
// Endprüfung P3 (W5): der Reiter „Erledigt“ zeigt, was aus einem Eintrag wurde, wer es tat und wann
test('rowModel: abgeschlossene Einträge zeigen Ergebnis, Person und Zeitpunkt; drei Status ergeben drei Bezeichnungen', () => {
const base = { $id: 'a', kind: 'plattform_antwort', title: 'T', draft: 'D', createdAt: iso(-4 * DAY), doneBy: 'mensch:u2', doneAt: iso(-2 * HOUR) }
const ctx = { me: { id: 'u1', name: 'Justin' }, nowMs: NOW, employees: [{ userId: 'u2', displayName: 'Anna' }] }
const out = {}
for (const status of ['gesendet', 'verworfen', 'erledigt']) {
const m = rowModel({ ...base, status }, ctx)
out[status] = [m.statusLabel, m.statusTone, m.doneByLabel, m.doneAgeLabel, m.doneLine]
}
assert.deepEqual(out, {
gesendet: ['Gesendet', 'green', 'von Anna', 'vor 2 Std.', 'Gesendet von Anna · vor 2 Std.'],
verworfen: ['Verworfen', 'muted', 'von Anna', 'vor 2 Std.', 'Verworfen von Anna · vor 2 Std.'],
erledigt: ['Erledigt', 'muted', 'von Anna', 'vor 2 Std.', 'Erledigt von Anna · vor 2 Std.'],
})
assert.equal(new Set(Object.values(out).map((v) => v[0])).size, 3)
// das Alter ab Anlage bleibt im Modell (Alter-Feld unverändert), die Zeile zeigt aber die Abschlusszeit
assert.equal(rowModel({ ...base, status: 'gesendet' }, ctx).ageLabel, 'vor 4 Tagen')
})
test('rowModel: doneBy "mensch:<id>" über die Mitarbeiterliste, ein Worker wie "max" wird nie zu "Kollege"', () => {
const base = { $id: 'a', kind: 'mail_antwort', status: 'gesendet', title: 'T', createdAt: iso(-DAY), doneAt: iso(-5 * MIN) }
const ctx = { me: null, nowMs: NOW, employees: [{ userId: 'u1', displayName: 'Justin' }] }
assert.equal(rowModel({ ...base, doneBy: 'mensch:u1' }, ctx).doneByLabel, 'von Justin')
assert.equal(rowModel({ ...base, doneBy: 'mensch:u9' }, ctx).doneByLabel, 'von Kollege') // unbekannte Person
assert.equal(rowModel({ ...base, doneBy: 'mensch:u1' }, { ...ctx, employees: [] }).doneByLabel, 'von Kollege')
assert.equal(rowModel({ ...base, doneBy: 'max' }, ctx).doneByLabel, 'von Max (automatisch)') // P4: Max sendet automatisch
assert.equal(rowModel({ ...base, doneBy: 'clara' }, ctx).doneLine, 'Gesendet von Clara (automatisch) · vor 5 Min.')
assert.ok(!rowModel({ ...base, doneBy: 'max' }, ctx).doneLine.includes('Kollege'))
// unbekannte Kennungen (auch eine, die wie ein Objekt-Schlüssel aussieht) sind weder Mensch noch Worker
for (const by of ['system', 'constructor', '__proto__', 'u1']) assert.equal(rowModel({ ...base, doneBy: by }, ctx).doneByLabel, 'automatisch', by)
// ohne doneBy kein Satzteil
for (const by of [undefined, null, '', 7]) assert.deepEqual([rowModel({ ...base, doneBy: by }, ctx).doneByLabel, rowModel({ ...base, doneBy: by }, ctx).doneLine], ['', 'Gesendet · vor 5 Min.'], String(by))
})
test('rowModel: fehlendes oder kaputtes doneAt gibt keine Zeitangabe, offene Zeilen bleiben unverändert', () => {
const base = { $id: 'a', kind: 'plattform_antwort', status: 'verworfen', title: 'T', createdAt: iso(-DAY), doneBy: 'mensch:u1' }
const ctx = { me: null, nowMs: NOW, employees: [{ userId: 'u1', displayName: 'Justin' }] }
for (const doneAt of [undefined, null, '', 'gestern']) {
const m = rowModel({ ...base, doneAt }, ctx)
assert.deepEqual([m.doneAgeLabel, m.doneLine], ['', 'Verworfen von Justin'], String(doneAt))
}
// offene, übernommene und veraltete Einträge: keine Abschlussfelder, auch wenn doneBy/doneAt (aus einer Altlast) gesetzt wären
for (const status of ['offen', 'in_arbeit', 'veraltet']) {
const m = rowModel({ ...base, status, doneAt: iso(-HOUR), claimedBy: 'mensch:u1' }, ctx)
assert.deepEqual([m.statusLabel, m.statusTone, m.doneByLabel, m.doneAgeLabel, m.doneLine], ['', '', '', '', ''], status)
assert.equal(m.ageLabel, 'vor 1 Tag')
}
// unbekannter Status (neuer Dienst) und Objekt-Schlüssel als Status
for (const status of ['unbekannt', 'constructor', undefined]) assert.equal(rowModel({ ...base, status }, ctx).statusLabel, '', String(status))
})
test('employeeName: "mensch:"-Präfix weg, Nachschlagen über userId, sonst "Kollege"', () => {
const employees = [{ userId: 'u2', displayName: 'Kenso' }, { userId: 'u3', displayName: '' }, { userId: 'u4' }]
assert.equal(employeeName('mensch:u2', employees), 'Kenso')