INBOX-1: Anfragen-Posteingang UI - Liste (Status-Filter, Sync-Button mit MailNotConfigured-Hinweis), Detail (Triage/Zuordnen/Verwerfen, KI-Entwurf mit AiKeyMissing-Hinweis, bestaetigter Versand) + nav + 6 e2e-Specs (Mock um /api/requests erweitert)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 11:23:41 +02:00
parent 36454f3747
commit aaaf5b6fb7
11 changed files with 844 additions and 1 deletions

View File

@@ -135,6 +135,53 @@ export async function installMockApi(page: Page): Promise<MockDb> {
if (method === 'PUT') return json(route, 204)
}
// ── INBOX-1: Anfragen-Posteingang (vor den generischen Kollektionen) ──
if (path === '/requests/sync' && method === 'POST') {
return json(route, 200, db.mailConfigured ? { imported: 0, error: null } : { imported: 0, error: 'MailNotConfigured' })
}
let rm = path.match(/^\/requests\/([^/]+)\/draft$/)
if (rm && method === 'POST') {
if (!db.aiConfigured) {
return json(route, 503, { code: 'AiKeyMissing', message: 'AI nicht konfiguriert' })
}
const r = db.requests.find((x) => x.id === rm![1])
if (!r) return json(route, 404, { title: 'Not Found' })
r.draftReply = `Hallo ${r.fromName ?? ''},\n\nvielen Dank für deine Anfrage!\n\nViele Grüße`
return json(route, 200, r)
}
rm = path.match(/^\/requests\/([^/]+)\/send$/)
if (rm && method === 'POST') {
if (!db.mailConfigured) {
return json(route, 503, { title: 'MailNotConfigured', detail: 'Gmail ist noch nicht konfiguriert.' })
}
const r = db.requests.find((x) => x.id === rm![1])
if (!r) return json(route, 404, { title: 'Not Found' })
r.status = 'Answered'
r.answeredAt = '2026-06-06T10:00:00Z'
return json(route, 200, r)
}
rm = path.match(/^\/requests(?:\/([^/]+))?$/)
if (rm) {
const reqId = rm[1] ? decodeURIComponent(rm[1]) : null
if (!reqId && method === 'GET') {
return json(route, 200, pagedResponse(db.requests as unknown as Row[], url))
}
const r = db.requests.find((x) => x.id === reqId)
if (!r) return json(route, 404, { title: 'Not Found' })
if (method === 'GET') return json(route, 200, r)
if (method === 'PUT') {
// Triage-Vertrag des Backends: assignedContactId wird IMMER übernommen.
const body = request.postDataJSON() as { status?: string | null; assignedContactId?: string | null }
r.assignedContactId = body.assignedContactId ?? null
if (body.status) {
r.status = body.status as typeof r.status
if (body.status === 'Answered') r.answeredAt ??= '2026-06-06T10:00:00Z'
}
return json(route, 204)
}
return json(route, 405)
}
// SEARCH-2b: distinct Herkunft (originBreeder) values, sorted — vor der
// generischen /gerbils/:id-Route abfangen.
if (path === '/gerbils/breeders' && method === 'GET') {