Add Putzplan UI section
This commit is contained in:
parent
669aed188c
commit
544eb4d745
@ -427,6 +427,37 @@
|
||||
<h2>Offene Schadensmeldungen</h2>
|
||||
<div class="ticket-list" id="ticketList"></div>
|
||||
</section>
|
||||
|
||||
<section id="cleaningAssignSection" style="display:none">
|
||||
<h2>Putzaufgabe zuweisen</h2>
|
||||
<div class="invite-card">
|
||||
<div id="cleaningFormError" style="display:none" class="form-error"></div>
|
||||
<form id="cleaningForm" class="invite-form-row" style="flex-wrap:wrap;">
|
||||
<div class="field" style="flex:1 1 200px;">
|
||||
<label for="cleaningUser">Mieter</label>
|
||||
<select id="cleaningUser"></select>
|
||||
</div>
|
||||
<div class="field" style="flex:1 1 160px;">
|
||||
<label for="cleaningArea">Bereich</label>
|
||||
<select id="cleaningArea"></select>
|
||||
</div>
|
||||
<div class="field" style="flex:1 1 160px;">
|
||||
<label for="cleaningWeekOf">Woche ab (Mo.)</label>
|
||||
<input type="date" id="cleaningWeekOf" required />
|
||||
</div>
|
||||
<div class="field" style="flex:1 1 160px;">
|
||||
<label for="cleaningDueDate">Fällig bis</label>
|
||||
<input type="date" id="cleaningDueDate" required />
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" style="width:auto" id="cleaningSubmitBtn">Zuweisen</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="cleaningSection">
|
||||
<h2 id="cleaningSectionTitle">Putzplan</h2>
|
||||
<div class="ticket-list" id="cleaningList"></div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@ -444,6 +475,8 @@
|
||||
SCHIMMEL: 'Schimmel', SCHLUESSEL_SCHLOSS: 'Schlüssel/Schloss', SONSTIGES: 'Sonstiges',
|
||||
};
|
||||
const TICKET_STATUS_LABEL = { OPEN: 'Gemeldet', IN_PROGRESS: 'In Bearbeitung', RESOLVED: 'Erledigt', CLOSED: 'Geschlossen' };
|
||||
const CLEANING_AREA_LABEL = { BATHROOM_1: 'Bad 1', BATHROOM_2: 'Bad 2', KITCHEN: 'Wohnküche', HALLWAY_LAUNDRY: 'Waschraum/Flur' };
|
||||
const CLEANING_STATUS_LABEL = { PENDING: 'Offen', COMPLETED: 'Erledigt', VERIFIED: 'Bestätigt', MISSED: 'Verpasst' };
|
||||
|
||||
function formatEuro(n) {
|
||||
return new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(n || 0);
|
||||
@ -609,11 +642,16 @@
|
||||
const isLandlord = currentUser.role === 'LANDLORD' || currentUser.role === 'ADMIN';
|
||||
document.getElementById('inviteSection').style.display = isLandlord ? 'block' : 'none';
|
||||
document.getElementById('rentSectionTitle').textContent = isLandlord ? 'Miet-Ampel je Zimmer' : 'Meine Miete';
|
||||
document.getElementById('cleaningAssignSection').style.display = isLandlord ? 'block' : 'none';
|
||||
document.getElementById('cleaningSectionTitle').textContent = isLandlord ? 'Putzplan (alle Mieter)' : 'Mein Putzplan';
|
||||
if (isLandlord) initInvitePanel();
|
||||
if (isLandlord) initCleaningAssignForm();
|
||||
|
||||
initTicketForm();
|
||||
loadData();
|
||||
loadCleaningTasks();
|
||||
setInterval(loadData, REFRESH_INTERVAL_MS);
|
||||
setInterval(loadCleaningTasks, REFRESH_INTERVAL_MS);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
@ -955,6 +993,136 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// PUTZPLAN
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
async function initCleaningAssignForm() {
|
||||
const userSelect = document.getElementById('cleaningUser');
|
||||
const areaSelect = document.getElementById('cleaningArea');
|
||||
userSelect.innerHTML = '';
|
||||
areaSelect.innerHTML = '';
|
||||
Object.keys(CLEANING_AREA_LABEL).forEach(key => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = key;
|
||||
opt.textContent = CLEANING_AREA_LABEL[key];
|
||||
areaSelect.appendChild(opt);
|
||||
});
|
||||
|
||||
try {
|
||||
const data = await apiFetch('/tenants');
|
||||
data.tenants.forEach(t => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = t.id;
|
||||
opt.textContent = t.room ? `${t.fullName} (${t.room.roomNumber})` : t.fullName;
|
||||
userSelect.appendChild(opt);
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('[cleaning-tasks] Mieterliste laden fehlgeschlagen:', err.message);
|
||||
}
|
||||
|
||||
const form = document.getElementById('cleaningForm');
|
||||
const errorBox = document.getElementById('cleaningFormError');
|
||||
form.onsubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
errorBox.style.display = 'none';
|
||||
const submitBtn = document.getElementById('cleaningSubmitBtn');
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = 'Weist zu…';
|
||||
try {
|
||||
const assignedUserId = userSelect.value;
|
||||
const area = areaSelect.value;
|
||||
const weekOf = document.getElementById('cleaningWeekOf').value;
|
||||
const dueDate = document.getElementById('cleaningDueDate').value;
|
||||
await apiFetch('/cleaning-tasks', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ assignedUserId, area, weekOf, dueDate }),
|
||||
});
|
||||
form.reset();
|
||||
loadCleaningTasks();
|
||||
} catch (err) {
|
||||
errorBox.textContent = err.message;
|
||||
errorBox.style.display = 'block';
|
||||
} finally {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Zuweisen';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function loadCleaningTasks() {
|
||||
if (!currentUser) return;
|
||||
try {
|
||||
const data = await apiFetch('/cleaning-tasks?status=PENDING');
|
||||
renderCleaningTasks(data.tasks);
|
||||
const dataCompleted = await apiFetch('/cleaning-tasks?status=COMPLETED');
|
||||
renderCleaningTasks(data.tasks.concat(dataCompleted.tasks));
|
||||
} catch (err) {
|
||||
console.error('[cleaning-tasks] Laden fehlgeschlagen:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
function renderCleaningTasks(tasks) {
|
||||
const isLandlord = currentUser.role === 'LANDLORD' || currentUser.role === 'ADMIN';
|
||||
const list = document.getElementById('cleaningList');
|
||||
list.innerHTML = '';
|
||||
if (!tasks.length) {
|
||||
list.appendChild(el('div', 'empty-state', isLandlord ? 'Aktuell keine Putzaufgaben.' : 'Aktuell keine offenen Putzaufgaben für dich. 🎉'));
|
||||
return;
|
||||
}
|
||||
tasks.forEach(t => {
|
||||
const row = el('div', 'ticket-row');
|
||||
const left = el('div');
|
||||
left.appendChild(el('div', 'title', CLEANING_AREA_LABEL[t.area] || t.area));
|
||||
left.appendChild(el('div', 'meta-text',
|
||||
`${isLandlord && t.assignedUser ? t.assignedUser.fullName + ' · ' : ''}` +
|
||||
`Fällig ${new Date(t.dueDate).toLocaleDateString('de-DE')} · ` +
|
||||
CLEANING_STATUS_LABEL[t.status]));
|
||||
row.appendChild(left);
|
||||
|
||||
const right = el('div');
|
||||
right.style.display = 'flex';
|
||||
right.style.gap = '8px';
|
||||
right.style.alignItems = 'center';
|
||||
|
||||
if (!isLandlord && t.status === 'PENDING') {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'btn-secondary';
|
||||
btn.textContent = 'Erledigt';
|
||||
btn.onclick = () => completeCleaningTask(t.id);
|
||||
right.appendChild(btn);
|
||||
}
|
||||
if (isLandlord && t.status === 'COMPLETED') {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'btn-secondary';
|
||||
btn.textContent = 'Bestätigen';
|
||||
btn.onclick = () => verifyCleaningTask(t.id);
|
||||
right.appendChild(btn);
|
||||
}
|
||||
|
||||
row.appendChild(right);
|
||||
list.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
async function completeCleaningTask(id) {
|
||||
try {
|
||||
await apiFetch(`/cleaning-tasks/${id}/complete`, { method: 'POST', body: JSON.stringify({}) });
|
||||
loadCleaningTasks();
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyCleaningTask(id) {
|
||||
try {
|
||||
await apiFetch(`/cleaning-tasks/${id}`, { method: 'PATCH', body: JSON.stringify({ status: 'VERIFIED' }) });
|
||||
loadCleaningTasks();
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
</script>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user