Files
lernbar/src/app/(angemeldet)/verwaltung/aktionen.ts
T
Florian WildeandClaude Opus 5 103a603eb4 Baustein 3: Wochenplan als Startseite, Fortschritt, Wochen verschieben
- Wochenplan ist die Startseite des Azubis: Thema, Leitfrage, fuenf Tage
  mit Theorie und Praxis, Fortschrittsbalken, was noch offen ist
- Ausbilder sieht je Azubi die laufende Woche mit Stand und offenen Punkten
- /woche/<nr> fuer jede Woche, Ausbilder waehlt ueber ?azubi= den Stand;
  Zugriff wird serverseitig bei jedem Aufruf geprueft
- Tabelle wochenplanung mit geplantem und tatsaechlichem Datum, Zustand
  und Notiz fuer den Azubi
- Verschieben per Datumsfeld, wahlweise mit allen folgenden Wochen
- Reihenfolge per Ziehen und Ablegen sowie per Tastatur (Alt + Pfeil)
- Warnung bei sich ueberlappenden Terminen
- Plan entsteht automatisch beim Anlegen eines Azubikontos
- Planungs- und Fortschrittslogik als reine Funktionen, 36 Tests dafuer
- Insgesamt 104 Tests

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 09:04:39 +02:00

116 lines
4.7 KiB
TypeScript
Raw 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.
'use server';
import { revalidatePath } from 'next/cache';
import { erfordereAusbilder } from '@/lib/auth/rechte';
import { passwortMangel } from '@/lib/auth/passwort';
import { aktivSetzen, benutzerAnlegen, benutzerNachEmail, benutzerNachId, passwortSetzen } from '@/lib/repos/benutzer';
import { ausbildungAendern, ausbildungAnlegen, ausbildungNachAzubi, ausbildungNachId } from '@/lib/repos/ausbildung';
import { alsDatumText } from '@/lib/zeit';
import { inhalte } from '@/lib/inhalte';
import { planAuffrischen } from '@/lib/repos/wochenplanung';
export type Rueckmeldung = { fehler?: string; erfolg?: string };
const datumMuster = /^\d{4}-\d{2}-\d{2}$/;
export async function azubiAnlegen(_vorher: Rueckmeldung, formular: FormData): Promise<Rueckmeldung> {
const ausbilder = await erfordereAusbilder();
const name = String(formular.get('name') ?? '').trim();
const email = String(formular.get('email') ?? '')
.trim()
.toLowerCase();
const passwort = String(formular.get('passwort') ?? '');
const beginn = String(formular.get('beginn') ?? '').trim();
const probezeitBis = String(formular.get('probezeitBis') ?? '').trim();
const ende = String(formular.get('ende') ?? '').trim();
if (!name) return { fehler: 'Bitte einen Namen eintragen.' };
if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) return { fehler: 'Die E-Mail-Adresse sieht nicht gültig aus.' };
const mangel = passwortMangel(passwort);
if (mangel) return { fehler: mangel };
if (!datumMuster.test(beginn)) return { fehler: 'Bitte ein Beginndatum wählen.' };
if (benutzerNachEmail(email)) return { fehler: 'Diese E-Mail-Adresse ist schon vergeben.' };
const azubi = await benutzerAnlegen({ name, email, passwort, rolle: 'azubi' });
const neueAusbildung = ausbildungAnlegen({
azubiId: azubi.id,
ausbilderId: ausbilder.id,
beginn,
probezeitBis: datumMuster.test(probezeitBis) ? probezeitBis : null,
ende: datumMuster.test(ende) ? ende : null,
});
// Der Wochenplan entsteht gleich mit – ohne ihn ist die Startseite leer.
const wochen = inhalte().wochen.map((w) => w.nummer);
const eingeplant = planAuffrischen(neueAusbildung.id, beginn, wochen);
revalidatePath('/verwaltung');
revalidatePath('/');
return {
erfolg:
`Konto für ${name} angelegt und Ausbildung ab ${alsDatumText(beginn)} eingerichtet. ` +
`${eingeplant} ${eingeplant === 1 ? 'Woche' : 'Wochen'} eingeplant.`,
};
}
export async function ausbildungBearbeiten(_vorher: Rueckmeldung, formular: FormData): Promise<Rueckmeldung> {
const ausbilder = await erfordereAusbilder();
const id = String(formular.get('ausbildungId') ?? '');
const bestand = ausbildungNachId(id);
if (!bestand || bestand.ausbilderId !== ausbilder.id) {
return { fehler: 'Diese Ausbildung gehört nicht zu dir.' };
}
const beginn = String(formular.get('beginn') ?? '').trim();
const ende = String(formular.get('ende') ?? '').trim();
const probezeitBis = String(formular.get('probezeitBis') ?? '').trim();
if (!datumMuster.test(beginn)) return { fehler: 'Bitte ein gültiges Beginndatum wählen.' };
ausbildungAendern(id, {
beginn,
ende: datumMuster.test(ende) ? ende : null,
probezeitBis: datumMuster.test(probezeitBis) ? probezeitBis : null,
});
revalidatePath('/verwaltung');
return { erfolg: 'Ausbildung aktualisiert.' };
}
export async function passwortZuruecksetzen(_vorher: Rueckmeldung, formular: FormData): Promise<Rueckmeldung> {
const ausbilder = await erfordereAusbilder();
const benutzerId = String(formular.get('benutzerId') ?? '');
const neu = String(formular.get('passwort') ?? '');
const ziel = benutzerNachId(benutzerId);
if (!ziel) return { fehler: 'Konto nicht gefunden.' };
const bezug = ausbildungNachAzubi(benutzerId);
const eigenesKonto = ziel.id === ausbilder.id;
const eigenerAzubi = ziel.rolle === 'azubi' && bezug?.ausbilderId === ausbilder.id;
if (!eigenesKonto && !eigenerAzubi) return { fehler: 'Dieses Konto darfst du nicht ändern.' };
const mangel = passwortMangel(neu);
if (mangel) return { fehler: mangel };
await passwortSetzen(benutzerId, neu);
revalidatePath('/verwaltung');
return { erfolg: `Neues Passwort für ${ziel.name} gesetzt. Alle offenen Sitzungen wurden beendet.` };
}
export async function kontoAktivSetzen(formular: FormData): Promise<void> {
const ausbilder = await erfordereAusbilder();
const benutzerId = String(formular.get('benutzerId') ?? '');
const aktiv = String(formular.get('aktiv') ?? '') === '1';
const ziel = benutzerNachId(benutzerId);
if (!ziel || ziel.id === ausbilder.id) return;
const bezug = ausbildungNachAzubi(benutzerId);
if (ziel.rolle !== 'azubi' || bezug?.ausbilderId !== ausbilder.id) return;
aktivSetzen(benutzerId, aktiv);
revalidatePath('/verwaltung');
}