update
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
||||
Info,
|
||||
ExpandMore,
|
||||
PictureAsPdf as PdfIcon,
|
||||
Settings as SettingsIcon,
|
||||
} from '@mui/icons-material';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
@@ -82,6 +83,9 @@ function AdminSettings() {
|
||||
const [pdfLogo, setPdfLogo] = useState('');
|
||||
const [pdfOrgName, setPdfOrgName] = useState('');
|
||||
|
||||
// State for app logo
|
||||
const [appLogo, setAppLogo] = useState('');
|
||||
|
||||
// Fetch all settings
|
||||
const { data: settings, isLoading } = useQuery({
|
||||
queryKey: ['admin-settings'],
|
||||
@@ -119,6 +123,8 @@ function AdminSettings() {
|
||||
if (pdfLogoSetting?.value != null) setPdfLogo(pdfLogoSetting.value);
|
||||
const pdfOrgNameSetting = settings.find((s) => s.key === 'pdf_org_name');
|
||||
if (pdfOrgNameSetting?.value != null) setPdfOrgName(pdfOrgNameSetting.value);
|
||||
const appLogoSetting = settings.find((s) => s.key === 'app_logo');
|
||||
if (appLogoSetting?.value != null) setAppLogo(appLogoSetting.value);
|
||||
}
|
||||
}, [settings]);
|
||||
|
||||
@@ -197,6 +203,30 @@ function AdminSettings() {
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
// App logo mutation + handlers
|
||||
const appLogoMutation = useMutation({
|
||||
mutationFn: (value: string) => settingsApi.update('app_logo', value),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['admin-settings'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['pdf-settings'] });
|
||||
},
|
||||
});
|
||||
const handleAppLogoUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (ev) => setAppLogo(ev.target?.result as string);
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
const handleSaveAppLogo = async () => {
|
||||
try {
|
||||
await appLogoMutation.mutateAsync(appLogo);
|
||||
showSuccess('Logo gespeichert');
|
||||
} catch {
|
||||
showError('Fehler beim Speichern des Logos');
|
||||
}
|
||||
};
|
||||
|
||||
if (!canAccess) {
|
||||
return <Navigate to="/dashboard" replace />;
|
||||
}
|
||||
@@ -276,7 +306,59 @@ function AdminSettings() {
|
||||
</Typography>
|
||||
|
||||
<Stack spacing={3}>
|
||||
{/* Section 1: Link Collections */}
|
||||
{/* Section 1: General Settings (App Logo) */}
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
||||
<SettingsIcon color="primary" sx={{ mr: 2 }} />
|
||||
<Typography variant="h6">Allgemeine Einstellungen</Typography>
|
||||
</Box>
|
||||
<Divider sx={{ mb: 2 }} />
|
||||
<Stack spacing={2}>
|
||||
<Box>
|
||||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
||||
Logo (wird im Header und in PDF-Exporten verwendet)
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||
<Button component="label" variant="outlined" size="small">
|
||||
Logo hochladen
|
||||
<input
|
||||
type="file"
|
||||
hidden
|
||||
accept="image/*"
|
||||
onChange={handleAppLogoUpload}
|
||||
/>
|
||||
</Button>
|
||||
{appLogo && (
|
||||
<>
|
||||
<Box
|
||||
component="img"
|
||||
src={appLogo}
|
||||
alt="Logo Vorschau"
|
||||
sx={{ height: 40, maxWidth: 120, objectFit: 'contain', borderRadius: 1 }}
|
||||
/>
|
||||
<IconButton size="small" onClick={() => setAppLogo('')} title="Logo entfernen">
|
||||
<Delete fontSize="small" />
|
||||
</IconButton>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box>
|
||||
<Button
|
||||
onClick={handleSaveAppLogo}
|
||||
variant="contained"
|
||||
size="small"
|
||||
disabled={appLogoMutation.isPending}
|
||||
>
|
||||
Speichern
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Section 2: Link Collections */}
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
||||
@@ -375,7 +457,7 @@ function AdminSettings() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Section 2: Refresh Intervals */}
|
||||
{/* Section 3: Refresh Intervals */}
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
||||
@@ -439,7 +521,7 @@ function AdminSettings() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Section 3: PDF Settings */}
|
||||
{/* Section 4: PDF Settings */}
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
||||
@@ -459,35 +541,6 @@ function AdminSettings() {
|
||||
size="small"
|
||||
placeholder="FREIWILLIGE FEUERWEHR REMS"
|
||||
/>
|
||||
<Box>
|
||||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
||||
Logo (erscheint rechts im PDF-Header, neben dem Organisationsnamen)
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||
<Button component="label" variant="outlined" size="small">
|
||||
Logo hochladen
|
||||
<input
|
||||
type="file"
|
||||
hidden
|
||||
accept="image/*"
|
||||
onChange={handleLogoUpload}
|
||||
/>
|
||||
</Button>
|
||||
{pdfLogo && (
|
||||
<>
|
||||
<Box
|
||||
component="img"
|
||||
src={pdfLogo}
|
||||
alt="Logo Vorschau"
|
||||
sx={{ height: 40, maxWidth: 120, objectFit: 'contain', borderRadius: 1 }}
|
||||
/>
|
||||
<IconButton size="small" onClick={() => setPdfLogo('')} title="Logo entfernen">
|
||||
<Delete fontSize="small" />
|
||||
</IconButton>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<TextField
|
||||
label="PDF Kopfzeile (links, unter dem Header-Banner)"
|
||||
value={pdfHeader}
|
||||
@@ -527,7 +580,7 @@ function AdminSettings() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Section 4: Info */}
|
||||
{/* Section 5: Info */}
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
||||
|
||||
Reference in New Issue
Block a user