update
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useRef, useEffect, useCallback } from 'react';
|
||||
import { useState, useRef, useEffect, useCallback } from 'react';
|
||||
import {
|
||||
Box, Button, Card, CardContent, Chip, CircularProgress, IconButton, Tooltip, Typography,
|
||||
Box, Button, Card, CardContent, Checkbox, Chip, CircularProgress, FormControlLabel, IconButton, Tooltip, Typography,
|
||||
} from '@mui/material';
|
||||
import SyncIcon from '@mui/icons-material/Sync';
|
||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||
@@ -12,6 +12,7 @@ function FdiskSyncTab() {
|
||||
const queryClient = useQueryClient();
|
||||
const { showSuccess, showError } = useNotification();
|
||||
const logBoxRef = useRef<HTMLDivElement>(null);
|
||||
const [force, setForce] = useState(false);
|
||||
|
||||
const { data, isLoading, isError } = useQuery({
|
||||
queryKey: ['admin', 'fdisk-sync', 'logs'],
|
||||
@@ -27,7 +28,7 @@ function FdiskSyncTab() {
|
||||
}, [data?.logs.length]);
|
||||
|
||||
const triggerMutation = useMutation({
|
||||
mutationFn: adminApi.fdiskSyncTrigger,
|
||||
mutationFn: (forceSync: boolean) => adminApi.fdiskSyncTrigger(forceSync),
|
||||
onSuccess: () => {
|
||||
showSuccess('Sync gestartet');
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'fdisk-sync', 'logs'] });
|
||||
@@ -71,12 +72,16 @@ function FdiskSyncTab() {
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<SyncIcon />}
|
||||
onClick={() => triggerMutation.mutate()}
|
||||
onClick={() => triggerMutation.mutate(force)}
|
||||
disabled={running || triggerMutation.isPending}
|
||||
>
|
||||
Jetzt synchronisieren
|
||||
</Button>
|
||||
</Box>
|
||||
<FormControlLabel
|
||||
control={<Checkbox checked={force} onChange={(e) => setForce(e.target.checked)} />}
|
||||
label="Alle Mitglieder erzwungen synchronisieren"
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
CardContent,
|
||||
CardHeader,
|
||||
Avatar,
|
||||
Autocomplete,
|
||||
Button,
|
||||
Chip,
|
||||
Tabs,
|
||||
@@ -41,9 +42,11 @@ import {
|
||||
MemberWithProfile,
|
||||
StatusEnum,
|
||||
DienstgradEnum,
|
||||
FunktionEnum,
|
||||
TshirtGroesseEnum,
|
||||
DIENSTGRAD_VALUES,
|
||||
STATUS_VALUES,
|
||||
FUNKTION_VALUES,
|
||||
TSHIRT_GROESSE_VALUES,
|
||||
STATUS_LABELS,
|
||||
STATUS_COLORS,
|
||||
@@ -239,6 +242,7 @@ function MitgliedDetail() {
|
||||
funktion: member.profile.funktion,
|
||||
status: member.profile.status,
|
||||
eintrittsdatum: toGermanDate(member.profile.eintrittsdatum) || undefined,
|
||||
austrittsdatum: toGermanDate(member.profile.austrittsdatum) || undefined,
|
||||
geburtsdatum: toGermanDate(member.profile.geburtsdatum) || undefined,
|
||||
telefon_mobil: member.profile.telefon_mobil ?? undefined,
|
||||
telefon_privat: member.profile.telefon_privat ?? undefined,
|
||||
@@ -248,6 +252,7 @@ function MitgliedDetail() {
|
||||
tshirt_groesse: member.profile.tshirt_groesse ?? undefined,
|
||||
schuhgroesse: member.profile.schuhgroesse ?? undefined,
|
||||
bemerkungen: member.profile.bemerkungen ?? undefined,
|
||||
fdisk_standesbuch_nr: member.profile.fdisk_standesbuch_nr ?? undefined,
|
||||
});
|
||||
}
|
||||
}, [member]);
|
||||
@@ -263,6 +268,7 @@ function MitgliedDetail() {
|
||||
const payload: UpdateMemberProfileData = {
|
||||
...formData,
|
||||
eintrittsdatum: formData.eintrittsdatum ? fromGermanDate(formData.eintrittsdatum) || undefined : undefined,
|
||||
austrittsdatum: formData.austrittsdatum ? fromGermanDate(formData.austrittsdatum) || undefined : undefined,
|
||||
geburtsdatum: formData.geburtsdatum ? fromGermanDate(formData.geburtsdatum) || undefined : undefined,
|
||||
dienstgrad_seit: formData.dienstgrad_seit ? fromGermanDate(formData.dienstgrad_seit) || undefined : undefined,
|
||||
};
|
||||
@@ -282,12 +288,23 @@ function MitgliedDetail() {
|
||||
// Reset form to current profile values
|
||||
if (member?.profile) {
|
||||
setFormData({
|
||||
mitglieds_nr: member.profile.mitglieds_nr ?? undefined,
|
||||
dienstgrad: member.profile.dienstgrad ?? undefined,
|
||||
dienstgrad_seit: toGermanDate(member.profile.dienstgrad_seit) || undefined,
|
||||
funktion: member.profile.funktion,
|
||||
status: member.profile.status,
|
||||
eintrittsdatum: toGermanDate(member.profile.eintrittsdatum) || undefined,
|
||||
austrittsdatum: toGermanDate(member.profile.austrittsdatum) || undefined,
|
||||
geburtsdatum: toGermanDate(member.profile.geburtsdatum) || undefined,
|
||||
telefon_mobil: member.profile.telefon_mobil ?? undefined,
|
||||
telefon_privat: member.profile.telefon_privat ?? undefined,
|
||||
notfallkontakt_name: member.profile.notfallkontakt_name ?? undefined,
|
||||
notfallkontakt_telefon: member.profile.notfallkontakt_telefon ?? undefined,
|
||||
fuehrerscheinklassen: member.profile.fuehrerscheinklassen,
|
||||
tshirt_groesse: member.profile.tshirt_groesse ?? undefined,
|
||||
schuhgroesse: member.profile.schuhgroesse ?? undefined,
|
||||
bemerkungen: member.profile.bemerkungen ?? undefined,
|
||||
fdisk_standesbuch_nr: member.profile.fdisk_standesbuch_nr ?? undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -542,6 +559,35 @@ function MitgliedDetail() {
|
||||
onChange={(e) => handleFieldChange('geburtsdatum', e.target.value || undefined)}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Austrittsdatum"
|
||||
fullWidth
|
||||
size="small"
|
||||
placeholder="TT.MM.JJJJ"
|
||||
value={formData.austrittsdatum ?? ''}
|
||||
onChange={(e) => handleFieldChange('austrittsdatum', e.target.value || undefined)}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Standesbuchnummer"
|
||||
fullWidth
|
||||
size="small"
|
||||
value={formData.fdisk_standesbuch_nr ?? ''}
|
||||
onChange={(e) => handleFieldChange('fdisk_standesbuch_nr', e.target.value || undefined)}
|
||||
/>
|
||||
|
||||
<Autocomplete
|
||||
multiple
|
||||
options={[...FUNKTION_VALUES]}
|
||||
value={formData.funktion ?? []}
|
||||
onChange={(_e, newValue) => handleFieldChange('funktion', newValue as FunktionEnum[])}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label="Funktion" size="small" />
|
||||
)}
|
||||
size="small"
|
||||
/>
|
||||
</Stack>
|
||||
) : (
|
||||
<>
|
||||
@@ -574,6 +620,13 @@ function MitgliedDetail() {
|
||||
: null
|
||||
}
|
||||
/>
|
||||
<FieldRow
|
||||
label="Austrittsdatum"
|
||||
value={profile?.austrittsdatum
|
||||
? new Date(profile.austrittsdatum).toLocaleDateString('de-AT')
|
||||
: null}
|
||||
/>
|
||||
<FieldRow label="Standesbuchnummer" value={profile?.fdisk_standesbuch_nr ?? null} />
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
@@ -700,7 +753,19 @@ function MitgliedDetail() {
|
||||
title="Führerscheinklassen"
|
||||
/>
|
||||
<CardContent>
|
||||
{profile?.fuehrerscheinklassen && profile.fuehrerscheinklassen.length > 0 ? (
|
||||
{editMode && canWrite ? (
|
||||
<Autocomplete
|
||||
multiple
|
||||
freeSolo
|
||||
options={['AM', 'A', 'B', 'BE', 'C', 'CE', 'C1', 'C1E', 'D', 'DE', 'D1', 'D1E', 'F']}
|
||||
value={formData.fuehrerscheinklassen ?? []}
|
||||
onChange={(_e, newValue) => handleFieldChange('fuehrerscheinklassen', newValue)}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label="Führerscheinklassen" size="small" placeholder="Klasse hinzufügen…" />
|
||||
)}
|
||||
size="small"
|
||||
/>
|
||||
) : profile?.fuehrerscheinklassen && profile.fuehrerscheinklassen.length > 0 ? (
|
||||
<Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap' }}>
|
||||
{profile.fuehrerscheinklassen.map((k) => (
|
||||
<Chip key={k} label={k} size="small" variant="outlined" />
|
||||
|
||||
@@ -28,5 +28,5 @@ export const adminApi = {
|
||||
broadcast: (data: BroadcastPayload) => api.post<ApiResponse<{ sent: number }>>('/api/admin/notifications/broadcast', data).then(r => r.data.data),
|
||||
getPingHistory: (serviceId: string) => api.get<ApiResponse<PingHistoryEntry[]>>(`/api/admin/services/${serviceId}/ping-history`).then(r => r.data.data),
|
||||
fdiskSyncLogs: () => api.get<ApiResponse<FdiskSyncLogsResponse>>('/api/admin/fdisk-sync/logs').then(r => r.data.data),
|
||||
fdiskSyncTrigger: () => api.post<ApiResponse<{ started: boolean }>>('/api/admin/fdisk-sync/trigger').then(r => r.data.data),
|
||||
fdiskSyncTrigger: (force = false) => api.post<ApiResponse<{ started: boolean }>>('/api/admin/fdisk-sync/trigger', { force }).then(r => r.data.data),
|
||||
};
|
||||
|
||||
@@ -73,6 +73,7 @@ export interface MitgliederProfile {
|
||||
tshirt_groesse: TshirtGroesseEnum | null;
|
||||
schuhgroesse: string | null;
|
||||
bemerkungen: string | null;
|
||||
fdisk_standesbuch_nr: string | null;
|
||||
bild_url: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
|
||||
Reference in New Issue
Block a user