Files
smart-go/web/components/auth/LoginModal.tsx
T
2026-04-23 18:58:13 +08:00

119 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import * as Dialog from '@radix-ui/react-dialog';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { useAuthStore } from '@/stores/auth-store';
import { useAuthUiStore } from '@/stores/auth-ui-store';
export function LoginModal() {
const open = useAuthUiStore((s) => s.loginModalOpen);
const close = useAuthUiStore((s) => s.closeLoginModal);
const hint = useAuthUiStore((s) => s.loginHint);
const login = useAuthStore((s) => s.login);
const router = useRouter();
const [user, setUser] = useState('');
const [pass, setPass] = useState('');
const [tenant, setTenant] = useState('');
const [err, setErr] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function onSubmit(e: React.FormEvent) {
e.preventDefault();
setErr(null);
setLoading(true);
try {
await login(user, pass, tenant || undefined);
close();
setUser('');
setPass('');
setTenant('');
router.refresh();
} catch (ex) {
setErr(ex instanceof Error ? ex.message : String(ex));
} finally {
setLoading(false);
}
}
return (
<Dialog.Root
open={open}
onOpenChange={(next) => {
if (!next) {
close();
}
}}
>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-280 bg-black/40" />
<Dialog.Content
className="fixed left-1/2 top-1/2 z-280 w-full max-w-sm -translate-x-1/2 -translate-y-1/2 rounded-lg border border-neutral-200 bg-white p-5 shadow-xl outline-none focus:outline-none"
onPointerDownOutside={(e) => e.preventDefault()}
>
<Dialog.Title id="relogin-title" className="text-lg font-medium text-neutral-900">
</Dialog.Title>
{hint ? (
<Dialog.Description className="mt-1 text-sm text-neutral-600">{hint}</Dialog.Description>
) : (
<Dialog.Description className="sr-only"></Dialog.Description>
)}
<form onSubmit={onSubmit} className="mt-4 space-y-3">
<label className="block text-sm">
<span className="text-neutral-600"></span>
<input
className="mt-1 w-full rounded border border-neutral-300 px-2 py-1"
value={user}
onChange={(e) => setUser(e.target.value)}
autoComplete="username"
required
/>
</label>
<label className="block text-sm">
<span className="text-neutral-600"></span>
<input
type="password"
className="mt-1 w-full rounded border border-neutral-300 px-2 py-1"
value={pass}
onChange={(e) => setPass(e.target.value)}
autoComplete="current-password"
required
/>
</label>
<label className="block text-sm">
<span className="text-neutral-600"> ID</span>
<input
className="mt-1 w-full rounded border border-neutral-300 px-2 py-1"
value={tenant}
onChange={(e) => setTenant(e.target.value)}
/>
</label>
{err ? <p className="text-sm text-red-600">{err}</p> : null}
<div className="flex justify-end gap-2 pt-2">
<button
type="button"
className="rounded border border-neutral-300 px-3 py-1.5 text-sm"
onClick={() => {
close();
setErr(null);
}}
>
</button>
<button
type="submit"
disabled={loading}
className="rounded bg-neutral-900 px-3 py-1.5 text-sm text-white disabled:opacity-50"
>
{loading ? '提交中…' : '登录'}
</button>
</div>
</form>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}