import { IconBook2 as Book, IconLock as Lock, IconMail as Mail, IconShield as Shield, IconLockOpen as Unlock, } from '@tabler/icons-react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { toast } from 'sonner'; import { PageHeader } from '@/components/PageHeader'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Switch } from '@/components/ui/switch'; import { authSettingsQueryKey, useAuthSettings } from '@/hooks/api'; import { apiJson } from '@/lib/api'; import { ContentPanel } from '../../components/ContentPanel'; type LockedAccount = { email: string; attemptCount: number; firstAttempt: string; lastAttempt: string; }; const lockedAccountsQueryKey = ['admin', 'locked-accounts']; export default function AdminSettings() { const { t } = useTranslation(); const queryClient = useQueryClient(); const { data: authSettings, isLoading: mfaLoading } = useAuthSettings(); const mfaEnabled = authSettings?.settings?.mfaEnabled ?? false; const userDocsEnabled = authSettings?.settings?.userDocsEnabled ?? true; const apiDocsEnabled = authSettings?.settings?.apiDocsEnabled ?? true; const { data: lockedAccountsData, isLoading: lockedLoading } = useQuery({ queryKey: lockedAccountsQueryKey, queryFn: async () => { const data = await apiJson<{ lockedAccounts?: LockedAccount[] }>( '/api/v1/auth/admin/locked-accounts', {}, 'Failed to fetch locked accounts' ); return data.lockedAccounts || []; }, }); const lockedAccounts = lockedAccountsData ?? []; const mfaToggleMutation = useMutation({ mutationFn: (enabled: boolean) => apiJson( '/api/v1/auth/admin/settings', { method: 'PATCH', body: { mfa_enabled: enabled } }, 'Failed to update MFA setting' ), onSuccess: () => { queryClient.invalidateQueries({ queryKey: authSettingsQueryKey }); }, onError: () => { toast.error('Failed to update MFA setting'); }, }); const userDocsToggleMutation = useMutation({ mutationFn: (enabled: boolean) => apiJson( '/api/v1/auth/admin/settings', { method: 'PATCH', body: { user_docs_enabled: enabled } }, t('admin.settings.documentation.userDocsUpdateFailed') ), onSuccess: () => { queryClient.invalidateQueries({ queryKey: authSettingsQueryKey }); }, onError: () => { toast.error(t('admin.settings.documentation.userDocsUpdateFailed')); }, }); const apiDocsToggleMutation = useMutation({ mutationFn: (enabled: boolean) => apiJson( '/api/v1/auth/admin/settings', { method: 'PATCH', body: { api_docs_enabled: enabled } }, t('admin.settings.documentation.apiDocsUpdateFailed') ), onSuccess: () => { queryClient.invalidateQueries({ queryKey: authSettingsQueryKey }); }, onError: () => { toast.error(t('admin.settings.documentation.apiDocsUpdateFailed')); }, }); const unlockMutation = useMutation({ mutationFn: (email: string) => apiJson( '/api/v1/auth/admin/unlock-account', { method: 'POST', body: { email } }, 'Failed to unlock account' ), onSuccess: () => { queryClient.invalidateQueries({ queryKey: lockedAccountsQueryKey }); }, onError: () => { toast.error('Failed to unlock account'); }, }); return ( <>
{/* Security Settings */} Security
Require MFA
Users must set up two-factor authentication
{mfaLoading ? (
Loading...
) : ( mfaToggleMutation.mutate(enabled)} disabled={mfaToggleMutation.isPending} /> )}
{/* Documentation */} {t('admin.settings.documentation.title')} {t('admin.settings.documentation.description')}
{t('admin.settings.documentation.userDocsLabel')}
{t('admin.settings.documentation.userDocsDescription')}
{mfaLoading ? (
{t('common.loading')}
) : ( userDocsToggleMutation.mutate(enabled)} disabled={userDocsToggleMutation.isPending} /> )}
{t('admin.settings.documentation.apiDocsLabel')}
{t('admin.settings.documentation.apiDocsDescription')}
{mfaLoading ? (
{t('common.loading')}
) : ( apiDocsToggleMutation.mutate(enabled)} disabled={apiDocsToggleMutation.isPending} /> )}
{/* Email Setup */} Email (SMTP) Shared by Supabase Auth (auth emails) and the app (transactional emails).{' '} Resend {' '} recommended.
                {`# .env.local
SMTP_HOST=smtp.resend.com
SMTP_PORT=465
SMTP_USER=resend
SMTP_PASS=re_xxxx_your_key
SMTP_ADMIN_EMAIL=noreply@yourdomain.com
SMTP_SENDER_NAME=YourApp`}
              
{/* Locked Accounts */} Locked Accounts Accounts temporarily locked due to failed login attempts {lockedLoading ? (
Loading...
) : lockedAccounts.length === 0 ? (
No accounts are currently locked.
) : (
{lockedAccounts.map((account) => (
{account.email}
{account.attemptCount} failed attempts
))}
)}
); }