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

View File

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