update
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import {
|
||||
Box,
|
||||
TextField,
|
||||
@@ -11,20 +11,49 @@ import {
|
||||
DialogContentText,
|
||||
DialogActions,
|
||||
CircularProgress,
|
||||
FormControlLabel,
|
||||
Checkbox,
|
||||
Chip,
|
||||
OutlinedInput,
|
||||
InputLabel,
|
||||
FormControl,
|
||||
Select,
|
||||
} from '@mui/material';
|
||||
import type { SelectChangeEvent } from '@mui/material';
|
||||
import SendIcon from '@mui/icons-material/Send';
|
||||
import PeopleIcon from '@mui/icons-material/People';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { adminApi } from '../../services/admin';
|
||||
import { useNotification } from '../../contexts/NotificationContext';
|
||||
import type { BroadcastPayload } from '../../types/admin.types';
|
||||
|
||||
const DIENSTGRAD_OPTIONS = [
|
||||
'Mitglied',
|
||||
'Maschinist',
|
||||
'Truppführer',
|
||||
'Gruppenführer',
|
||||
'Zugkommandant',
|
||||
'Kommandant',
|
||||
];
|
||||
|
||||
const GROUP_OPTIONS = [
|
||||
'dashboard_admin',
|
||||
'dashboard_kommando',
|
||||
'dashboard_gruppenfuehrer',
|
||||
];
|
||||
|
||||
function NotificationBroadcastTab() {
|
||||
const { showSuccess, showError } = useNotification();
|
||||
const [titel, setTitel] = useState('');
|
||||
const [nachricht, setNachricht] = useState('');
|
||||
const [schwere, setSchwere] = useState<'info' | 'warnung' | 'fehler'>('info');
|
||||
const [targetGroup, setTargetGroup] = useState('');
|
||||
const [targetDienstgrad, setTargetDienstgrad] = useState<string[]>([]);
|
||||
const [alleBenutzer, setAlleBenutzer] = useState(true);
|
||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||
const [previewCount, setPreviewCount] = useState<number | null>(null);
|
||||
const [previewLoading, setPreviewLoading] = useState(false);
|
||||
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
const broadcastMutation = useMutation({
|
||||
mutationFn: (data: BroadcastPayload) => adminApi.broadcast(data),
|
||||
@@ -34,26 +63,74 @@ function NotificationBroadcastTab() {
|
||||
setNachricht('');
|
||||
setSchwere('info');
|
||||
setTargetGroup('');
|
||||
setTargetDienstgrad([]);
|
||||
setAlleBenutzer(true);
|
||||
setPreviewCount(null);
|
||||
},
|
||||
onError: () => {
|
||||
showError('Fehler beim Senden der Benachrichtigung');
|
||||
},
|
||||
});
|
||||
|
||||
const fetchPreview = useCallback(() => {
|
||||
if (alleBenutzer) {
|
||||
// For "Alle Benutzer" we still fetch the count (no filters)
|
||||
setPreviewLoading(true);
|
||||
adminApi.broadcastPreview({})
|
||||
.then((result) => setPreviewCount(result.count))
|
||||
.catch(() => setPreviewCount(null))
|
||||
.finally(() => setPreviewLoading(false));
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: { targetGroup?: string; targetDienstgrad?: string[] } = {};
|
||||
if (targetGroup.trim()) payload.targetGroup = targetGroup.trim();
|
||||
if (targetDienstgrad.length > 0) payload.targetDienstgrad = targetDienstgrad;
|
||||
|
||||
setPreviewLoading(true);
|
||||
adminApi.broadcastPreview(payload)
|
||||
.then((result) => setPreviewCount(result.count))
|
||||
.catch(() => setPreviewCount(null))
|
||||
.finally(() => setPreviewLoading(false));
|
||||
}, [alleBenutzer, targetGroup, targetDienstgrad]);
|
||||
|
||||
useEffect(() => {
|
||||
if (debounceRef.current) clearTimeout(debounceRef.current);
|
||||
debounceRef.current = setTimeout(fetchPreview, 300);
|
||||
return () => {
|
||||
if (debounceRef.current) clearTimeout(debounceRef.current);
|
||||
};
|
||||
}, [fetchPreview]);
|
||||
|
||||
const handleSubmit = () => {
|
||||
setConfirmOpen(true);
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
setConfirmOpen(false);
|
||||
broadcastMutation.mutate({
|
||||
titel,
|
||||
nachricht,
|
||||
schwere,
|
||||
...(targetGroup.trim() ? { targetGroup: targetGroup.trim() } : {}),
|
||||
});
|
||||
const payload: BroadcastPayload = { titel, nachricht, schwere };
|
||||
if (!alleBenutzer) {
|
||||
if (targetGroup.trim()) payload.targetGroup = targetGroup.trim();
|
||||
if (targetDienstgrad.length > 0) payload.targetDienstgrad = targetDienstgrad;
|
||||
}
|
||||
broadcastMutation.mutate(payload);
|
||||
};
|
||||
|
||||
const handleDienstgradChange = (event: SelectChangeEvent<string[]>) => {
|
||||
const value = event.target.value;
|
||||
setTargetDienstgrad(typeof value === 'string' ? value.split(',') : value);
|
||||
};
|
||||
|
||||
const filtersActive = !alleBenutzer && (targetGroup.trim() || targetDienstgrad.length > 0);
|
||||
|
||||
const filterDescription = (() => {
|
||||
if (alleBenutzer) return 'alle aktiven Benutzer';
|
||||
const parts: string[] = [];
|
||||
if (targetGroup.trim()) parts.push(`Gruppe "${targetGroup.trim()}"`);
|
||||
if (targetDienstgrad.length > 0) parts.push(`Dienstgrad: ${targetDienstgrad.join(', ')}`);
|
||||
return parts.length > 0 ? parts.join(' + ') : 'alle aktiven Benutzer';
|
||||
})();
|
||||
|
||||
return (
|
||||
<Box sx={{ maxWidth: 600 }}>
|
||||
<Typography variant="h6" sx={{ mb: 2 }}>Benachrichtigung senden</Typography>
|
||||
@@ -91,15 +168,77 @@ function NotificationBroadcastTab() {
|
||||
<MenuItem value="fehler">Fehler</MenuItem>
|
||||
</TextField>
|
||||
|
||||
<TextField
|
||||
label="Zielgruppe (optional)"
|
||||
fullWidth
|
||||
value={targetGroup}
|
||||
onChange={(e) => setTargetGroup(e.target.value)}
|
||||
helperText="Leer lassen um an alle aktiven Benutzer zu senden"
|
||||
sx={{ mb: 3 }}
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={alleBenutzer}
|
||||
onChange={(e) => {
|
||||
setAlleBenutzer(e.target.checked);
|
||||
if (e.target.checked) {
|
||||
setTargetGroup('');
|
||||
setTargetDienstgrad([]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label="Alle Benutzer"
|
||||
sx={{ mb: 2, display: 'block' }}
|
||||
/>
|
||||
|
||||
{!alleBenutzer && (
|
||||
<>
|
||||
<TextField
|
||||
select
|
||||
label="Authentik-Gruppe"
|
||||
fullWidth
|
||||
value={targetGroup}
|
||||
onChange={(e) => setTargetGroup(e.target.value)}
|
||||
sx={{ mb: 2 }}
|
||||
>
|
||||
<MenuItem value="">
|
||||
<em>Keine Einschraenkung</em>
|
||||
</MenuItem>
|
||||
{GROUP_OPTIONS.map((g) => (
|
||||
<MenuItem key={g} value={g}>{g}</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
|
||||
<FormControl fullWidth sx={{ mb: 2 }}>
|
||||
<InputLabel>Dienstgrad</InputLabel>
|
||||
<Select
|
||||
multiple
|
||||
value={targetDienstgrad}
|
||||
onChange={handleDienstgradChange}
|
||||
input={<OutlinedInput label="Dienstgrad" />}
|
||||
renderValue={(selected) => (
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
|
||||
{selected.map((value) => (
|
||||
<Chip key={value} label={value} size="small" />
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
>
|
||||
{DIENSTGRAD_OPTIONS.map((d) => (
|
||||
<MenuItem key={d} value={d}>{d}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Box sx={{ mb: 3, display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<PeopleIcon color="action" fontSize="small" />
|
||||
{previewLoading ? (
|
||||
<CircularProgress size={16} />
|
||||
) : (
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{previewCount !== null
|
||||
? `Wird an ${previewCount} Benutzer gesendet`
|
||||
: 'Empfaengeranzahl wird geladen...'}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={broadcastMutation.isPending ? <CircularProgress size={18} color="inherit" /> : <SendIcon />}
|
||||
@@ -113,8 +252,10 @@ function NotificationBroadcastTab() {
|
||||
<DialogTitle>Benachrichtigung senden?</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Sind Sie sicher, dass Sie diese Benachrichtigung
|
||||
{targetGroup.trim() ? ` an die Gruppe "${targetGroup.trim()}"` : ' an alle aktiven Benutzer'} senden moechten?
|
||||
Sind Sie sicher, dass Sie diese Benachrichtigung an {filterDescription} senden moechten?
|
||||
{previewCount !== null && (
|
||||
<> ({previewCount} {previewCount === 1 ? 'Empfaenger' : 'Empfaenger'})</>
|
||||
)}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
|
||||
Reference in New Issue
Block a user