diff --git a/frontend/src/pages/Settings.tsx b/frontend/src/pages/Settings.tsx index 8259ea8..c6657d8 100644 --- a/frontend/src/pages/Settings.tsx +++ b/frontend/src/pages/Settings.tsx @@ -15,8 +15,9 @@ import { CircularProgress, Button, Chip, + TextField, } from '@mui/material'; -import { Settings as SettingsIcon, Notifications, Palette, Language, SettingsBrightness, LightMode, DarkMode, Widgets, Cloud, LinkOff, Forum } from '@mui/icons-material'; +import { Settings as SettingsIcon, Notifications, Palette, Language, SettingsBrightness, LightMode, DarkMode, Widgets, Cloud, LinkOff, Forum, Badge } from '@mui/icons-material'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import DashboardLayout from '../components/dashboard/DashboardLayout'; import { useThemeMode } from '../contexts/ThemeContext'; @@ -24,6 +25,8 @@ import { preferencesApi } from '../services/settings'; import { WIDGETS, WidgetKey } from '../constants/widgets'; import { nextcloudApi } from '../services/nextcloud'; import { useNotification } from '../contexts/NotificationContext'; +import { useAuth } from '../contexts/AuthContext'; +import { membersService } from '../services/members'; const POLL_INTERVAL = 2000; const POLL_TIMEOUT = 5 * 60 * 1000; @@ -31,7 +34,35 @@ const POLL_TIMEOUT = 5 * 60 * 1000; function Settings() { const { themeMode, setThemeMode } = useThemeMode(); const queryClient = useQueryClient(); - const { showInfo } = useNotification(); + const { showInfo, showSuccess, showError } = useNotification(); + const { user } = useAuth(); + + // FDISK Standesbuchnummer + const [standesNr, setStandesNr] = useState(''); + const [standesNrEdited, setStandesNrEdited] = useState(false); + + const { data: memberProfile, isLoading: profileLoading } = useQuery({ + queryKey: ['member', user?.id], + queryFn: () => membersService.getMember(user!.id), + enabled: !!user?.id, + }); + + useEffect(() => { + if (memberProfile?.profile?.fdisk_standesbuch_nr != null) { + setStandesNr(memberProfile.profile.fdisk_standesbuch_nr); + } + }, [memberProfile]); + + const standesNrMutation = useMutation({ + mutationFn: (value: string) => + membersService.updateMember(user!.id, { fdisk_standesbuch_nr: value || undefined }), + onSuccess: () => { + showSuccess('Standesbuchnummer gespeichert'); + setStandesNrEdited(false); + queryClient.invalidateQueries({ queryKey: ['member', user?.id] }); + }, + onError: () => showError('Speichern fehlgeschlagen'), + }); const { data: preferences, isLoading: prefsLoading } = useQuery({ queryKey: ['user-preferences'], @@ -251,6 +282,49 @@ function Settings() { + {/* FDISK-Profil */} + + + + + + FDISK-Profil + + + + Die Standesbuchnummer wird für die automatische FDISK-Synchronisation verwendet. + + {profileLoading ? ( + + + + ) : ( + + { + setStandesNr(e.target.value); + setStandesNrEdited(true); + }} + placeholder="z.B. 123456" + sx={{ flex: 1 }} + /> + + + )} + + + + {/* Notification Settings */} diff --git a/sync/src/db.ts b/sync/src/db.ts index b3efa8e..7a8def4 100644 --- a/sync/src/db.ts +++ b/sync/src/db.ts @@ -171,9 +171,10 @@ export async function syncToDatabase( `UPDATE mitglieder_profile SET updated_at = NOW() WHERE user_id = $1`, [userId] ); - log(`Forced update ${member.vorname} ${member.zuname} (${member.standesbuchNr}): no changes, timestamp refreshed`); + log(`Forced ${member.vorname} ${member.zuname} (${member.standesbuchNr}): ${member.status}, ${dienstgrad ?? member.dienstgrad}, Eintritt ${member.eintrittsdatum ?? '—'}`); forced++; } else { + log(`OK ${member.vorname} ${member.zuname} (${member.standesbuchNr}): ${member.status}, ${dienstgrad ?? member.dienstgrad}, Eintritt ${member.eintrittsdatum ?? '—'}`); unchanged++; } } @@ -218,6 +219,7 @@ export async function syncToDatabase( log(`New Ausbildung: ${ausb.standesbuchNr} — ${ausb.kursname}${ausb.kursDatum ? ` (${ausb.kursDatum})` : ''}`); ausbildungNew++; } else { + log(`Updated Ausbildung: ${ausb.standesbuchNr} — ${ausb.kursname}${ausb.kursDatum ? ` (${ausb.kursDatum})` : ''}`); ausbildungUpdated++; } }