import { Router } from 'express' import { config } from '../../config.js' import { Query, countDocuments, listDocuments } from '../../services/appwriteAdmin.js' import { anfrageAnsicht, entscheideAnfrage, leseShopConfig } from '../../services/shop/anfrage.js' import { listCustomerFeatureDocs } from '../../services/siteFeatures.js' /** * Webshop im Ticketsystem (Kunden-Reiter "Funktionen"): Stand je Projekt und Entscheidung ueber die Anfrage. * Eingehaengt in routes/team/index.js unter /api/team/shop, 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-shop]', err.message) return res.status(status).json({ error: err.message || 'Interner Serverfehler', ...(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-shop] ${was} nicht lesbar: ${err.message}`) return null } } /** stripeStatus (JSON-Text im shopConfig) als Objekt oder null, wenn leer oder nicht lesbar. */ function stripeStatus(text) { try { const roh = typeof text === 'string' && text ? JSON.parse(text) : null return roh !== null && typeof roh === 'object' && !Array.isArray(roh) ? roh : null } catch { return null } } async function projektStand(project, feature) { let shop = null if (feature) { try { shop = await leseShopConfig(feature.$id) } catch (err) { console.warn(`[team-shop] shopConfig ${feature.$id} nicht lesbar: ${err.message}`) } } const shopId = feature?.$id const [artikel, offeneBestellungen] = shopId ? await Promise.all([ zaehle('shopProducts', [Query.equal('shopId', shopId)], 'Artikelzahl'), zaehle('shopOrders', [Query.equal('shopId', shopId), Query.equal('status', 'bezahlt')], 'Bestellzahl'), ]) : [0, 0] return { projectId: project.$id, projectName: project.projectName || '', state: feature?.state || 'off', anfrage: shop ? anfrageAnsicht(shop) : null, stripe: { accountId: Boolean(shop?.stripeAccountId), status: stripeStatus(shop?.stripeStatus) }, geoeffnet: shop?.geoeffnet === true, zahlen: { artikel, offeneBestellungen }, } } // Uebersicht je Projekt des Kunden; archivierte Projekte nur, wenn dort ein Shop-Dokument 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 shopDocs = new Map(docs.filter((d) => d.featureKey === 'shop').map((d) => [d.projectId, d])) const sichtbar = projects.filter((p) => !p.archived || shopDocs.has(p.$id)) res.json({ projects: await Promise.all(sichtbar.map((p) => projektStand(p, shopDocs.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