praktikum, Notfallkontakt und ein Platz pro Dienstelle bei neuer Zeitraum
This commit is contained in:
Binary file not shown.
@@ -84,6 +84,12 @@ model Anmeldung {
|
||||
sozialverhalten String?
|
||||
motivation String?
|
||||
alter Int?
|
||||
|
||||
// Notfallkontakt
|
||||
notfallVorname String?
|
||||
notfallNachname String?
|
||||
notfallTelefon String?
|
||||
|
||||
status Status @default(OFFEN)
|
||||
|
||||
processedAt DateTime?
|
||||
|
||||
@@ -167,6 +167,21 @@
|
||||
<span class="font-medium text-gray-700">E-Mail:</span><br>
|
||||
<a href="mailto:{anmeldung.email}" class="text-blue-600 hover:text-blue-800 break-all text-xs">{anmeldung.email}</a>
|
||||
</div>
|
||||
|
||||
<!-- Notfallkontakt -->
|
||||
{#if anmeldung.notfallVorname || anmeldung.notfallNachname || anmeldung.notfallTelefon}
|
||||
<div class="mt-2 pt-2 border-t border-gray-100">
|
||||
<span class="font-medium text-orange-700">Notfallkontakt:</span>
|
||||
<div class="text-gray-600">
|
||||
{anmeldung.notfallVorname || ''} {anmeldung.notfallNachname || ''}
|
||||
</div>
|
||||
{#if anmeldung.notfallTelefon}
|
||||
<a href="tel:{anmeldung.notfallTelefon}" class="text-blue-600 hover:text-blue-800">
|
||||
{anmeldung.notfallTelefon}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
let zeitraum = '';
|
||||
let motivation = '';
|
||||
|
||||
// Notfallkontakt
|
||||
let notfallVorname = '';
|
||||
let notfallNachname = '';
|
||||
let notfallTelefon = '';
|
||||
|
||||
let wunsch1Id = '';
|
||||
let wunsch2Id = '';
|
||||
let wunsch3Id = '';
|
||||
@@ -208,6 +213,9 @@
|
||||
noteMathe = '';
|
||||
sozialverhalten = '';
|
||||
schulklasse = '';
|
||||
notfallVorname = '';
|
||||
notfallNachname = '';
|
||||
notfallTelefon = '';
|
||||
pdfDateien = [];
|
||||
fileInputKey += 1;
|
||||
success = false;
|
||||
@@ -246,6 +254,9 @@
|
||||
data.append('sozialverhalten', sozialverhalten);
|
||||
data.append('schulklasse', schulklasse);
|
||||
data.append('alter', alter);
|
||||
data.append('notfallVorname', notfallVorname);
|
||||
data.append('notfallNachname', notfallNachname);
|
||||
data.append('notfallTelefon', notfallTelefon);
|
||||
|
||||
for (const pdf of pdfDateien) {
|
||||
data.append('pdfs', pdf);
|
||||
@@ -471,6 +482,16 @@
|
||||
</div>
|
||||
{/key}
|
||||
|
||||
<!-- Notfallkontakt -->
|
||||
<div class="border-t pt-4 mt-4">
|
||||
<h2 class="text-lg font-semibold text-gray-700 mb-3">Notfallkontakt</h2>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<input bind:value={notfallVorname} placeholder="Vorname Notfallkontakt" required class="input" />
|
||||
<input bind:value={notfallNachname} placeholder="Nachname Notfallkontakt" required class="input" />
|
||||
<input bind:value={notfallTelefon} type="tel" placeholder="Mobilnummer Notfallkontakt" required class="input col-span-2" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if showAblehnungModal}
|
||||
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div class="bg-white p-6 rounded shadow-lg text-center space-y-4 max-w-sm w-full">
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
noteDeutsch?: string;
|
||||
noteMathe?: string;
|
||||
sozialverhalten?: string;
|
||||
notfallVorname?: string;
|
||||
notfallNachname?: string;
|
||||
notfallTelefon?: string;
|
||||
wunsch1?: { id: number; name: string };
|
||||
wunsch2?: { id: number; name: string };
|
||||
wunsch3?: { id: number; name: string };
|
||||
|
||||
@@ -23,7 +23,7 @@ export async function GET() {
|
||||
]
|
||||
});
|
||||
|
||||
const formattedAnmeldungen = anmeldungen.map(anmeldung => ({
|
||||
const formattedAnmeldungen = anmeldungen.map((anmeldung) => ({
|
||||
id: anmeldung.id,
|
||||
anrede: anmeldung.anrede,
|
||||
vorname: anmeldung.vorname,
|
||||
@@ -45,33 +45,48 @@ export async function GET() {
|
||||
noteMathe: anmeldung.noteMathe ? anmeldung.noteMathe.toString() : undefined,
|
||||
sozialverhalten: anmeldung.sozialverhalten,
|
||||
|
||||
// Notfallkontakt
|
||||
notfallVorname: anmeldung.notfallVorname,
|
||||
notfallNachname: anmeldung.notfallNachname,
|
||||
notfallTelefon: anmeldung.notfallTelefon,
|
||||
|
||||
status: mapPrismaStatusToFrontend(anmeldung.status),
|
||||
processedAt: anmeldung.processedAt ? anmeldung.processedAt.getTime() : undefined,
|
||||
|
||||
wunsch1: anmeldung.wunsch1 ? {
|
||||
wunsch1: anmeldung.wunsch1
|
||||
? {
|
||||
id: anmeldung.wunsch1.id,
|
||||
name: anmeldung.wunsch1.name
|
||||
} : undefined,
|
||||
wunsch2: anmeldung.wunsch2 ? {
|
||||
}
|
||||
: undefined,
|
||||
wunsch2: anmeldung.wunsch2
|
||||
? {
|
||||
id: anmeldung.wunsch2.id,
|
||||
name: anmeldung.wunsch2.name
|
||||
} : undefined,
|
||||
wunsch3: anmeldung.wunsch3 ? {
|
||||
}
|
||||
: undefined,
|
||||
wunsch3: anmeldung.wunsch3
|
||||
? {
|
||||
id: anmeldung.wunsch3.id,
|
||||
name: anmeldung.wunsch3.name
|
||||
} : undefined,
|
||||
}
|
||||
: undefined,
|
||||
|
||||
assignedDienststelle: anmeldung.zugewiesen ? {
|
||||
assignedDienststelle: anmeldung.zugewiesen
|
||||
? {
|
||||
id: anmeldung.zugewiesen.id,
|
||||
name: anmeldung.zugewiesen.name
|
||||
} : undefined,
|
||||
}
|
||||
: undefined,
|
||||
|
||||
zeitraum: anmeldung.praktikum ? {
|
||||
zeitraum: anmeldung.praktikum
|
||||
? {
|
||||
id: anmeldung.praktikum.id,
|
||||
bezeichnung: anmeldung.praktikum.bezeichnung,
|
||||
startDatum: anmeldung.praktikum.startDatum.toISOString(),
|
||||
endDatum: anmeldung.praktikum.endDatum.toISOString()
|
||||
} : undefined,
|
||||
}
|
||||
: undefined,
|
||||
|
||||
timestamp: anmeldung.timestamp ? anmeldung.timestamp.getTime() : Date.now(),
|
||||
|
||||
@@ -81,7 +96,10 @@ export async function GET() {
|
||||
return json(formattedAnmeldungen);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Anmeldungen:', error);
|
||||
return json({ error: 'Fehler beim Laden der Anmeldungen', details: (error as Error).message }, { status: 500 });
|
||||
return json(
|
||||
{ error: 'Fehler beim Laden der Anmeldungen', details: (error as Error).message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +131,10 @@ export async function POST({ request, url }) {
|
||||
const zeitraumId = existingAnmeldung.praktikumId;
|
||||
|
||||
if (!zeitraumId) {
|
||||
return json({ error: 'Kein Praktikumszeitraum für diese Anmeldung gefunden' }, { status: 400 });
|
||||
return json(
|
||||
{ error: 'Kein Praktikumszeitraum für diese Anmeldung gefunden' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Prüfen ob ZeitraumPlaetze Eintrag existiert und freie Plätze vorhanden sind
|
||||
@@ -127,16 +148,22 @@ export async function POST({ request, url }) {
|
||||
});
|
||||
|
||||
if (!zeitraumPlaetze) {
|
||||
return json({
|
||||
return json(
|
||||
{
|
||||
error: 'Keine Plätze für diese Dienststelle in diesem Zeitraum konfiguriert'
|
||||
}, { status: 400 });
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Prüfen ob noch Plätze frei sind (plaetze > 0)
|
||||
if (zeitraumPlaetze.plaetze <= 0) {
|
||||
return json({
|
||||
return json(
|
||||
{
|
||||
error: 'Keine freien Plätze mehr für diese Dienststelle in diesem Zeitraum verfügbar.'
|
||||
}, { status: 400 });
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Anmeldung als angenommen markieren und Plätze in ZeitraumPlaetze reduzieren
|
||||
@@ -188,12 +215,18 @@ export async function POST({ request, url }) {
|
||||
|
||||
// Spezifische Fehlermeldung für "keine Plätze"
|
||||
if ((error as Error).message === 'Keine freien Plätze mehr verfügbar') {
|
||||
return json({
|
||||
return json(
|
||||
{
|
||||
error: 'Keine freien Plätze mehr für diese Dienststelle in diesem Zeitraum verfügbar.'
|
||||
}, { status: 400 });
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
return json({ error: 'Fehler beim Annehmen der Anmeldung', details: (error as Error).message }, { status: 500 });
|
||||
return json(
|
||||
{ error: 'Fehler beim Annehmen der Anmeldung', details: (error as Error).message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +283,10 @@ export async function PATCH({ request, url }) {
|
||||
return json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Aktualisieren der Anmeldung:', error);
|
||||
return json({ error: 'Fehler beim Aktualisieren der Anmeldung', details: (error as Error).message }, { status: 500 });
|
||||
return json(
|
||||
{ error: 'Fehler beim Aktualisieren der Anmeldung', details: (error as Error).message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,17 +306,20 @@ export async function DELETE({ url }) {
|
||||
return json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Löschen der Anmeldung:', error);
|
||||
return json({ error: 'Fehler beim Löschen der Anmeldung', details: (error as Error).message }, { status: 500 });
|
||||
return json(
|
||||
{ error: 'Fehler beim Löschen der Anmeldung', details: (error as Error).message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Hilfsfunktion: Prisma Status zu Frontend Status
|
||||
function mapPrismaStatusToFrontend(prismaStatus: string): string {
|
||||
const statusMap: Record<string, string> = {
|
||||
'OFFEN': 'pending',
|
||||
'BEARBEITUNG': 'processing',
|
||||
'ANGENOMMEN': 'accepted',
|
||||
'ABGELEHNT': 'rejected'
|
||||
OFFEN: 'pending',
|
||||
BEARBEITUNG: 'processing',
|
||||
ANGENOMMEN: 'accepted',
|
||||
ABGELEHNT: 'rejected'
|
||||
};
|
||||
|
||||
return statusMap[prismaStatus] || 'pending';
|
||||
|
||||
@@ -33,10 +33,7 @@ export async function GET({ url }) {
|
||||
zugewiesen: true,
|
||||
praktikum: true
|
||||
},
|
||||
orderBy: [
|
||||
{ zugewiesen: { name: 'asc' } },
|
||||
{ nachname: 'asc' }
|
||||
]
|
||||
orderBy: [{ zugewiesen: { name: 'asc' } }, { nachname: 'asc' }]
|
||||
});
|
||||
|
||||
// Excel-Datei erstellen
|
||||
@@ -46,7 +43,7 @@ export async function GET({ url }) {
|
||||
|
||||
const worksheet = workbook.addWorksheet('Angenommene Anmeldungen');
|
||||
|
||||
// Spalten definieren
|
||||
// Spalten definieren (inkl. Notfallkontakt)
|
||||
worksheet.columns = [
|
||||
{ header: 'Dienststelle', key: 'dienststelle', width: 30 },
|
||||
{ header: 'Anrede', key: 'anrede', width: 10 },
|
||||
@@ -65,6 +62,9 @@ export async function GET({ url }) {
|
||||
{ header: 'Note Deutsch', key: 'noteDeutsch', width: 12 },
|
||||
{ header: 'Note Mathe', key: 'noteMathe', width: 12 },
|
||||
{ header: 'Sozialverhalten', key: 'sozialverhalten', width: 35 },
|
||||
{ header: 'Notfall Vorname', key: 'notfallVorname', width: 15 },
|
||||
{ header: 'Notfall Nachname', key: 'notfallNachname', width: 15 },
|
||||
{ header: 'Notfall Telefon', key: 'notfallTelefon', width: 18 },
|
||||
{ header: 'Angenommen am', key: 'processedAt', width: 15 }
|
||||
];
|
||||
|
||||
@@ -79,8 +79,17 @@ export async function GET({ url }) {
|
||||
headerRow.alignment = { vertical: 'middle', horizontal: 'center' };
|
||||
headerRow.height = 25;
|
||||
|
||||
// Notfallkontakt-Spalten orange hervorheben
|
||||
['R1', 'S1', 'T1'].forEach((cell) => {
|
||||
worksheet.getCell(cell).fill = {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: 'FFED7D31' }
|
||||
};
|
||||
});
|
||||
|
||||
// Daten einfügen
|
||||
anmeldungen.forEach(anmeldung => {
|
||||
anmeldungen.forEach((anmeldung) => {
|
||||
worksheet.addRow({
|
||||
dienststelle: anmeldung.zugewiesen?.name || 'Nicht zugewiesen',
|
||||
anrede: anmeldung.anrede,
|
||||
@@ -99,6 +108,9 @@ export async function GET({ url }) {
|
||||
noteDeutsch: anmeldung.noteDeutsch,
|
||||
noteMathe: anmeldung.noteMathe,
|
||||
sozialverhalten: anmeldung.sozialverhalten || '-',
|
||||
notfallVorname: anmeldung.notfallVorname || '-',
|
||||
notfallNachname: anmeldung.notfallNachname || '-',
|
||||
notfallTelefon: anmeldung.notfallTelefon || '-',
|
||||
processedAt: anmeldung.processedAt
|
||||
? new Date(anmeldung.processedAt).toLocaleDateString('de-DE')
|
||||
: '-'
|
||||
@@ -132,17 +144,36 @@ export async function GET({ url }) {
|
||||
});
|
||||
});
|
||||
|
||||
// Filter aktivieren
|
||||
// Filter aktivieren (angepasst auf neue Spaltenanzahl: A bis U = 21 Spalten)
|
||||
worksheet.autoFilter = {
|
||||
from: 'A1',
|
||||
to: `R${anmeldungen.length + 1}`
|
||||
to: `U${anmeldungen.length + 1}`
|
||||
};
|
||||
|
||||
// Zusammenfassung am Ende
|
||||
const summaryRow = worksheet.addRow([]);
|
||||
const totalRow = worksheet.addRow([
|
||||
`Gesamt: ${anmeldungen.length} angenommene Anmeldungen`,
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
''
|
||||
]);
|
||||
totalRow.font = { bold: true };
|
||||
|
||||
@@ -161,22 +192,24 @@ export async function GET({ url }) {
|
||||
'Content-Length': buffer.byteLength.toString()
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Exportieren:', error);
|
||||
return json({ error: 'Fehler beim Exportieren', details: (error as Error).message }, { status: 500 });
|
||||
return json(
|
||||
{ error: 'Fehler beim Exportieren', details: (error as Error).message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Hilfsfunktion: Schulart formatieren
|
||||
function formatSchulart(schulart: string): string {
|
||||
const schulartMap: Record<string, string> = {
|
||||
'Gymnasium': 'Gymnasium',
|
||||
'KGS_Gymnasialzweig': 'KGS Gymnasialzweig',
|
||||
'Fachoberschule': 'Fachoberschule',
|
||||
'Realschule': 'Realschule',
|
||||
'KGSR': 'KGS Realschulzweig',
|
||||
'IGSR': 'IGS Realschulzweig'
|
||||
Gymnasium: 'Gymnasium',
|
||||
KGS_Gymnasialzweig: 'KGS Gymnasialzweig',
|
||||
Fachoberschule: 'Fachoberschule',
|
||||
Realschule: 'Realschule',
|
||||
KGSR: 'KGS Realschulzweig',
|
||||
IGSR: 'IGS Realschulzweig'
|
||||
};
|
||||
|
||||
return schulartMap[schulart] || schulart;
|
||||
|
||||
@@ -7,13 +7,13 @@ import { prisma } from '$lib/prisma';
|
||||
async function createZeitraumPlaetzeForZeitraum(zeitraumId: number) {
|
||||
const dienststellen = await prisma.dienststelle.findMany();
|
||||
|
||||
// Erstelle für jede existierende Dienststelle einen Eintrag mit 0 Plätzen
|
||||
// Erstelle für jede existierende Dienststelle einen Eintrag mit 1 Platz
|
||||
for (const dienststelle of dienststellen) {
|
||||
await prisma.zeitraumPlaetze.create({
|
||||
data: {
|
||||
zeitraumId: zeitraumId,
|
||||
dienststelleId: dienststelle.id,
|
||||
plaetze: 0 // Standardwert: 0 Plätze
|
||||
plaetze: 1 // Standardwert: 1 Platz pro Dienststelle
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -64,14 +64,17 @@ export const POST: RequestHandler = async ({ request, cookies }) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Automatisch ZeitraumPlaetze für alle existierenden Dienststellen erstellen
|
||||
// Automatisch ZeitraumPlaetze für alle existierenden Dienststellen erstellen (mit 1 Platz)
|
||||
await createZeitraumPlaetzeForZeitraum(zeitraum.id);
|
||||
|
||||
return json(zeitraum);
|
||||
} catch (error: any) {
|
||||
console.error('Fehler beim Erstellen des Zeitraums:', error);
|
||||
if (error.code === 'P2002') {
|
||||
return json({ error: 'Ein Zeitraum mit dieser Bezeichnung existiert bereits' }, { status: 400 });
|
||||
return json(
|
||||
{ error: 'Ein Zeitraum mit dieser Bezeichnung existiert bereits' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
return json({ error: 'Serverfehler' }, { status: 500 });
|
||||
}
|
||||
@@ -110,7 +113,10 @@ export const PATCH: RequestHandler = async ({ request, cookies }) => {
|
||||
} catch (error: any) {
|
||||
console.error('Fehler beim Aktualisieren des Zeitraums:', error);
|
||||
if (error.code === 'P2002') {
|
||||
return json({ error: 'Ein Zeitraum mit dieser Bezeichnung existiert bereits' }, { status: 400 });
|
||||
return json(
|
||||
{ error: 'Ein Zeitraum mit dieser Bezeichnung existiert bereits' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
return json({ error: 'Serverfehler' }, { status: 500 });
|
||||
}
|
||||
|
||||
@@ -57,16 +57,20 @@ export async function POST({ request }: RequestEvent) {
|
||||
schulklasse: formData.get('schulklasse') as string,
|
||||
noteDeutsch: parseInt(formData.get('noteDeutsch') as string),
|
||||
noteMathe: parseInt(formData.get('noteMathe') as string),
|
||||
sozialverhalten: formData.get('sozialverhalten') as string || null,
|
||||
sozialverhalten: (formData.get('sozialverhalten') as string) || null,
|
||||
// Praktikum
|
||||
praktikumId: parseInt(formData.get('zeitraum') as string),
|
||||
motivation: formData.get('motivation') as string || '',
|
||||
motivation: (formData.get('motivation') as string) || '',
|
||||
// Wünsche
|
||||
wunsch1Id: parseInt(formData.get('wunsch1Id') as string),
|
||||
wunsch2Id: parseInt(formData.get('wunsch2Id') as string),
|
||||
wunsch3Id: parseInt(formData.get('wunsch3Id') as string),
|
||||
// Alter (falls vom Frontend gesendet)
|
||||
alter: formData.get('alter') ? parseInt(formData.get('alter') as string) : null,
|
||||
// Notfallkontakt
|
||||
notfallVorname: (formData.get('notfallVorname') as string) || null,
|
||||
notfallNachname: (formData.get('notfallNachname') as string) || null,
|
||||
notfallTelefon: (formData.get('notfallTelefon') as string) || null,
|
||||
// System
|
||||
zugewiesenId: null,
|
||||
// timestamp wird automatisch durch @default(now()) gesetzt
|
||||
|
||||
Reference in New Issue
Block a user