Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | 1x 1x 34x 34x 34x 9x 34x 34x 34x 34x 13x 6x 34x 3x 2x 2x 2x 1x 1x 34x 7x 27x 12x 3x | /**
* AdminSettingsTab (Story 10.26)
*
* Settings tab for the Admin Page with Email Forwarding configuration.
* Allows organizers to configure support@batbern.ch forwarding recipients.
*
* AC #8: Support contacts in Admin Settings tab
*/
import React, { useEffect, useState } from 'react';
import {
Alert,
Box,
Button,
CircularProgress,
Snackbar,
TextField,
Typography,
} from '@mui/material';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { getAdminSetting, updateAdminSetting } from '@/services/adminSettingsService';
const SUPPORT_CONTACTS_KEY = 'email-forwarding.support-contacts';
export const AdminSettingsTab: React.FC = () => {
const { t } = useTranslation('admin');
const queryClient = useQueryClient();
const { data: setting, isLoading } = useQuery({
queryKey: ['admin-settings', SUPPORT_CONTACTS_KEY],
queryFn: () => getAdminSetting(SUPPORT_CONTACTS_KEY),
});
const [supportContacts, setSupportContacts] = useState('');
const [snackbarOpen, setSnackbarOpen] = useState(false);
const [snackbarError, setSnackbarError] = useState(false);
useEffect(() => {
if (setting) {
setSupportContacts(setting.value ?? '');
}
}, [setting]);
const mutation = useMutation({
mutationFn: () => updateAdminSetting(SUPPORT_CONTACTS_KEY, supportContacts),
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: ['admin-settings', SUPPORT_CONTACTS_KEY] });
setSnackbarError(false);
setSnackbarOpen(true);
},
onError: () => {
setSnackbarError(true);
setSnackbarOpen(true);
},
});
if (isLoading) {
return (
<Box sx={{ display: 'flex', justifyContent: 'center', mt: 4 }}>
<CircularProgress />
</Box>
);
}
return (
<Box sx={{ maxWidth: 700 }} data-testid="admin-settings-tab">
<Typography variant="h6" gutterBottom>
{t('settings.emailForwarding.title', 'Email Forwarding')}
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
{t(
'settings.emailForwarding.subtitle',
'Configure email forwarding recipients for batbern.ch addresses.'
)}
</Typography>
<TextField
label={t('settings.emailForwarding.supportContacts', 'support@batbern.ch recipients')}
value={supportContacts}
onChange={(e) => {
setSupportContacts(e.target.value);
}}
fullWidth
placeholder="alice@example.ch, bob@example.ch"
helperText={t(
'settings.emailForwarding.supportContactsHelp',
'Comma-separated email addresses. If empty, emails to support@batbern.ch are forwarded to all organizers.'
)}
sx={{ mb: 3 }}
inputProps={{ 'aria-label': 'support contacts' }}
/>
<Box>
<Button
variant="contained"
onClick={() => {
mutation.mutate();
}}
disabled={mutation.isPending}
startIcon={
mutation.isPending ? <CircularProgress size={16} color="inherit" /> : undefined
}
>
{t('settings.save', 'Save')}
</Button>
</Box>
<Snackbar
open={snackbarOpen}
autoHideDuration={4000}
onClose={() => {
setSnackbarOpen(false);
}}
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
>
<Alert
severity={snackbarError ? 'error' : 'success'}
onClose={() => {
setSnackbarOpen(false);
}}
>
{snackbarError
? t('settings.saveFailed', 'Save failed. Please try again.')
: t('settings.saved', 'Settings saved.')}
</Alert>
</Snackbar>
</Box>
);
};
|