Müll-Erinnerung gemeinschaftlich abhakbar machen

Jeder Mieter kann eine Mülltonne als rausgestellt markieren; danach
verschwindet die Erinnerung für alle Mieter, nicht nur für den, der sie
abgehakt hat.
This commit is contained in:
Giuseppe Lombardo 2026-08-13 13:31:55 +00:00
parent b15ba77336
commit 778cb94b3a
3 changed files with 59 additions and 3 deletions

View File

@ -105,7 +105,13 @@ storageRouter.delete(
// MÜLL-KALENDER
// ----------------------------------------------------------------------------
const TRASH_SELECT = { id: true, type: true, date: true };
const TRASH_SELECT = {
id: true,
type: true,
date: true,
completedAt: true,
completedBy: { select: { id: true, fullName: true } },
};
storageRouter.post(
'/trash-schedule',
@ -146,6 +152,26 @@ storageRouter.get('/trash-schedule', requireAuth, async (req: AuthedRequest, res
res.status(200).json({ entries });
});
// Jeder Mieter (nicht nur der Vermieter) kann eine Mülltonne als
// "rausgestellt" markieren — das ist eine gemeinschaftliche Aufgabe, keine
// pro Person. Danach verschwindet die Erinnerung für alle.
storageRouter.post(
'/trash-schedule/:id/complete',
requireAuth,
async (req: AuthedRequest, res: Response) => {
try {
const entry = await prisma.trashSchedule.update({
where: { id: req.params.id },
data: { completedAt: new Date(), completedById: req.user!.id },
select: TRASH_SELECT,
});
res.status(200).json({ entry });
} catch {
res.status(404).json({ error: 'Eintrag nicht gefunden' });
}
},
);
storageRouter.delete(
'/trash-schedule/:id',
requireAuth,

View File

@ -189,6 +189,7 @@ model User {
accessCodeAssigned AccessCode[] @relation("AccessCodeUser")
accessCodesGiven AccessCode[] @relation("AccessCodeAssignedBy")
announcementsMade Announcement[]
trashCompleted TrashSchedule[]
notifications Notification[]
invitationsSent Invitation[] @relation("InvitationsSent")
storageSlots StorageSlot[]
@ -640,6 +641,13 @@ model TrashSchedule {
date DateTime
reminderSentAt DateTime? @map("reminder_sent_at")
// Wird gesetzt, sobald irgendein Mieter die Tonne rausgestellt hat — dann
// verschwindet die Erinnerung für ALLE Mieter (gemeinschaftliche Aufgabe,
// keine Erledigung pro Person).
completedAt DateTime? @map("completed_at")
completedById String? @map("completed_by_id")
completedBy User? @relation(fields: [completedById], references: [id])
createdAt DateTime @default(now()) @map("created_at")
@@unique([type, date])

View File

@ -3283,8 +3283,14 @@
const from = today.toISOString().slice(0, 10);
const to = weekAhead.toISOString().slice(0, 10);
const data = await apiFetch(`/trash-schedule?from=${from}&to=${to}`);
data.entries.forEach(e => {
items.push({ date: new Date(e.date), icon: '🗑️', text: `Müll rausstellen: ${TRASH_TYPE_LABEL[e.type] || e.type} am ${new Date(e.date).toLocaleDateString('de-DE')}` });
data.entries.filter(e => !e.completedAt).forEach(e => {
items.push({
date: new Date(e.date),
icon: '🗑️',
text: `Müll rausstellen: ${TRASH_TYPE_LABEL[e.type] || e.type} am ${new Date(e.date).toLocaleDateString('de-DE')}`,
actionLabel: 'Erledigt',
onAction: () => completeTrashEntry(e.id),
});
});
} catch (err) {
console.error('[notifications] Müllkalender laden fehlgeschlagen:', err.message);
@ -3316,10 +3322,26 @@
items.forEach(item => {
const row = el('div', 'ticket-row');
row.appendChild(el('div', 'title', `${item.icon} ${item.text}`));
if (item.onAction) {
const btn = document.createElement('button');
btn.className = 'btn-secondary';
btn.textContent = item.actionLabel;
btn.onclick = item.onAction;
row.appendChild(btn);
}
list.appendChild(row);
});
}
async function completeTrashEntry(id) {
try {
await apiFetch(`/trash-schedule/${id}/complete`, { method: 'POST' });
loadNotifications();
} catch (err) {
alert(err.message);
}
}
function buildProfileCompletenessBadge(user) {
const missing = [];
if (!user.phoneNumber) missing.push('Telefonnummer');