import { Router } from 'express' import { config } from '../../config.js' import { Query, countDocuments, listDocuments } from '../../services/appwriteAdmin.js' import { anfrageAnsichtTeam, entscheideAnfrage } from '../../services/unterkunft/anfrageFunktion.js' import { ladeEinstellungen, listeUnterkuenfte } from '../../services/unterkunft/speicher.js' import { listCustomerFeatureDocs } from '../../services/siteFeatures.js' /** * Buchungskalender im Ticketsystem (Kunden-Reiter "Funktionen"): GET /api/team/unterkunft/:customerId (Stand je Projekt) und * POST /api/team/unterkunft/:customerId/anfrage (Entscheidung ueber die Anfrage der Funktion). Huelle wie routes/team/shop.js. * Eingehaengt in routes/team/index.js unter /unterkunft, dort hinter requireStaffJwt. */ const router = Router() const sendError = (res, err) => { const status = err.status >= 400 && err.status < 600 ? err.status : 500 if (status >= 500) console.error('[team-unterkunft]', err.message) return res.status(status).json({ error: status >= 500 ? 'Interner Serverfehler' : err.message, ...(err.body?.code ? { code: err.body.code } : {}) }) } const route = (fn) => (req, res) => fn(req, res).catch((err) => sendError(res, err)) const person = (req) => ({ name: req.session.adminName || req.session.name || '', email: req.session.email || '' }) /** Zaehler oder null, wenn die Abfrage scheitert (die Uebersicht bleibt lesbar). */ async function zaehle(collection, queries, was) { try { return await countDocuments(config.collections[collection], queries) } catch (err) { console.warn(`[team-unterkunft] ${was} nicht lesbar: ${err.message}`) return null } } /** stripeStatus (JSON-Objekt in ukEinstellungen, schon geparst) oder null. */ const stripeStatus = (roh) => (roh !== null && typeof roh === 'object' && !Array.isArray(roh) ? roh : null) async function projektStand(project, feature) { let einst = null let unterkuenfte = 0 if (feature) { try { einst = await ladeEinstellungen(feature.$id) } catch (err) { console.warn(`[team-unterkunft] Einstellungen ${feature.$id} nicht lesbar: ${err.message}`) } try { unterkuenfte = (await listeUnterkuenfte(feature.$id)).length } catch (err) { console.warn(`[team-unterkunft] Unterkuenfte ${feature.$id} nicht lesbar: ${err.message}`) unterkuenfte = null } } const offeneAnfragen = feature ? await zaehle('ukAnfragen', [Query.equal('featureId', feature.$id), Query.equal('status', 'neu')], 'Anfragenzahl') : 0 return { projectId: project.$id, name: project.projectName || '', state: feature?.state || 'off', anfrage: einst?.anfrageStatus ? anfrageAnsichtTeam(einst) : null, stripe: { accountId: Boolean(einst?.stripeAccountId), status: stripeStatus(einst?.stripeStatus) }, unterkuenfte, offeneAnfragen, } } // Uebersicht je Projekt des Kunden; archivierte Projekte nur, wenn dort ein Dokument der Funktion existiert router.get('/:customerId', route(async (req, res) => { const customerId = req.params.customerId const [docs, projects] = await Promise.all([ listCustomerFeatureDocs(customerId), listDocuments(config.collections.websiteProjects, [Query.equal('customerId', customerId), Query.orderDesc('$createdAt'), Query.limit(1000)]), ]) const ukDocs = new Map(docs.filter((d) => d.featureKey === 'unterkunft').map((d) => [d.projectId, d])) const sichtbar = projects.filter((p) => !p.archived || ukDocs.has(p.$id)) res.json({ projects: await Promise.all(sichtbar.map((p) => projektStand(p, ukDocs.get(p.$id)))) }) })) router.post('/:customerId/anfrage', route(async (req, res) => { const { projectId, entscheidung, begruendung } = req.body || {} const ergebnis = await entscheideAnfrage({ customerId: req.params.customerId, projectId, entscheidung, begruendung, person: person(req), }) res.json({ ok: true, anfrage: ergebnis.anfrage, ...(ergebnis.mailFehler ? { mailFehler: true } : {}) }) })) export default router