refactor(ts): convert LoginModeCard + EmailNotifCard admin cards to TSX (B18)

LoginModeCard (AuthModeConfig; AdminUser[]) and EmailNotifCard (EmailConfig +
keyed set(k,v)). typecheck 0, build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
null 2026-07-04 21:19:39 -05:00
parent a9a58d8aa2
commit c9b5f93569
2 changed files with 49 additions and 27 deletions

View File

@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { Eye, EyeOff } from 'lucide-react'; import { Eye, EyeOff } from 'lucide-react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { api } from '@/api'; import { api } from '@/api';
import { errMessage } from '@/lib/utils';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
@ -10,8 +11,21 @@ import {
} from '@/components/ui/select'; } from '@/components/ui/select';
import { SectionHeading, FieldRow, Toggle } from './adminShared'; import { SectionHeading, FieldRow, Toggle } from './adminShared';
export default function EmailNotifCard() { interface EmailConfig {
const DEFAULTS = { enabled: boolean;
sender_name: string;
sender_address: string;
smtp_host: string;
smtp_port: string;
smtp_encryption: string;
smtp_self_signed: boolean;
smtp_username: string;
smtp_password: string;
allow_user_config: boolean;
global_recipient: string;
}
const DEFAULTS: EmailConfig = {
enabled: false, enabled: false,
sender_name: '', sender_address: '', sender_name: '', sender_address: '',
smtp_host: '', smtp_port: '587', smtp_encryption: 'starttls', smtp_host: '', smtp_port: '587', smtp_encryption: 'starttls',
@ -19,9 +33,10 @@ export default function EmailNotifCard() {
smtp_username: '', smtp_password: '', smtp_username: '', smtp_password: '',
allow_user_config: false, allow_user_config: false,
global_recipient: '', global_recipient: '',
}; };
const [cfg, setCfg] = useState(DEFAULTS); export default function EmailNotifCard() {
const [cfg, setCfg] = useState<EmailConfig>(DEFAULTS);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [loadError, setLoadError] = useState(''); const [loadError, setLoadError] = useState('');
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
@ -31,12 +46,12 @@ export default function EmailNotifCard() {
useEffect(() => { useEffect(() => {
api.notifAdmin() api.notifAdmin()
.then(d => setCfg({ ...DEFAULTS, ...d })) .then(d => setCfg({ ...DEFAULTS, ...(d as Partial<EmailConfig>) }))
.catch(err => setLoadError(err.message || 'Failed to load email settings')) .catch(err => setLoadError(errMessage(err, 'Failed to load email settings')))
.finally(() => setLoading(false)); .finally(() => setLoading(false));
}, []); // eslint-disable-line react-hooks/exhaustive-deps }, []); // eslint-disable-line react-hooks/exhaustive-deps
const set = (k, v) => setCfg(p => ({ ...p, [k]: v })); const set = (k: keyof EmailConfig, v: string | boolean) => setCfg(p => ({ ...p, [k]: v }) as EmailConfig);
const handleSave = async () => { const handleSave = async () => {
setSaving(true); setSaving(true);
@ -44,7 +59,7 @@ export default function EmailNotifCard() {
await api.saveNotifAdmin(cfg); await api.saveNotifAdmin(cfg);
toast.success('Email settings saved.'); toast.success('Email settings saved.');
} catch (err) { } catch (err) {
toast.error(err.message || 'Failed to save.'); toast.error(errMessage(err, 'Failed to save.'));
} finally { } finally {
setSaving(false); setSaving(false);
} }
@ -57,7 +72,7 @@ export default function EmailNotifCard() {
await api.testEmail({ to: testEmail }); await api.testEmail({ to: testEmail });
toast.success('Test email sent.'); toast.success('Test email sent.');
} catch (err) { } catch (err) {
toast.error(err.message || 'Failed to send test email.'); toast.error(errMessage(err, 'Failed to send test email.'));
} finally { } finally {
setTesting(false); setTesting(false);
} }

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { api } from '@/api'; import { api } from '@/api';
import { cn } from '@/lib/utils'; import { cn, errMessage } from '@/lib/utils';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
@ -15,9 +15,15 @@ import {
} from '@/components/ui/alert-dialog'; } from '@/components/ui/alert-dialog';
import { LogIn, UserCheck, ShieldCheck } from 'lucide-react'; import { LogIn, UserCheck, ShieldCheck } from 'lucide-react';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import type { AdminUser } from './UsersTable';
export default function LoginModeCard({ users, onModeChange }) { interface AuthModeConfig {
const [modeData, setModeData] = useState(null); auth_mode?: string;
default_user_id?: number | null;
}
export default function LoginModeCard({ users, onModeChange }: { users?: AdminUser[]; onModeChange?: (mode: string) => void }) {
const [modeData, setModeData] = useState<AuthModeConfig | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [loadError, setLoadError] = useState(''); const [loadError, setLoadError] = useState('');
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
@ -27,25 +33,26 @@ export default function LoginModeCard({ users, onModeChange }) {
useEffect(() => { useEffect(() => {
api.authModeConfig() api.authModeConfig()
.then(d => { .then(raw => {
const d = raw as AuthModeConfig;
setModeData(d); setModeData(d);
const mode = d.auth_mode === 'single' ? 'single' : 'multi'; const mode = d.auth_mode === 'single' ? 'single' : 'multi';
setSelected(mode); setSelected(mode);
setSelectedUser(d.default_user_id?.toString() || ''); setSelectedUser(d.default_user_id?.toString() || '');
onModeChange?.(mode); onModeChange?.(mode);
}) })
.catch(err => setLoadError(err.message || 'Failed to load login mode config')) .catch(err => setLoadError(errMessage(err, 'Failed to load login mode config')))
.finally(() => setLoading(false)); .finally(() => setLoading(false));
}, []); // eslint-disable-line }, []); // eslint-disable-line
const doSetMode = async (mode, userId) => { const doSetMode = async (mode: string, userId: string | null) => {
setSaving(true); setSaving(true);
try { try {
await api.setAuthMode({ await api.setAuthMode({
auth_mode: mode, auth_mode: mode,
default_user_id: mode === 'single' ? parseInt(userId, 10) : null, default_user_id: mode === 'single' ? parseInt(userId || '', 10) : null,
}); });
const d = await api.authModeConfig(); const d = await api.authModeConfig() as AuthModeConfig;
setModeData(d); setModeData(d);
const resolved = d.auth_mode === 'single' ? 'single' : 'multi'; const resolved = d.auth_mode === 'single' ? 'single' : 'multi';
setSelected(resolved); setSelected(resolved);
@ -53,7 +60,7 @@ export default function LoginModeCard({ users, onModeChange }) {
onModeChange?.(resolved); onModeChange?.(resolved);
toast.success(mode === 'single' ? 'No Login mode enabled.' : 'Login requirement restored.'); toast.success(mode === 'single' ? 'No Login mode enabled.' : 'Login requirement restored.');
} catch (err) { } catch (err) {
toast.error(err.message || 'Failed to update login mode.'); toast.error(errMessage(err, 'Failed to update login mode.'));
// revert UI selection on error // revert UI selection on error
setSelected(modeData?.auth_mode === 'single' ? 'single' : 'multi'); setSelected(modeData?.auth_mode === 'single' ? 'single' : 'multi');
} finally { } finally {