diff options
| author | Maze <mazewinther@gmail.com> | 2025-06-29 14:48:26 +0200 |
|---|---|---|
| committer | Maze <mazewinther@gmail.com> | 2025-06-29 14:48:26 +0200 |
| commit | 4e0352d4d6f1be48a5148ac7e866d61945656e85 (patch) | |
| tree | bbaab017fa8779ab8cac65efdc0e2908384cc5c9 /apps/web/src/app | |
| parent | 8aa0aeb6e3a42ee3d1a2e0759506c636f3c8037a (diff) | |
| parent | 306c2885f1c34a8fb16c0a499efebcd4cb796c93 (diff) | |
| download | opencut-4e0352d4d6f1be48a5148ac7e866d61945656e85.tar.gz | |
Merge branch 'main' of https://github.com/mazeincoding/AppCut
Diffstat (limited to 'apps/web/src/app')
| -rw-r--r-- | apps/web/src/app/(auth)/login/page.tsx | 216 | ||||
| -rw-r--r-- | apps/web/src/app/(auth)/signup/page.tsx | 259 |
2 files changed, 201 insertions, 274 deletions
diff --git a/apps/web/src/app/(auth)/login/page.tsx b/apps/web/src/app/(auth)/login/page.tsx index 755784fa..ec79bd7f 100644 --- a/apps/web/src/app/(auth)/login/page.tsx +++ b/apps/web/src/app/(auth)/login/page.tsx @@ -1,7 +1,6 @@ "use client"; import { useRouter } from "next/navigation"; -import { signIn } from "@opencut/auth/client"; import { Button } from "@/components/ui/button"; import { Card, @@ -10,7 +9,7 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { Suspense, useState } from "react"; +import { memo, Suspense } from "react"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; @@ -18,121 +17,22 @@ import Link from "next/link"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { ArrowLeft, Loader2 } from "lucide-react"; import { GoogleIcon } from "@/components/icons"; +import { useLogin } from "@/hooks/auth/useLogin"; -function LoginForm() { - const router = useRouter(); - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - const [error, setError] = useState<string | null>(null); - const [isEmailLoading, setIsEmailLoading] = useState(false); - const [isGoogleLoading, setIsGoogleLoading] = useState(false); - - const handleLogin = async () => { - setError(null); - setIsEmailLoading(true); - - const { error } = await signIn.email({ - email, - password, - }); - - if (error) { - setError(error.message || "An unexpected error occurred."); - setIsEmailLoading(false); - return; - } - - router.push("/editor"); - }; - - const handleGoogleLogin = async () => { - setError(null); - setIsGoogleLoading(true); - - try { - await signIn.social({ - provider: "google", - callbackURL: "/editor", - }); - } catch (error) { - setError("Failed to sign in with Google. Please try again."); - setIsGoogleLoading(false); - } - }; - - const isAnyLoading = isEmailLoading || isGoogleLoading; - - return ( - <div className="flex flex-col space-y-6"> - {error && ( - <Alert variant="destructive"> - <AlertTitle>Error</AlertTitle> - <AlertDescription>{error}</AlertDescription> - </Alert> - )} - - <Button - onClick={handleGoogleLogin} - variant="outline" - size="lg" - disabled={isAnyLoading} - > - {isGoogleLoading ? ( - <Loader2 className="animate-spin" /> - ) : ( - <GoogleIcon /> - )}{" "} - Continue with Google - </Button> - <div className="relative"> - <div className="absolute inset-0 flex items-center"> - <Separator className="w-full" /> - </div> - <div className="relative flex justify-center text-xs uppercase"> - <span className="bg-background px-2 text-muted-foreground"> - Or continue with - </span> - </div> - </div> - <div className="space-y-4"> - <div className="space-y-2"> - <Label htmlFor="email">Email</Label> - <Input - id="email" - type="email" - placeholder="m@example.com" - value={email} - onChange={(e) => setEmail(e.target.value)} - disabled={isAnyLoading} - className="h-11" - /> - </div> - <div className="space-y-2"> - <Label htmlFor="password">Password</Label> - <Input - id="password" - type="password" - value={password} - onChange={(e) => setPassword(e.target.value)} - disabled={isAnyLoading} - className="h-11" - /> - </div> - <Button - onClick={handleLogin} - disabled={isAnyLoading || !email || !password} - className="w-full h-11" - size="lg" - > - {isEmailLoading ? <Loader2 className="animate-spin" /> : "Sign in"} - </Button> - </div> - </div> - ); -} - -export default function LoginPage() { +const LoginPage = () => { const router = useRouter(); + const { + email, + setEmail, + password, + setPassword, + error, + isAnyLoading, + isEmailLoading, + isGoogleLoading, + handleLogin, + handleGoogleLogin, + } = useLogin(); return ( <div className="flex h-screen items-center justify-center relative"> @@ -158,19 +58,85 @@ export default function LoginPage() { </div> } > - <LoginForm /> + <div className="flex flex-col space-y-6"> + {error && ( + <Alert variant="destructive"> + <AlertTitle>Error</AlertTitle> + <AlertDescription>{error}</AlertDescription> + </Alert> + )} + + <Button + onClick={handleGoogleLogin} + variant="outline" + size="lg" + disabled={isAnyLoading} + > + {isGoogleLoading ? ( + <Loader2 className="animate-spin" /> + ) : ( + <GoogleIcon /> + )}{" "} + Continue with Google + </Button> + <div className="relative"> + <div className="absolute inset-0 flex items-center"> + <Separator className="w-full" /> + </div> + <div className="relative flex justify-center text-xs uppercase"> + <span className="bg-background px-2 text-muted-foreground"> + Or continue with + </span> + </div> + </div> + <div className="space-y-4"> + <div className="space-y-2"> + <Label htmlFor="email">Email</Label> + <Input + id="email" + type="email" + placeholder="m@example.com" + value={email} + onChange={(e) => setEmail(e.target.value)} + disabled={isAnyLoading} + className="h-11" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="password">Password</Label> + <Input + id="password" + type="password" + value={password} + onChange={(e) => setPassword(e.target.value)} + disabled={isAnyLoading} + className="h-11" + /> + </div> + <Button + onClick={handleLogin} + disabled={isAnyLoading || !email || !password} + className="w-full h-11" + size="lg" + > + {isEmailLoading ? <Loader2 className="animate-spin" /> : "Sign in"} + </Button> + </div> + </div> + <div className="mt-6 text-center text-sm"> + Don't have an account?{" "} + <Link + href="/signup" + className="font-medium text-primary underline-offset-4 hover:underline" + > + Sign up + </Link> + </div> </Suspense> - <div className="mt-6 text-center text-sm"> - Don't have an account?{" "} - <Link - href="/signup" - className="font-medium text-primary underline-offset-4 hover:underline" - > - Sign up - </Link> - </div> </CardContent> </Card> </div> ); } + +export default memo(LoginPage); diff --git a/apps/web/src/app/(auth)/signup/page.tsx b/apps/web/src/app/(auth)/signup/page.tsx index 1c98b9e1..e9705213 100644 --- a/apps/web/src/app/(auth)/signup/page.tsx +++ b/apps/web/src/app/(auth)/signup/page.tsx @@ -1,7 +1,6 @@ "use client"; import { useRouter } from "next/navigation"; -import { signUp, signIn } from "@opencut/auth/client"; import { Button } from "@/components/ui/button"; import { Card, @@ -10,151 +9,32 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { Suspense, useState } from "react"; +import { memo, Suspense } from "react"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import Link from "next/link"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { Loader2, ArrowLeft } from "lucide-react"; +import { ArrowLeft, Loader2 } from "lucide-react"; import { GoogleIcon } from "@/components/icons"; +import { useSignUp } from "@/hooks/auth/useSignUp"; -function SignUpForm() { - const router = useRouter(); - const [name, setName] = useState(""); - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - const [error, setError] = useState<string | null>(null); - const [isEmailLoading, setIsEmailLoading] = useState(false); - const [isGoogleLoading, setIsGoogleLoading] = useState(false); - - const handleSignUp = async () => { - setError(null); - setIsEmailLoading(true); - - const { error } = await signUp.email({ - name, - email, - password, - }); - - if (error) { - setError(error.message || "An unexpected error occurred."); - setIsEmailLoading(false); - return; - } - - router.push("/login"); - }; - - const handleGoogleSignUp = async () => { - setError(null); - setIsGoogleLoading(true); - - try { - await signIn.social({ - provider: "google", - }); - - router.push("/editor"); - } catch (error) { - setError("Failed to sign up with Google. Please try again."); - setIsGoogleLoading(false); - } - }; - - const isAnyLoading = isEmailLoading || isGoogleLoading; - - return ( - <div className="flex flex-col space-y-6"> - {error && ( - <Alert variant="destructive"> - <AlertTitle>Error</AlertTitle> - <AlertDescription>{error}</AlertDescription> - </Alert> - )} - - <Button - onClick={handleGoogleSignUp} - variant="outline" - size="lg" - disabled={isAnyLoading} - > - {isGoogleLoading ? ( - <Loader2 className="animate-spin" /> - ) : ( - <GoogleIcon /> - )}{" "} - Continue with Google - </Button> - - <div className="relative"> - <div className="absolute inset-0 flex items-center"> - <Separator className="w-full" /> - </div> - <div className="relative flex justify-center text-xs uppercase"> - <span className="bg-background px-2 text-muted-foreground"> - Or continue with - </span> - </div> - </div> - - <div className="space-y-4"> - <div className="space-y-2"> - <Label htmlFor="name">Full Name</Label> - <Input - id="name" - type="text" - placeholder="John Doe" - value={name} - onChange={(e) => setName(e.target.value)} - disabled={isAnyLoading} - className="h-11" - /> - </div> - <div className="space-y-2"> - <Label htmlFor="email">Email</Label> - <Input - id="email" - type="email" - placeholder="m@example.com" - value={email} - onChange={(e) => setEmail(e.target.value)} - disabled={isAnyLoading} - className="h-11" - /> - </div> - <div className="space-y-2"> - <Label htmlFor="password">Password</Label> - <Input - id="password" - type="password" - placeholder="Create a strong password" - value={password} - onChange={(e) => setPassword(e.target.value)} - disabled={isAnyLoading} - className="h-11" - /> - </div> - <Button - onClick={handleSignUp} - disabled={isAnyLoading || !name || !email || !password} - className="w-full h-11" - size="lg" - > - {isEmailLoading ? ( - <Loader2 className="animate-spin" /> - ) : ( - "Create account" - )} - </Button> - </div> - </div> - ); -} - -export default function SignUpPage() { +const SignUpPage = () => { const router = useRouter(); + const { + name, + setName, + email, + setEmail, + password, + setPassword, + error, + isAnyLoading, + isEmailLoading, + isGoogleLoading, + handleSignUp, + handleGoogleSignUp, + } = useSignUp(); return ( <div className="flex h-screen items-center justify-center relative"> @@ -165,7 +45,6 @@ export default function SignUpPage() { > <ArrowLeft className="h-5 w-5" /> Back </Button> - <Card className="w-[400px] shadow-lg border-0"> <CardHeader className="text-center pb-4"> <CardTitle className="text-2xl font-semibold"> @@ -183,19 +62,101 @@ export default function SignUpPage() { </div> } > - <SignUpForm /> + <div className="flex flex-col space-y-6"> + {error && ( + <Alert variant="destructive"> + <AlertTitle>Error</AlertTitle> + <AlertDescription>{error}</AlertDescription> + </Alert> + )} + <Button + onClick={handleGoogleSignUp} + variant="outline" + size="lg" + disabled={isAnyLoading} + > + {isGoogleLoading ? ( + <Loader2 className="animate-spin" /> + ) : ( + <GoogleIcon /> + )}{" "} + Continue with Google + </Button> + <div className="relative"> + <div className="absolute inset-0 flex items-center"> + <Separator className="w-full" /> + </div> + <div className="relative flex justify-center text-xs uppercase"> + <span className="bg-background px-2 text-muted-foreground"> + Or continue with + </span> + </div> + </div> + <div className="space-y-4"> + <div className="space-y-2"> + <Label htmlFor="name">Full Name</Label> + <Input + id="name" + type="text" + placeholder="John Doe" + value={name} + onChange={(e) => setName(e.target.value)} + disabled={isAnyLoading} + className="h-11" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="email">Email</Label> + <Input + id="email" + type="email" + placeholder="m@example.com" + value={email} + onChange={(e) => setEmail(e.target.value)} + disabled={isAnyLoading} + className="h-11" + /> + </div> + <div className="space-y-2"> + <Label htmlFor="password">Password</Label> + <Input + id="password" + type="password" + placeholder="Create a strong password" + value={password} + onChange={(e) => setPassword(e.target.value)} + disabled={isAnyLoading} + className="h-11" + /> + </div> + <Button + onClick={handleSignUp} + disabled={isAnyLoading || !name || !email || !password} + className="w-full h-11" + size="lg" + > + {isEmailLoading ? ( + <Loader2 className="animate-spin" /> + ) : ( + "Create account" + )} + </Button> + </div> + </div> + <div className="mt-6 text-center text-sm"> + Already have an account?{" "} + <Link + href="/login" + className="font-medium text-primary underline-offset-4 hover:underline" + > + Sign in + </Link> + </div> </Suspense> - <div className="mt-6 text-center text-sm"> - Already have an account?{" "} - <Link - href="/login" - className="font-medium text-primary underline-offset-4 hover:underline" - > - Sign in - </Link> - </div> </CardContent> </Card> </div> ); } + +export default memo(SignUpPage); |
