Add Putzplan cleaning-tasks route
This commit is contained in:
parent
e3ac1e9c4a
commit
57ee85292c
147
backend/src/routes/cleaningTasks.ts
Normal file
147
backend/src/routes/cleaningTasks.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import { Router, Response } from 'express';
|
||||
import { PrismaClient, CleaningArea, CleaningStatus } from '@prisma/client';
|
||||
import { AuthedRequest, requireAuth, requireRole } from '../middleware/auth';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
/**
|
||||
* Router für den Putzplan (rotierende Reinigungsaufgaben).
|
||||
*
|
||||
* Zugriffsregel: ein Mieter sieht (und kann abhaken) nur seine eigenen
|
||||
* zugewiesenen Putzaufgaben. Vermieter/Admin sehen und verwalten den
|
||||
* kompletten Putzplan aller Mieter.
|
||||
*/
|
||||
export const cleaningTasksRouter = Router();
|
||||
|
||||
const AREAS = Object.values(CleaningArea);
|
||||
|
||||
const TASK_SELECT = {
|
||||
id: true,
|
||||
area: true,
|
||||
weekOf: true,
|
||||
dueDate: true,
|
||||
completedAt: true,
|
||||
proofImageUrl: true,
|
||||
status: true,
|
||||
verifiedBy: true,
|
||||
assignedUser: { select: { id: true, fullName: true } },
|
||||
};
|
||||
|
||||
// Vermieter/Admin legen eine neue Putzaufgabe an (z. B. für die kommende Rotationswoche).
|
||||
cleaningTasksRouter.post(
|
||||
'/cleaning-tasks',
|
||||
requireAuth,
|
||||
requireRole('LANDLORD', 'ADMIN'),
|
||||
async (req: AuthedRequest, res: Response) => {
|
||||
const { assignedUserId, area, weekOf, dueDate } = req.body || {};
|
||||
|
||||
if (!assignedUserId || typeof assignedUserId !== 'string') {
|
||||
return res.status(400).json({ error: 'assignedUserId ist erforderlich' });
|
||||
}
|
||||
if (!area || !AREAS.includes(area)) {
|
||||
return res.status(400).json({ error: `Ungültiger Bereich. Erlaubt: ${AREAS.join(', ')}` });
|
||||
}
|
||||
if (!weekOf || Number.isNaN(Date.parse(weekOf))) {
|
||||
return res.status(400).json({ error: 'weekOf (Datum) ist erforderlich' });
|
||||
}
|
||||
if (!dueDate || Number.isNaN(Date.parse(dueDate))) {
|
||||
return res.status(400).json({ error: 'dueDate (Datum) ist erforderlich' });
|
||||
}
|
||||
|
||||
const assignedUser = await prisma.user.findUnique({ where: { id: assignedUserId } });
|
||||
if (!assignedUser) {
|
||||
return res.status(400).json({ error: 'Zugewiesener Nutzer nicht gefunden' });
|
||||
}
|
||||
|
||||
const task = await prisma.cleaningTask.create({
|
||||
data: {
|
||||
assignedUserId,
|
||||
area,
|
||||
weekOf: new Date(weekOf),
|
||||
dueDate: new Date(dueDate),
|
||||
status: CleaningStatus.PENDING,
|
||||
},
|
||||
select: TASK_SELECT,
|
||||
});
|
||||
|
||||
res.status(201).json({ task });
|
||||
},
|
||||
);
|
||||
|
||||
cleaningTasksRouter.get('/cleaning-tasks', requireAuth, async (req: AuthedRequest, res: Response) => {
|
||||
const isLandlord = req.user!.role === 'LANDLORD' || req.user!.role === 'ADMIN';
|
||||
const statusFilter = typeof req.query.status === 'string' ? req.query.status : undefined;
|
||||
|
||||
const where: Record<string, unknown> = {};
|
||||
if (statusFilter && (Object.values(CleaningStatus) as string[]).includes(statusFilter)) {
|
||||
where.status = statusFilter;
|
||||
}
|
||||
if (!isLandlord) {
|
||||
where.assignedUserId = req.user!.id;
|
||||
}
|
||||
|
||||
const tasks = await prisma.cleaningTask.findMany({
|
||||
where,
|
||||
orderBy: [{ weekOf: 'desc' }, { dueDate: 'asc' }],
|
||||
select: TASK_SELECT,
|
||||
});
|
||||
|
||||
res.status(200).json({ tasks });
|
||||
});
|
||||
|
||||
// Mieter hakt die eigene Putzaufgabe ab, optional mit Fotobeweis (URL).
|
||||
// Vermieter/Admin dürfen ebenfalls jede Aufgabe abschließen (z. B. Vertretung).
|
||||
cleaningTasksRouter.post(
|
||||
'/cleaning-tasks/:id/complete',
|
||||
requireAuth,
|
||||
async (req: AuthedRequest, res: Response) => {
|
||||
const task = await prisma.cleaningTask.findUnique({ where: { id: req.params.id } });
|
||||
if (!task) return res.status(404).json({ error: 'Putzaufgabe nicht gefunden' });
|
||||
|
||||
const isLandlord = req.user!.role === 'LANDLORD' || req.user!.role === 'ADMIN';
|
||||
if (!isLandlord && task.assignedUserId !== req.user!.id) {
|
||||
return res.status(403).json({ error: 'Du kannst nur deine eigenen Putzaufgaben abhaken' });
|
||||
}
|
||||
|
||||
const { proofImageUrl } = req.body || {};
|
||||
|
||||
const updated = await prisma.cleaningTask.update({
|
||||
where: { id: req.params.id },
|
||||
data: {
|
||||
status: CleaningStatus.COMPLETED,
|
||||
completedAt: new Date(),
|
||||
proofImageUrl: typeof proofImageUrl === 'string' && proofImageUrl.trim() ? proofImageUrl.trim() : task.proofImageUrl,
|
||||
},
|
||||
select: TASK_SELECT,
|
||||
});
|
||||
|
||||
res.status(200).json({ task: updated });
|
||||
},
|
||||
);
|
||||
|
||||
// Vermieter/Admin bestätigt eine erledigte Aufgabe (Mitbewohner-Verifizierung) oder markiert sie als verpasst.
|
||||
cleaningTasksRouter.patch(
|
||||
'/cleaning-tasks/:id',
|
||||
requireAuth,
|
||||
requireRole('LANDLORD', 'ADMIN'),
|
||||
async (req: AuthedRequest, res: Response) => {
|
||||
const { status } = req.body || {};
|
||||
if (!status || !(Object.values(CleaningStatus) as string[]).includes(status)) {
|
||||
return res.status(400).json({ error: `Ungültiger Status. Erlaubt: ${Object.values(CleaningStatus).join(', ')}` });
|
||||
}
|
||||
|
||||
try {
|
||||
const task = await prisma.cleaningTask.update({
|
||||
where: { id: req.params.id },
|
||||
data: {
|
||||
status,
|
||||
verifiedBy: status === CleaningStatus.VERIFIED ? req.user!.id : undefined,
|
||||
},
|
||||
select: TASK_SELECT,
|
||||
});
|
||||
res.status(200).json({ task });
|
||||
} catch {
|
||||
res.status(404).json({ error: 'Putzaufgabe nicht gefunden' });
|
||||
}
|
||||
},
|
||||
);
|
||||
Loading…
Reference in New Issue
Block a user