Files
breakpilot-lehrer/studio-v2/app/stundenplan/_components/regeln/TeacherUnavailableWindowEditor.tsx
T
Benjamin Admin a315db0388
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 28s
CI / test-go-edu-search (push) Successful in 33s
CI / test-python-klausur (push) Failing after 2m32s
CI / test-python-agent-core (push) Successful in 19s
CI / test-nodejs-website (push) Successful in 24s
Fix JSX attribute syntax in constraint editor descriptions
German curly quotes („…") combined with a closing straight " inside
JSX attribute values were terminating the attribute prematurely, e.g.
`description="Beispiel: „X" (jugendgerecht)."` lost everything after
the inner straight quote. Switch all such descriptions to the JSX
expression form `description={"…"}` so the inner quotes are part of
a JavaScript string literal and parsed correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 23:41:24 +02:00

100 lines
5.2 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 client'
import { useState, useEffect } from 'react'
import { teacherUnavailableWindowApi, teachersApi } from '@/lib/stundenplan/api'
import type { TeacherUnavailableWindow, TimetableTeacher } from '@/app/stundenplan/types'
import { useConstraintCrud, ConstraintShell, useShellStyles, DAYS, dayLabel } from './_shell'
type FormState = Omit<TeacherUnavailableWindow, 'id' | 'created_by_user_id' | 'created_at'>
const initialForm: FormState = {
teacher_id: '', day_of_week: 2, start_time: '13:00', end_time: '17:00',
is_hard: true, weight: 100, active: true, note: '',
}
export function TeacherUnavailableWindowEditor() {
const styles = useShellStyles()
const crud = useConstraintCrud<TeacherUnavailableWindow, FormState>(teacherUnavailableWindowApi, initialForm)
const [teachers, setTeachers] = useState<TimetableTeacher[]>([])
useEffect(() => { teachersApi.list().then(setTeachers).catch(() => setTeachers([])) }, [])
const teacherLabel = (id: string): string => {
const t = teachers.find(x => x.id === id)
return t ? `${t.last_name}, ${t.first_name}` : id.slice(0, 8) + '…'
}
return (
<ConstraintShell
testId="teacher-unavailable-window-editor"
title="Lehrer: Zeitfenster nicht verfuegbar"
description={"Beispiel: „Lehrer Z Dienstags 13:0017:00 nicht"."}
newLabel="+ Neue Regel"
newDisabled={teachers.length === 0}
prereqWarning={teachers.length === 0 ? 'Zuerst Lehrer anlegen.' : null}
emptyText="Keine Regeln vorhanden."
tableHeaders={['Lehrer', 'Tag', 'Zeitfenster', 'Hart', 'Notiz']}
state={crud}
formBody={
<>
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
<div>
<label className="block text-sm mb-1 opacity-70">Lehrer</label>
<select required value={crud.form.teacher_id} onChange={e => crud.setForm({ ...crud.form, teacher_id: e.target.value })} className={`w-full px-3 py-2 rounded-lg border ${styles.inputClass}`}>
<option value="">— bitte waehlen —</option>
{teachers.map(t => <option key={t.id} value={t.id}>{t.last_name}, {t.first_name}</option>)}
</select>
</div>
<div>
<label className="block text-sm mb-1 opacity-70">Wochentag</label>
<select value={crud.form.day_of_week} onChange={e => crud.setForm({ ...crud.form, day_of_week: parseInt(e.target.value) })} className={`w-full px-3 py-2 rounded-lg border ${styles.inputClass}`}>
{DAYS.map(d => <option key={d.v} value={d.v}>{d.label}</option>)}
</select>
</div>
<div>
<label className="block text-sm mb-1 opacity-70">Von</label>
<input type="time" required value={crud.form.start_time} onChange={e => crud.setForm({ ...crud.form, start_time: e.target.value })} className={`w-full px-3 py-2 rounded-lg border ${styles.inputClass}`} />
</div>
<div>
<label className="block text-sm mb-1 opacity-70">Bis</label>
<input type="time" required value={crud.form.end_time} onChange={e => crud.setForm({ ...crud.form, end_time: e.target.value })} className={`w-full px-3 py-2 rounded-lg border ${styles.inputClass}`} />
</div>
<div className="flex items-center gap-2">
<input type="checkbox" id="is_hard_w" checked={crud.form.is_hard} onChange={e => crud.setForm({ ...crud.form, is_hard: e.target.checked })} className="w-5 h-5" />
<label htmlFor="is_hard_w" className="text-sm">Harte Regel</label>
</div>
<div>
<label className="block text-sm mb-1 opacity-70">Weight (0-100)</label>
<input type="number" min={0} max={100} value={crud.form.weight} onChange={e => crud.setForm({ ...crud.form, weight: parseInt(e.target.value) || 0 })} className={`w-full px-3 py-2 rounded-lg border ${styles.inputClass}`} />
</div>
<div className="md:col-span-2 flex items-end">
<button type="submit" disabled={crud.submitting} className={styles.submitBtn}>
{crud.submitting ? 'Speichert...' : 'Anlegen'}
</button>
</div>
</div>
<div className="mt-3">
<label className="block text-sm mb-1 opacity-70">Begruendung (optional)</label>
<input value={crud.form.note || ''} onChange={e => crud.setForm({ ...crud.form, note: e.target.value })} className={`w-full px-3 py-2 rounded-lg border ${styles.inputClass}`} />
</div>
</>
}
renderRow={(item) => {
const c = item as TeacherUnavailableWindow
return (
<tr key={c.id} className={styles.rowClass}>
<td className="px-4 py-3 font-medium">{teacherLabel(c.teacher_id)}</td>
<td className="px-4 py-3">{dayLabel(c.day_of_week)}</td>
<td className="px-4 py-3">{c.start_time}{c.end_time}</td>
<td className="px-4 py-3">{c.is_hard ? '✓' : '—'}</td>
<td className="px-4 py-3 text-sm opacity-70">{c.note || '—'}</td>
<td className="px-4 py-3 text-right">
<button onClick={() => crud.remove(c.id)} className={styles.deleteBtn}>Loeschen</button>
</td>
</tr>
)
}}
/>
)
}