All files / src/components/organizer/Admin AdminSettingsTab.tsx

92.3% Statements 24/26
91.66% Branches 11/12
80% Functions 8/10
92.3% Lines 24/26

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