diff --git a/.gitignore b/.gitignore index 6e0dbfd..d093a4a 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,6 @@ Thumbs.db .idea/ .vscode/settings.json *~ + +.claude +.codex \ No newline at end of file diff --git a/frontend/src/app/login/page.tsx b/frontend/src/app/login/page.tsx index 98f7607..883963f 100644 --- a/frontend/src/app/login/page.tsx +++ b/frontend/src/app/login/page.tsx @@ -2,10 +2,10 @@ import { useId, useState } from "react"; import { useRouter } from "next/navigation"; -import { Heart, Mail, Lock, Loader2 } from "lucide-react"; +import { Mail, Lock, Loader2 } from "lucide-react"; import Link from "next/link"; import { Button } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; +import { AuthShell, AuthCard, AuthTitle, GoogleAuthButton } from "@/components/auth"; import { signIn } from "@/lib/auth-client"; export default function LoginPage() { @@ -63,175 +63,100 @@ export default function LoginPage() { }; return ( -
- {/* Header */} -
-
- - - 恋ai - -
-
- - {/* Main Content */} -
-
- {/* Title */} -
-

- ログイン -

-

- アカウントにログインして練習を始めましょう -

+ + + + +
+ + +
+
+
+
+
+ または +
- {/* Login Card */} - -
- {/* Google Login */} - - - {/* Divider */} +
+
+
-
-
-
-
- または -
+ + setEmail(e.target.value)} + placeholder="example@email.com" + required + className="w-full pl-10 pr-4 py-2 rounded-lg border-2 border-border bg-background text-foreground focus:outline-none focus:border-primary" + />
+
- {/* Email/Password Login */} - - {/* Email Input */} -
- -
- - setEmail(e.target.value)} - placeholder="example@email.com" - required - className="w-full pl-10 pr-4 py-2 rounded-lg border-2 border-border bg-background text-foreground focus:outline-none focus:border-primary" - /> -
-
- - {/* Password Input */} -
- -
- - setPassword(e.target.value)} - placeholder="••••••••" - required - className="w-full pl-10 pr-4 py-2 rounded-lg border-2 border-border bg-background text-foreground focus:outline-none focus:border-primary" - /> -
-
- - {/* Error Message */} - {error && ( -
-

{error}

-
- )} - - {/* Login Button */} - - - - {/* Forgot Password */} -
- - パスワードを忘れた方 - +
+ +
+ + setPassword(e.target.value)} + placeholder="••••••••" + required + className="w-full pl-10 pr-4 py-2 rounded-lg border-2 border-border bg-background text-foreground focus:outline-none focus:border-primary" + />
- - - {/* Sign Up Link */} - -

- アカウントをお持ちでない方は - - 新規登録 - -

-
+ + {error && ( +
+

{error}

+
+ )} + + + + +
+ + パスワードを忘れた方 + +
-
-
+ + + +

+ アカウントをお持ちでない方は + + 新規登録 + +

