Files
ferdi2goandClaude Fable 5.1 42d495c54e 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>
2026-10-15 11:55:53 +00:00

108 lines
5.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Tauschanfragen: Betrachter melden, dass sie an einem Tag nicht können; Bearbeiter führen den Tausch aus.
import { isValidDate } from './scheduler.js';
export const REQUEST_STATUS = ['offen', 'erledigt', 'abgelehnt'];
/** Rolle einer Person an einem Tag: 'main', 'co' oder null. */
export function roleOn(assignment, personId) {
if (!assignment || assignment.off || !personId) return null;
if (assignment.main === personId) return 'main';
if (assignment.co === personId) return 'co';
return null;
}
/** Darf die Person diese Rolle übernehmen? */
export function canTake(person, role) {
if (!person || person.active === false) return false;
return role === 'main' ? person.canMain !== false : person.canCo !== false;
}
/** Offener Platz an einem Tag, den die Person übernehmen könnte: 'main', 'co' oder null. */
export function openSlotFor(assignment, person) {
if (!assignment || assignment.off || !person) return null;
if (assignment.main === person.id || assignment.co === person.id) return null;
if (!assignment.main && canTake(person, 'main')) return 'main';
if (!assignment.co && canTake(person, 'co')) return 'co';
return null;
}
/**
* Eingabe einer Anfrage prüfen. persons: [{id, active}], assignments: { date: {main, co, off} }.
* Liefert { error } oder { request } (ohne id/status/createdAt).
*/
export function validateRequest(body, persons, assignments) {
const b = body || {};
const active = (id) => persons.find((p) => p.id === id && p.active !== false);
const personId = String(b.personId || '');
if (!active(personId)) return { error: 'Bitte wähle deinen Namen.' };
const date = String(b.date || '');
if (!isValidDate(date)) return { error: 'Ungültiger Tag.' };
const role = roleOn(assignments[date], personId);
if (!role) return { error: 'An diesem Tag bist du laut Plan nicht eingeteilt.' };
const partnerId = b.partnerId ? String(b.partnerId) : null;
if (partnerId && !active(partnerId)) return { error: 'Tauschpartner nicht gefunden.' };
if (partnerId === personId) return { error: 'Mit dir selbst kannst du nicht tauschen.' };
const partnerDate = b.partnerDate ? String(b.partnerDate) : null;
if (partnerDate && !isValidDate(partnerDate)) return { error: 'Ungültiger Wunschtag.' };
if (partnerDate === date) return { error: 'Der Wunschtag ist derselbe Tag.' };
if (partnerDate && !partnerId) {
// Ohne Tauschpartner heißt der Wunsch: einen noch offenen Platz am Wunschtag übernehmen
const me = active(personId);
const a = assignments[partnerDate];
if (!a || a.off) return { error: 'Am Wunschtag ist kein Meeting geplant.' };
if (a.main === personId || a.co === personId) return { error: 'Am Wunschtag bist du schon eingeteilt.' };
if (!openSlotFor(a, me)) return { error: 'Am Wunschtag ist kein Platz frei, den du übernehmen könntest. Nenne eine Person zum Tauschen.' };
}
const note = String(b.note || '').trim().slice(0, 300);
return { request: { personId, date, role, partnerId, partnerDate, note } };
}
/**
* Tausch ausführen. Liefert { changes: [{date, role, from, to}], vacated } und verändert assignments direkt.
* Fälle:
* - Partner + Wunschtag: über Kreuz – Partner übernimmt den Tag des Anfragenden, dieser den Slot des Partners am Wunschtag.
* - Nur Partner: Partner übernimmt den Slot des Anfragenden.
* - Nur Wunschtag: die Person übernimmt dort einen offenen Platz; ihr alter Platz wird frei (vacated) und
* sollte vom Aufrufer nachbesetzt werden.
* - Weder noch: nicht automatisch ausführbar.
* persons: [{ id, canMain, canCo, active }] für die Rollenprüfung.
*/
export function applySwap(assignments, req, persons = []) {
const find = (id) => persons.find((p) => p.id === id) || null;
const a = assignments[req.date];
const role = roleOn(a, req.personId);
if (!role) throw new Error('Die Person ist an diesem Tag nicht mehr eingeteilt. Bitte den Tag von Hand anpassen.');
const me = find(req.personId);
const changes = [];
if (!req.partnerId) {
if (!req.partnerDate) throw new Error('Ohne Wunschpartner oder Wunschtag bitte den Tag öffnen und selbst jemanden auswählen.');
const b = assignments[req.partnerDate];
const slot = openSlotFor(b, me || { id: req.personId, canMain: true, canCo: true });
if (!slot) throw new Error('Am Wunschtag ist kein passender Platz mehr frei. Bitte den Tag von Hand anpassen.');
b[slot] = req.personId;
changes.push({ date: req.partnerDate, role: slot, from: null, to: req.personId });
a[role] = null;
changes.push({ date: req.date, role, from: req.personId, to: null });
return { changes, vacated: { date: req.date, role } };
}
const partner = find(req.partnerId);
const otherRole = role === 'main' ? 'co' : 'main';
if (a[otherRole] === req.partnerId) throw new Error('Der Wunschpartner ist an diesem Tag schon die andere Moderation. Bitte den Tag von Hand anpassen.');
if (partner && !canTake(partner, role)) throw new Error(`Der Wunschpartner darf keine ${role === 'main' ? 'Haupt' : 'Neben'}moderation übernehmen. Bitte den Tag von Hand anpassen.`);
if (req.partnerDate) {
const b = assignments[req.partnerDate];
const partnerRole = roleOn(b, req.partnerId);
if (!partnerRole) throw new Error('Der Wunschpartner ist am Wunschtag nicht eingeteilt. Bitte den Tag von Hand anpassen.');
const otherB = partnerRole === 'main' ? 'co' : 'main';
if (b[otherB] === req.personId) throw new Error('Die anfragende Person ist am Wunschtag schon die andere Moderation. Bitte den Tag von Hand anpassen.');
if (me && !canTake(me, partnerRole)) throw new Error(`Die anfragende Person darf keine ${partnerRole === 'main' ? 'Haupt' : 'Neben'}moderation übernehmen. Bitte den Tag von Hand anpassen.`);
b[partnerRole] = req.personId;
changes.push({ date: req.partnerDate, role: partnerRole, from: req.partnerId, to: req.personId });
}
a[role] = req.partnerId;
changes.push({ date: req.date, role, from: req.personId, to: req.partnerId });
return { changes, vacated: null };
}