feat(warteliste): Nachfrage/Warteliste für Interessenten

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 22:43:18 +02:00
parent 5e14124322
commit d25d2ee152
17 changed files with 2724 additions and 0 deletions

View File

@@ -416,6 +416,73 @@ export async function installMockApi(page: Page): Promise<MockDb> {
return json(route, 405)
}
// WAITLIST: Warteliste/Nachfrage — GET liefert ein BLANKES Array (kein Gridify-Envelope),
// POST/PUT/DELETE wie eine einfache Kollektion. Vor den generischen Kollektionen, weil
// die GET-Antwort kein paginiertes Envelope ist.
const wlm = path.match(/^\/waiting-list(?:\/([^/]+))?$/)
if (wlm) {
const wlId = wlm[1] ? decodeURIComponent(wlm[1]) : null
const ALLOWED = ['offen', 'erfuellt', 'storniert']
const normStatus = (s: unknown): string | null => {
if (s === undefined || s === null || String(s).trim() === '') return 'offen'
const v = String(s).trim()
return ALLOWED.includes(v) ? v : null
}
if (!wlId) {
if (method === 'GET') {
// Neueste Anfrage zuerst (requestedAt desc, dann createdAt desc).
const sorted = [...db.waitingList].sort((a, b) => {
const ar = String(a.requestedAt ?? '')
const br = String(b.requestedAt ?? '')
if (ar !== br) return ar < br ? 1 : -1
return String(a.createdAt ?? '') < String(b.createdAt ?? '') ? 1 : -1
})
return json(route, 200, sorted)
}
if (method === 'POST') {
const body = request.postDataJSON() as Record<string, unknown>
const status = normStatus(body.status)
if (status === null) return json(route, 400, 'Ungültiger Status.')
if (!body.contactId && (!body.contactName || String(body.contactName).trim() === ''))
return json(route, 400, 'Kontakt oder Name ist erforderlich.')
const created = {
id: newId('wl'),
contactId: body.contactId ?? null,
contactName: body.contactName ?? null,
wishColor: body.wishColor ?? null,
wishGender: body.wishGender ?? null,
requestedAt: body.requestedAt ?? null,
status,
note: body.note ?? null,
createdAt: new Date().toISOString(),
}
db.waitingList.push(created)
return json(route, 201, created)
}
return json(route, 405)
}
const wlIdx = db.waitingList.findIndex((r) => r.id === wlId)
if (method === 'GET') {
return wlIdx >= 0 ? json(route, 200, db.waitingList[wlIdx]) : json(route, 404, { title: 'Not Found' })
}
if (method === 'PUT') {
if (wlIdx < 0) return json(route, 404, { title: 'Not Found' })
const body = request.postDataJSON() as Record<string, unknown>
const status = normStatus(body.status)
if (status === null) return json(route, 400, 'Ungültiger Status.')
if (!body.contactId && (!body.contactName || String(body.contactName).trim() === ''))
return json(route, 400, 'Kontakt oder Name ist erforderlich.')
Object.assign(db.waitingList[wlIdx], { ...body, status })
return json(route, 200, db.waitingList[wlIdx])
}
if (method === 'DELETE') {
if (wlIdx < 0) return json(route, 404, { title: 'Not Found' })
db.waitingList.splice(wlIdx, 1)
return json(route, 204)
}
return json(route, 405)
}
// Generische Kollektionen: /<resource> und /<resource>/<id>
m = path.match(/^\/([a-z-]+)(?:\/([^/]+))?$/)
const col = m ? collections[m[1]] : undefined