Split 1257-LOC client page into _types.ts plus nine components under _components/ (TabNavigation/StatCard/FilterBar in shared, CourseCard, EnrollmentCard, CertificatesTab, EnrollmentEditModal, CourseEditModal, SettingsTab, and PageSections for header actions and empty states). Behavior preserved exactly; page.tsx is now a thin wiring shell. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
111 lines
5.0 KiB
TypeScript
111 lines
5.0 KiB
TypeScript
'use client'
|
|
|
|
import React, { useState } from 'react'
|
|
|
|
export function SettingsTab({ onSaved, saved }: { onSaved: () => void; saved: boolean }) {
|
|
const SETTINGS_KEY = 'bp_academy_settings'
|
|
|
|
const loadSettings = () => {
|
|
try {
|
|
const raw = localStorage.getItem(SETTINGS_KEY)
|
|
if (raw) return JSON.parse(raw)
|
|
} catch { /* ignore */ }
|
|
return {}
|
|
}
|
|
|
|
const defaults = { emailReminders: true, reminderDays: 7, defaultPassingScore: 70, defaultValidityDays: 365 }
|
|
const saved_settings = loadSettings()
|
|
const [emailReminders, setEmailReminders] = useState<boolean>(saved_settings.emailReminders ?? defaults.emailReminders)
|
|
const [reminderDays, setReminderDays] = useState<number>(saved_settings.reminderDays ?? defaults.reminderDays)
|
|
const [defaultPassingScore, setDefaultPassingScore] = useState<number>(saved_settings.defaultPassingScore ?? defaults.defaultPassingScore)
|
|
const [defaultValidityDays, setDefaultValidityDays] = useState<number>(saved_settings.defaultValidityDays ?? defaults.defaultValidityDays)
|
|
|
|
const handleSave = () => {
|
|
localStorage.setItem(SETTINGS_KEY, JSON.stringify({ emailReminders, reminderDays, defaultPassingScore, defaultValidityDays }))
|
|
onSaved()
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6 max-w-2xl">
|
|
{/* Notifications */}
|
|
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
|
<h3 className="text-base font-semibold text-gray-900 mb-4">Benachrichtigungen</h3>
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<div className="text-sm font-medium text-gray-700">E-Mail-Erinnerung bei ueberfaelligen Kursen</div>
|
|
<div className="text-xs text-gray-500">Mitarbeiter per E-Mail an ausstehende Schulungen erinnern</div>
|
|
</div>
|
|
<button
|
|
onClick={() => setEmailReminders(!emailReminders)}
|
|
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${emailReminders ? 'bg-purple-600' : 'bg-gray-200'}`}
|
|
>
|
|
<span className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${emailReminders ? 'translate-x-6' : 'translate-x-1'}`} />
|
|
</button>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Tage vor Ablauf erinnern</label>
|
|
<input
|
|
type="number"
|
|
min={1}
|
|
max={90}
|
|
value={reminderDays}
|
|
onChange={e => setReminderDays(Number(e.target.value))}
|
|
className="w-32 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Course Defaults */}
|
|
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
|
<h3 className="text-base font-semibold text-gray-900 mb-4">Standard-Einstellungen fuer neue Kurse</h3>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Standard-Bestehensgrenze (%)</label>
|
|
<input
|
|
type="number"
|
|
min={0}
|
|
max={100}
|
|
value={defaultPassingScore}
|
|
onChange={e => setDefaultPassingScore(Number(e.target.value))}
|
|
className="w-32 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Gueltigkeitsdauer (Tage)</label>
|
|
<input
|
|
type="number"
|
|
min={1}
|
|
value={defaultValidityDays}
|
|
onChange={e => setDefaultValidityDays(Number(e.target.value))}
|
|
className="w-32 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Info */}
|
|
<div className="bg-blue-50 border border-blue-200 rounded-xl p-4">
|
|
<div className="flex items-start gap-3">
|
|
<svg className="w-5 h-5 text-blue-600 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
<p className="text-sm text-blue-700">
|
|
Zertifikate werden automatisch nach erfolgreichem Kursabschluss generiert. Die Gueltigkeitsdauer gilt ab dem Ausstellungsdatum.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
onClick={handleSave}
|
|
className={`px-6 py-2 rounded-lg text-sm font-medium transition-colors ${
|
|
saved ? 'bg-green-600 text-white' : 'bg-purple-600 text-white hover:bg-purple-700'
|
|
}`}
|
|
>
|
|
{saved ? 'Gespeichert ✓' : 'Einstellungen speichern'}
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|