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:
parent
a9a58d8aa2
commit
c9b5f93569
|
|
@ -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,18 +11,32 @@ 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;
|
||||||
enabled: false,
|
sender_name: string;
|
||||||
sender_name: '', sender_address: '',
|
sender_address: string;
|
||||||
smtp_host: '', smtp_port: '587', smtp_encryption: 'starttls',
|
smtp_host: string;
|
||||||
smtp_self_signed: false,
|
smtp_port: string;
|
||||||
smtp_username: '', smtp_password: '',
|
smtp_encryption: string;
|
||||||
allow_user_config: false,
|
smtp_self_signed: boolean;
|
||||||
global_recipient: '',
|
smtp_username: string;
|
||||||
};
|
smtp_password: string;
|
||||||
|
allow_user_config: boolean;
|
||||||
|
global_recipient: string;
|
||||||
|
}
|
||||||
|
|
||||||
const [cfg, setCfg] = useState(DEFAULTS);
|
const DEFAULTS: EmailConfig = {
|
||||||
|
enabled: false,
|
||||||
|
sender_name: '', sender_address: '',
|
||||||
|
smtp_host: '', smtp_port: '587', smtp_encryption: 'starttls',
|
||||||
|
smtp_self_signed: false,
|
||||||
|
smtp_username: '', smtp_password: '',
|
||||||
|
allow_user_config: false,
|
||||||
|
global_recipient: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
@ -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 {
|
||||||
Loading…
Reference in New Issue