'use server'; import { revalidatePath } from 'next/cache'; import { redirect } from 'next/navigation'; import { erfordereBenutzer, erfordereBewertungsrecht } from '@/lib/auth/rechte'; import { darfAzubiDatenSehen } from '@/lib/auth/regeln'; import { ausbildungNachAzubi, ausbildungNachId } from '@/lib/repos/ausbildung'; import { inhalte } from '@/lib/inhalte'; import { anlegen, bestaetigen, bestaetigungZuruecknehmen, gespraechNachId, inhaltSpeichern, inhaltVon, loeschen, } from '@/lib/repos/feedback'; import { istAbgeschlossen, type Gespraechsinhalt } from '@/lib/feedback/inhalt'; export type Rueckmeldung = { fehler?: string; erfolg?: string }; const datumMuster = /^\d{4}-\d{2}-\d{2}$/; /** * Holt ein Gespräch samt Rechteprüfung. * * Beide Seiten dürfen lesen und – solange nicht beide bestätigt haben – * schreiben. Das Gespräch gehört ihnen gemeinsam; eine Selbsteinschätzung, * die der Ausbilder eintragen könnte, wäre keine. */ export async function gespraechZumLesen(id: string) { const benutzer = await erfordereBenutzer(); const gespraech = gespraechNachId(id); if (!gespraech) return null; const ausbildung = ausbildungNachId(gespraech.ausbildungId); if (!ausbildung) return null; if (!darfAzubiDatenSehen(benutzer, ausbildung.azubiId, ausbildung)) return null; return { benutzer, gespraech, ausbildung }; } function aktualisiere(id?: string) { revalidatePath('/feedback'); if (id) revalidatePath(`/feedback/${id}`); } export async function gespraechAnlegen(formular: FormData): Promise { const ausbilder = await erfordereBenutzer(); const azubiId = String(formular.get('azubiId') ?? ''); erfordereBewertungsrecht(ausbilder, azubiId); const ausbildung = ausbildungNachAzubi(azubiId); if (!ausbildung) return; const datum = String(formular.get('datum') ?? '').trim(); const gespraech = anlegen( ausbildung.id, ausbilder.id, inhalte().feedback, datumMuster.test(datum) ? datum : undefined, ); aktualisiere(gespraech.id); redirect(`/feedback/${gespraech.id}`); } export async function gespraechSpeichern(_vorher: Rueckmeldung, formular: FormData): Promise { const id = String(formular.get('gespraechId') ?? ''); const gelesen = await gespraechZumLesen(id); if (!gelesen) return { fehler: 'Dieses Gespräch gibt es nicht.' }; const { benutzer, gespraech } = gelesen; if (istAbgeschlossen({ bestaetigtAzubi: gespraech.bestaetigtAzubi === 1, bestaetigtAusbilder: gespraech.bestaetigtAusbilder === 1, })) { return { fehler: 'Das Gespräch ist von beiden Seiten bestätigt und damit unveränderlich.' }; } const vorlage = inhalte().feedback; const bestand = inhaltVon(gespraech, vorlage); const istAzubi = benutzer.rolle === 'azubi'; // Jede Seite trägt nur ihre eigene Spalte ein. const bewertungen: Gespraechsinhalt['bewertungen'] = bestand.bewertungen.map((bewertung) => { const roh = String(formular.get(`stufe-${bewertung.kriteriumId}`) ?? '').trim(); const gelesen = roh === '' ? null : Number(roh); const gueltig = gelesen !== null && bestand.skala.some((s) => s.wert === gelesen) ? gelesen : null; return { ...bewertung, ausbilder: istAzubi ? bewertung.ausbilder : gueltig, azubi: istAzubi ? gueltig : bewertung.azubi, // Die Bemerkung je Kriterium schreibt der Ausbilder. bemerkung: istAzubi ? bewertung.bemerkung : String(formular.get(`bemerkung-${bewertung.kriteriumId}`) ?? '').slice(0, 500), }; }); // Die Freitexte schreibt der Ausbilder; der Azubi liest sie. const neu: Gespraechsinhalt = istAzubi ? { ...bestand, bewertungen } : { ...bestand, bewertungen, leitfadennotizen: bestand.leitfadennotizen.map((notiz, stelle) => ({ ...notiz, notiz: String(formular.get(`leitfaden-${stelle}`) ?? '').slice(0, 4000), })), staerken: String(formular.get('staerken') ?? '').slice(0, 4000), entwicklungsfelder: String(formular.get('entwicklungsfelder') ?? '').slice(0, 4000), ziele: String(formular.get('ziele') ?? '').slice(0, 4000), sonstiges: String(formular.get('sonstiges') ?? '').slice(0, 4000), }; const datum = String(formular.get('datum') ?? '').trim(); inhaltSpeichern(id, neu, !istAzubi && datumMuster.test(datum) ? datum : undefined); aktualisiere(id); return { erfolg: istAzubi ? 'Deine Selbsteinschätzung ist gespeichert.' : 'Gespeichert.' }; } export async function gespraechBestaetigen(_vorher: Rueckmeldung, formular: FormData): Promise { const id = String(formular.get('gespraechId') ?? ''); const gelesen = await gespraechZumLesen(id); if (!gelesen) return { fehler: 'Dieses Gespräch gibt es nicht.' }; const { benutzer, gespraech } = gelesen; const rolle = benutzer.rolle === 'azubi' ? 'azubi' : 'ausbilder'; const schon = rolle === 'azubi' ? gespraech.bestaetigtAzubi === 1 : gespraech.bestaetigtAusbilder === 1; if (schon) return { fehler: 'Du hast bereits bestätigt.' }; bestaetigen(id, rolle); aktualisiere(id); const andereHatSchon = rolle === 'azubi' ? gespraech.bestaetigtAusbilder === 1 : gespraech.bestaetigtAzubi === 1; return { erfolg: andereHatSchon ? 'Bestätigt. Beide Seiten haben zugestimmt – das Gespräch ist jetzt unveränderlich.' : 'Bestätigt. Sobald die andere Seite ebenfalls bestätigt, ist das Gespräch unveränderlich.', }; } export async function bestaetigungZurueck(formular: FormData): Promise { const id = String(formular.get('gespraechId') ?? ''); const gelesen = await gespraechZumLesen(id); if (!gelesen) return; const { benutzer, gespraech } = gelesen; const rolle = benutzer.rolle === 'azubi' ? 'azubi' : 'ausbilder'; // Hat die andere Seite schon bestätigt, ist das Gespräch abgeschlossen. const andereHatSchon = rolle === 'azubi' ? gespraech.bestaetigtAusbilder === 1 : gespraech.bestaetigtAzubi === 1; if (andereHatSchon) return; bestaetigungZuruecknehmen(id, rolle); aktualisiere(id); } export async function gespraechVerwerfen(formular: FormData): Promise { const id = String(formular.get('gespraechId') ?? ''); const gelesen = await gespraechZumLesen(id); if (!gelesen) return; const { benutzer, gespraech } = gelesen; if (benutzer.rolle !== 'ausbilder') return; // Bestätigtes bleibt. Verworfen wird nur, was noch niemand unterschrieben hat. if (gespraech.bestaetigtAzubi === 1 || gespraech.bestaetigtAusbilder === 1) return; loeschen(id); revalidatePath('/feedback'); redirect('/feedback'); }