122 lines
3.6 KiB
TypeScript
122 lines
3.6 KiB
TypeScript
"use client";
|
||
/**
|
||
* Email 面板(login/register 内部切换)
|
||
*
|
||
* 原始 Dart: lib/ui/auth/widgets/auth_email_panel.dart
|
||
*
|
||
* 视觉规格(与 Dart 对齐):
|
||
* - login 模式:顶部 24px spacer + logo(120h) + 24px gap
|
||
* - register 模式:48px spacer(两个 24px,无 logo)
|
||
* - 表单(EmailLoginForm / EmailRegisterForm)
|
||
* - 12px gap + 切换链接("Don't have an account? Sign up" / "Already have an account? Log in")
|
||
* - 16px gap + "Other Sign In Options" 链接(打开底部弹层)
|
||
* - Spacer + AuthLegalText
|
||
* - 顶部 "← Back" 按钮由父级 AuthPanel 渲染(悬浮 40×40 圆形)
|
||
*/
|
||
import Image from "next/image";
|
||
import { useState } from "react";
|
||
|
||
import { useAuthState } from "@/stores/auth/auth-context";
|
||
|
||
import { AuthErrorMessage } from "./auth-error-message";
|
||
import { AuthLegalText } from "./auth-legal-text";
|
||
import { AuthOtherOptionsDialog } from "./auth-other-options-dialog";
|
||
import { EmailLoginForm } from "./email-login-form";
|
||
import { EmailRegisterForm } from "./email-register-form";
|
||
import styles from "./auth-email-panel.module.css";
|
||
|
||
export interface AuthEmailPanelProps {
|
||
/** "Other Sign-in Options" 弹层中 Facebook 按钮回调(派发回 facebook 模式) */
|
||
onSwitchToFacebook: () => void;
|
||
/** "Other Sign-in Options" 弹层中 Google 按钮回调(可空;空时不渲染 Google 行) */
|
||
onGoogle?: () => void;
|
||
/** 弹层 Google 自定义子节点 */
|
||
googleSlot?: React.ReactNode;
|
||
}
|
||
|
||
export function AuthEmailPanel({
|
||
onSwitchToFacebook,
|
||
onGoogle,
|
||
googleSlot,
|
||
}: AuthEmailPanelProps) {
|
||
const state = useAuthState();
|
||
const [mode, setMode] = useState<"login" | "register">("login");
|
||
const [showOptions, setShowOptions] = useState(false);
|
||
|
||
return (
|
||
<div className={styles.panel}>
|
||
<div className={styles.topSection}>
|
||
{mode === "login" ? (
|
||
<>
|
||
<div className={styles.spacer24} />
|
||
<Image
|
||
src="/images/auth/ic-logo-login.png"
|
||
alt="Cozsweet"
|
||
width={120}
|
||
height={120}
|
||
className={styles.logo}
|
||
priority
|
||
/>
|
||
</>
|
||
) : (
|
||
<>
|
||
<div className={styles.spacer24} />
|
||
<div className={styles.spacer24} />
|
||
</>
|
||
)}
|
||
<div className={styles.spacer24} />
|
||
</div>
|
||
|
||
{mode === "login" ? (
|
||
<EmailLoginForm
|
||
globalError={state.errorMessage}
|
||
isLoading={state.isLoading}
|
||
/>
|
||
) : (
|
||
<EmailRegisterForm
|
||
globalError={state.errorMessage}
|
||
isLoading={state.isLoading}
|
||
/>
|
||
)}
|
||
|
||
<AuthErrorMessage message={state.errorMessage} />
|
||
|
||
<button
|
||
type="button"
|
||
className={styles.toggleButton}
|
||
onClick={() => setMode((m) => (m === "login" ? "register" : "login"))}
|
||
>
|
||
{mode === "login"
|
||
? "Don't have an account? Sign up"
|
||
: "Already have an account? Log in"}
|
||
</button>
|
||
|
||
<button
|
||
type="button"
|
||
className={styles.linkButton}
|
||
onClick={() => setShowOptions(true)}
|
||
>
|
||
Other Sign In Options
|
||
</button>
|
||
|
||
<div className={styles.spacer} />
|
||
<AuthLegalText />
|
||
|
||
<AuthOtherOptionsDialog
|
||
open={showOptions}
|
||
onClose={() => setShowOptions(false)}
|
||
mode="email"
|
||
onFacebook={onSwitchToFacebook}
|
||
onGoogle={() => {
|
||
setShowOptions(false);
|
||
onGoogle?.();
|
||
}}
|
||
onEmail={() => {
|
||
// 在 email 模式下不再显示 Email 入口
|
||
}}
|
||
googleSlot={googleSlot}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|