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++;
}
}