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 */}
-
-
- {/* Main Content */}
-
-
- {/* Title */}
-
-
- ログイン
-
-
- アカウントにログインして練習を始めましょう
-
+
+
+
+
+
+
+
+
- {/* Login Card */}
-
-
- {/* Google Login */}
-
-
- {/* Divider */}
+
-
- {/* 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 && (
+
+ )}
+
+
+
+
+
+
+ パスワードを忘れた方
+
+
-
-
+
+
+
+
+ アカウントをお持ちでない方は
+
+ 新規登録
+
+
+
+
);
}
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 */}
+
+
+
+
+
+ handleInputChange("confirmPassword", 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"
+ />
+
-
- {/* Login Link */}
-
-
- すでにアカウントをお持ちの方は
-
- ログイン
+ {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 (
+
+ );
+}
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 */}
+
- >
+
) : (