- 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>
116 lines
4.7 KiB
TypeScript
116 lines
4.7 KiB
TypeScript
'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');
|
||
}
|