diff options
| author | iza <59828082+izadoesdev@users.noreply.github.com> | 2025-06-24 07:56:56 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-24 07:56:56 +0300 |
| commit | e367d8960d8018b6c3445c8a7ff0022b8571bbc0 (patch) | |
| tree | e8bce314b6c2fbdee5358487354afe0353b8dff2 /apps/web/src/app/(auth)/login/page.tsx | |
| parent | 326da414d21026bad635cdaca05bf534ecf545cf (diff) | |
| parent | ad0476e78f6e5afa93a14cba563c229a5a198b62 (diff) | |
| download | opencut-e367d8960d8018b6c3445c8a7ff0022b8571bbc0.tar.gz | |
Merge pull request #24 from 3raphat/fix/auth-url
fix: update auth routes
Diffstat (limited to 'apps/web/src/app/(auth)/login/page.tsx')
| -rw-r--r-- | apps/web/src/app/(auth)/login/page.tsx | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/apps/web/src/app/(auth)/login/page.tsx b/apps/web/src/app/(auth)/login/page.tsx new file mode 100644 index 00000000..5f37ced9 --- /dev/null +++ b/apps/web/src/app/(auth)/login/page.tsx @@ -0,0 +1,176 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { signIn } from "@/lib/auth-client"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Suspense, useState } 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 { ArrowLeft, Loader2 } from "lucide-react"; +import { GoogleIcon } from "@/components/icons"; + +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", + }); + router.push("/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 router = useRouter(); + + return ( + <div className="flex h-screen items-center justify-center relative"> + <Button + variant="ghost" + onClick={() => router.back()} + className="absolute top-6 left-6" + > + <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">Welcome back</CardTitle> + <CardDescription className="text-base"> + Sign in to your account to continue + </CardDescription> + </CardHeader> + <CardContent className="pt-0"> + <Suspense + fallback={ + <div className="text-center"> + <Loader2 className="animate-spin" /> + </div> + } + > + <LoginForm /> + </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> + ); +} |
