import { Router, Response } from 'express'; import { PrismaClient } from '@prisma/client'; import { PaymentService } from '../modules/banking/paymentService'; import { AuthedRequest, requireAuth } from '../middleware/auth'; const prisma = new PrismaClient(); const paymentService = new PaymentService(prisma); /** Router für das Cockpit-Dashboard (aggregierte Übersichtsdaten). */ export const cockpitRouter = Router(); const TICKET_SELECT = { id: true, title: true, category: true, priority: true, status: true, createdAt: true, imageUrls: true, room: { select: { roomNumber: true } }, creator: { select: { id: true, fullName: true } }, craftsman: { select: { id: true, name: true, trade: true, phone: true } }, } as const; // Erfordert Login: sensible Miet- und Kontaktdaten dürfen nicht öffentlich sein. // // Berechtigungsregel: Mieter dürfen NUR ihre eigenen Miet-/Vertragsdaten und // Tickets sehen (plus Gemeinschaftsbereich-Tickets ohne Zimmerzuordnung) — // niemals die Miet-/Kontaktdaten oder privaten Zimmer-Tickets anderer Mieter. // Vermieter/Admin sehen weiterhin die vollständige Übersicht über die WG. cockpitRouter.get('/cockpit/summary', requireAuth, async (req: AuthedRequest, res: Response) => { const isLandlord = req.user!.role === 'LANDLORD' || req.user!.role === 'ADMIN'; if (isLandlord) { const [trafficLights, openTickets, rooms] = await Promise.all([ paymentService.getRentTrafficLights(), prisma.ticket.findMany({ where: { status: { in: ['OPEN', 'IN_PROGRESS'] } }, orderBy: { createdAt: 'desc' }, select: TICKET_SELECT, }), prisma.room.findMany({ select: { id: true, roomNumber: true, sizeSqm: true, status: true }, orderBy: { roomNumber: 'asc' }, }), ]); const counts = { GREEN: 0, YELLOW: 0, RED: 0 } as Record<'GREEN' | 'YELLOW' | 'RED', number>; let totalWarmRentSum = 0; for (const light of trafficLights) { counts[light.status]++; totalWarmRentSum += light.amountDue; } return res.status(200).json({ role: req.user!.role, generatedAt: new Date().toISOString(), rooms, trafficLights, summary: { totalRooms: rooms.length, rentGreen: counts.GREEN, rentYellow: counts.YELLOW, rentRed: counts.RED, totalMonthlyRentTarget: totalWarmRentSum, openTicketsCount: openTickets.length, }, openTickets, }); } // --- Mieter-Ansicht: nur eigene Daten + Gemeinschaftsbereich --- const me = await prisma.user.findUnique({ where: { id: req.user!.id }, select: { roomId: true }, }); const ownRoomId = me?.roomId ?? null; const [allTrafficLights, openTickets] = await Promise.all([ paymentService.getRentTrafficLights(), prisma.ticket.findMany({ where: { status: { in: ['OPEN', 'IN_PROGRESS'] }, OR: [ { creatorId: req.user!.id }, { roomId: null }, ...(ownRoomId ? [{ roomId: ownRoomId }] : []), ], }, orderBy: { createdAt: 'desc' }, select: TICKET_SELECT, }), ]); // Eigenen aktiven Vertrag laden, um die passende Ampel aus der Gesamtliste herauszufiltern // (getRentTrafficLights liefert alle Verträge — hier wird strikt auf den eigenen reduziert). const myContract = await prisma.contract.findFirst({ where: { userId: req.user!.id, isActive: true }, include: { room: true }, }); let myRent = null; if (myContract) { myRent = allTrafficLights.find((l) => l.contractId === myContract.id) || null; } res.status(200).json({ role: req.user!.role, generatedAt: new Date().toISOString(), myRoom: myContract?.room ? { id: myContract.room.id, roomNumber: myContract.room.roomNumber } : null, myRent, openTickets, }); });