Tauschanfragen gegen offene Plätze erlauben und Lücken nachbesetzen
Ein Wunschtag ohne Tauschpartner bedeutet jetzt: die Person übernimmt dort einen offenen Platz; ihr alter Platz wird beim Ausführen sofort fair nachbesetzt, ohne sie selbst. Rollen werden bei allen Tauschformen geprüft. Bearbeiter können Person und Wunschtag einer Anfrage nachtragen, etwa aus der Notiz. Der Anfragedialog zeigt, was am Wunschtag los ist. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
e8ab95159e
commit
42d495c54e
7 files changed
+196
-30
No files matched your search
+55
-5
@@ -334,11 +334,26 @@ function fillRequestPersons(date) {
|
||||
? `${fmtLong(date)}: ${assigned.map((p, i) => `${i === 0 && a.main === p.id ? 'Haupt' : 'Neben'} ${fullName(p)}`).join(', ')}`
|
||||
: `${fmtLong(date)}: kein Meeting geplant`;
|
||||
}
|
||||
/** Was ist am Wunschtag los? Offene Plätze sichtbar machen, damit der Wunsch passt. */
|
||||
function describeWishDay() {
|
||||
const f = requestForm.elements;
|
||||
const info = $('#request-wish-info');
|
||||
const base = 'Mit Person: ihr tauscht die Tage. Ohne Person: du übernimmst am Wunschtag einen noch offenen Platz, deiner wird neu besetzt.';
|
||||
const date = f.partnerDate.value;
|
||||
if (!date) { info.textContent = base; return; }
|
||||
const a = state.data.assignments[date];
|
||||
if (!a || a.off || holidayFor(date)) { info.textContent = `${fmtDate(date)}: kein Meeting geplant.`; return; }
|
||||
const slot = (role, id) => (id ? `${role} ${fullName(personById(id))}` : `${role} offen`);
|
||||
info.textContent = `${fmtDate(date)}: ${slot('Haupt', a.main)}, ${slot('Neben', a.co)}.${!a.main || !a.co ? ' Ein offener Platz kann ohne Tauschpartner übernommen werden.' : ' Beide Plätze sind besetzt, nenne die Person zum Tauschen.'}`;
|
||||
}
|
||||
requestForm.elements.partnerDate.addEventListener('change', describeWishDay);
|
||||
|
||||
function openRequestDialog(date) {
|
||||
const f = requestForm.elements;
|
||||
requestForm.reset();
|
||||
f.date.value = date || '';
|
||||
fillRequestPersons(f.date.value);
|
||||
describeWishDay();
|
||||
$('#request-result').hidden = true;
|
||||
$('#request-submit').disabled = false;
|
||||
requestDialog.showModal();
|
||||
@@ -382,7 +397,8 @@ function requestRow(r) {
|
||||
const partner = r.partnerId ? personById(r.partnerId) : null;
|
||||
const parts = [];
|
||||
if (partner) parts.push(`Wunsch: Tausch mit ${fullName(partner)}${r.partnerDate ? ` am ${fmtDate(r.partnerDate)}` : ' (übernimmt den Tag)'}`);
|
||||
else parts.push('Kein Wunschpartner genannt');
|
||||
else if (r.partnerDate) parts.push(`Wunsch: offenen Platz am ${fmtDate(r.partnerDate)} übernehmen`);
|
||||
else parts.push('Kein Wunsch genannt – aus der Notiz nachtragen oder den Tag öffnen');
|
||||
parts.push(`gestellt am ${new Date(r.createdAt).toLocaleDateString('de-DE')}`);
|
||||
if (r.status !== 'offen') parts.push(r.status === 'erledigt' ? 'erledigt' : 'abgelehnt');
|
||||
meta.textContent = parts.join(' · ');
|
||||
@@ -393,10 +409,20 @@ function requestRow(r) {
|
||||
actions.className = 'request-actions';
|
||||
const btn = (label, cls, fn) => { const b = document.createElement('button'); b.type = 'button'; b.className = cls; b.textContent = label; b.addEventListener('click', fn); actions.appendChild(b); };
|
||||
if (r.status === 'offen') {
|
||||
if (r.partnerId) btn('Ausführen', 'primary', () => run(async () => {
|
||||
const res = await api(`api/requests/${r.id}/execute`, { method: 'POST' });
|
||||
return res;
|
||||
}, 'Tausch ausgeführt. Nextcloud danach neu schreiben, damit die Termine stimmen.'));
|
||||
if (r.partnerId || r.partnerDate) btn('Ausführen', 'primary', async () => {
|
||||
try {
|
||||
const res = await api(`api/requests/${r.id}/execute`, { method: 'POST' });
|
||||
await load();
|
||||
let msg = 'Tausch ausgeführt.';
|
||||
if (res.filled) msg += res.filled.personId ? ` Lücke am ${fmtDate(res.filled.date)} mit ${fullName(personById(res.filled.personId))} besetzt.` : ` Lücke am ${fmtDate(res.filled.date)} konnte nicht besetzt werden, bitte im Kalender nachsehen.`;
|
||||
toast(`${msg} Nextcloud danach neu schreiben.`);
|
||||
} catch (err) { toast(err.message, true); }
|
||||
});
|
||||
btn('Wunsch anpassen', '', () => {
|
||||
const form = li.querySelector('.wish-form');
|
||||
form.hidden = !form.hidden;
|
||||
if (!form.hidden) form.querySelector('input[type=date]').focus();
|
||||
});
|
||||
btn('Tag öffnen', '', () => { location.hash = '#kalender'; state.month = new Date(r.date + 'T12:00:00'); renderCalendar(); openDayDialog(r.date); });
|
||||
btn('Erledigt', 'ghost', () => run(() => api(`api/requests/${r.id}`, { method: 'PUT', body: { status: 'erledigt' } }), 'Als erledigt markiert.'));
|
||||
btn('Ablehnen', 'ghost', () => run(() => api(`api/requests/${r.id}`, { method: 'PUT', body: { status: 'abgelehnt' } }), 'Anfrage abgelehnt.'));
|
||||
@@ -405,6 +431,30 @@ function requestRow(r) {
|
||||
btn('Löschen', 'ghost', () => run(() => api(`api/requests/${r.id}`, { method: 'DELETE' })));
|
||||
}
|
||||
li.appendChild(actions);
|
||||
if (r.status === 'offen') {
|
||||
// Wunsch nachtragen: Person und/oder Wunschtag, z. B. aus der Notiz
|
||||
const form = document.createElement('form');
|
||||
form.className = 'wish-form';
|
||||
form.hidden = true;
|
||||
const sel = document.createElement('select');
|
||||
sel.appendChild(new Option('– niemand Bestimmtes –', ''));
|
||||
for (const p of state.data.persons.filter((x) => x.active !== false && x.id !== r.personId && (x.canMain || x.canCo)).sort((x, y) => fullName(x).localeCompare(fullName(y)))) sel.appendChild(new Option(fullName(p), p.id));
|
||||
sel.value = r.partnerId || '';
|
||||
const dateInput = document.createElement('input');
|
||||
dateInput.type = 'date';
|
||||
dateInput.value = r.partnerDate || '';
|
||||
const save = document.createElement('button');
|
||||
save.type = 'submit';
|
||||
save.textContent = 'Wunsch speichern';
|
||||
const l1 = document.createElement('label'); l1.textContent = 'Tauschen mit'; l1.appendChild(sel);
|
||||
const l2 = document.createElement('label'); l2.textContent = 'Wunschtag'; l2.appendChild(dateInput);
|
||||
form.append(l1, l2, save);
|
||||
form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
run(() => api(`api/requests/${r.id}`, { method: 'PUT', body: { partnerId: sel.value || null, partnerDate: dateInput.value || null } }), 'Wunsch gespeichert.');
|
||||
});
|
||||
li.appendChild(form);
|
||||
}
|
||||
return li;
|
||||
}
|
||||
function renderRequests() {
|
||||
|
||||
Reference in new issue
Block a user