+
+ ); } diff --git a/frontend/src/app/signup/page.tsx b/frontend/src/app/signup/page.tsx index 90fbed5..eb12913 100644 --- a/frontend/src/app/signup/page.tsx +++ b/frontend/src/app/signup/page.tsx @@ -2,10 +2,10 @@ import { useId, useState } from "react"; import { useRouter } from "next/navigation"; -import { Heart, Mail, Lock, User, Loader2 } from "lucide-react"; +import { Mail, Lock, User, Loader2 } from "lucide-react"; import Link from "next/link"; import { Button } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; +import { AuthShell, AuthCard, AuthTitle, GoogleAuthButton } from "@/components/auth"; import { signUp, signIn } from "@/lib/auth-client"; export default function SignupPage() { @@ -32,7 +32,6 @@ export default function SignupPage() { setIsLoading(true); setError(null); - // Validation if (formData.password !== formData.confirmPassword) { setError("パスワードが一致しません"); setIsLoading(false); @@ -52,7 +51,6 @@ export default function SignupPage() { name: formData.name, }); - // サインアップ成功 router.push("/"); } catch (err: unknown) { console.error("Signup error:", err); @@ -87,221 +85,158 @@ export default function SignupPage() { }; return ( -
- {/* Header */} -
-
- -
-
- - {/* Main Content */} -
-
- {/* Title */} -
-

- 新規登録 -

-

- アカウントを作成して練習を始めましょう -

+ + + + +
+ + +
+
+
+
+
+ または +
- {/* Signup Card */} - -
- {/* Google Signup */} - - - {/* Divider */} +
+
+
-
-
-
-
- または -
+
+
- {/* Email/Password Signup */} - - {/* Name Input */} -
- -
-
-
- - {/* Email Input */} -
- -
-
-
- - {/* Password Input */} -
- -
-
-
- - {/* Confirm Password Input */} -
- -
-
-
- - {/* Error Message */} - {error && ( -
-

{error}

-
- )} +
+ +
+
+
- {/* Terms */} -

- 登録することで、 - - 利用規約 - - と - - プライバシーポリシー - - に同意したものとみなされます。 -

+
+ +
+
+
- {/* Signup Button */} - - +
+ +
+
- - {/* Login Link */} - -

- すでにアカウントをお持ちの方は - - ログイン + {error && ( +

+

{error}

+
+ )} + +

+ 登録することで、 + + 利用規約 + と + + プライバシーポリシー + + に同意したものとみなされます。

-
+ + +
-
-
+ + + +

+ すでにアカウントをお持ちの方は + + ログイン + +

+
+ ); } diff --git a/frontend/src/components/auth/AuthCard.tsx b/frontend/src/components/auth/AuthCard.tsx new file mode 100644 index 0000000..a83c3af --- /dev/null +++ b/frontend/src/components/auth/AuthCard.tsx @@ -0,0 +1,24 @@ +import type { ComponentProps } from "react"; + +import { Card } from "@/components/ui/card"; +import { cn } from "@/lib/utils"; + +type AuthCardProps = ComponentProps & { + withPadding?: boolean; +}; + +export function AuthCard({ + children, + className, + withPadding = true, + ...props +}: AuthCardProps) { + return ( + + {children} + + ); +} diff --git a/frontend/src/components/auth/AuthShell.tsx b/frontend/src/components/auth/AuthShell.tsx new file mode 100644 index 0000000..99056a1 --- /dev/null +++ b/frontend/src/components/auth/AuthShell.tsx @@ -0,0 +1,33 @@ +import type { ReactNode } from "react"; +import Link from "next/link"; +import { Heart } from "lucide-react"; + +type AuthShellProps = { + children: ReactNode; +}; + +export function AuthShell({ children }: AuthShellProps) { + return ( +
+
+
+ +
+
+ +
+
{children}
+
+
+ ); +} diff --git a/frontend/src/components/auth/AuthTitle.tsx b/frontend/src/components/auth/AuthTitle.tsx new file mode 100644 index 0000000..94c69e8 --- /dev/null +++ b/frontend/src/components/auth/AuthTitle.tsx @@ -0,0 +1,13 @@ +type AuthTitleProps = { + title: string; + description: string; +}; + +export function AuthTitle({ title, description }: AuthTitleProps) { + return ( +
+

{title}

+

{description}

+
+ ); +} diff --git a/frontend/src/components/auth/GoogleAuthButton.tsx b/frontend/src/components/auth/GoogleAuthButton.tsx new file mode 100644 index 0000000..185a02a --- /dev/null +++ b/frontend/src/components/auth/GoogleAuthButton.tsx @@ -0,0 +1,64 @@ +import { Loader2 } from "lucide-react"; + +import { Button } from "@/components/ui/button"; + +type GoogleAuthButtonProps = { + label: string; + isLoading: boolean; + onClick: () => void | Promise; +}; + +export function GoogleAuthButton({ + label, + isLoading, + onClick, +}: GoogleAuthButtonProps) { + return ( + + ); +} + +function GoogleIcon() { + return ( + + ); +} diff --git a/frontend/src/components/auth/index.ts b/frontend/src/components/auth/index.ts new file mode 100644 index 0000000..4a6ba4f --- /dev/null +++ b/frontend/src/components/auth/index.ts @@ -0,0 +1,4 @@ +export { AuthShell } from "./AuthShell"; +export { AuthCard } from "./AuthCard"; +export { AuthTitle } from "./AuthTitle"; +export { GoogleAuthButton } from "./GoogleAuthButton"; diff --git a/frontend/src/components/simulation/UserVideoDisplay.tsx b/frontend/src/components/simulation/UserVideoDisplay.tsx index 8015bde..4433be8 100644 --- a/frontend/src/components/simulation/UserVideoDisplay.tsx +++ b/frontend/src/components/simulation/UserVideoDisplay.tsx @@ -14,23 +14,21 @@ export const UserVideoDisplay = memo( return (
{stream && videoEnabled ? ( - <> -
- - {/* User Label */} -
-

-

-
+
+ + {/* User Label */} +
+

+

- +
) : (