Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
co-authored by
renee-png
parent
d7e7de54d3
commit
a2a4e6c811
@@ -0,0 +1,96 @@
|
||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||
import { useState } from "react";
|
||||
import { z } from "zod";
|
||||
import { supabase } from "@/integrations/supabase/client";
|
||||
import { lovable } from "@/integrations/lovable";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
||||
import { toast } from "sonner";
|
||||
import { GraduationCap } from "lucide-react";
|
||||
|
||||
const searchSchema = z.object({ mode: z.enum(["signin", "signup"]).optional() });
|
||||
|
||||
export const Route = createFileRoute("/auth")({
|
||||
validateSearch: searchSchema,
|
||||
head: () => ({ meta: [{ title: "Sign in — School Portal" }] }),
|
||||
component: AuthPage,
|
||||
});
|
||||
|
||||
function AuthPage() {
|
||||
const navigate = useNavigate();
|
||||
const { mode } = Route.useSearch();
|
||||
const [tab, setTab] = useState<"signin" | "signup">(mode ?? "signin");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [fullName, setFullName] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const onSignIn = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { error } = await supabase.auth.signInWithPassword({ email, password });
|
||||
setLoading(false);
|
||||
if (error) return toast.error(error.message);
|
||||
navigate({ to: "/dashboard" });
|
||||
};
|
||||
|
||||
const onSignUp = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { error } = await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: { emailRedirectTo: window.location.origin, data: { full_name: fullName } },
|
||||
});
|
||||
setLoading(false);
|
||||
if (error) return toast.error(error.message);
|
||||
toast.success("Account created. You can sign in now.");
|
||||
setTab("signin");
|
||||
};
|
||||
|
||||
const onGoogle = async () => {
|
||||
const res = await lovable.auth.signInWithOAuth("google", { redirect_uri: window.location.origin });
|
||||
if (res.error) toast.error(res.error.message ?? "Google sign-in failed");
|
||||
else if (!res.redirected) navigate({ to: "/dashboard" });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-muted/30 p-4">
|
||||
<div className="w-full max-w-md bg-card border rounded-lg p-8 shadow-sm">
|
||||
<div className="flex items-center gap-2 mb-6">
|
||||
<GraduationCap className="h-6 w-6 text-primary" />
|
||||
<h1 className="font-semibold text-lg">School Portal</h1>
|
||||
</div>
|
||||
<Tabs value={tab} onValueChange={(v) => setTab(v as "signin" | "signup")}>
|
||||
<TabsList className="grid grid-cols-2 w-full">
|
||||
<TabsTrigger value="signin">Sign in</TabsTrigger>
|
||||
<TabsTrigger value="signup">Create account</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="signin">
|
||||
<form onSubmit={onSignIn} className="space-y-4 mt-4">
|
||||
<div><Label>Email</Label><Input type="email" required value={email} onChange={(e) => setEmail(e.target.value)} /></div>
|
||||
<div><Label>Password</Label><Input type="password" required value={password} onChange={(e) => setPassword(e.target.value)} /></div>
|
||||
<Button type="submit" className="w-full" disabled={loading}>Sign in</Button>
|
||||
</form>
|
||||
</TabsContent>
|
||||
<TabsContent value="signup">
|
||||
<form onSubmit={onSignUp} className="space-y-4 mt-4">
|
||||
<div><Label>Full name</Label><Input required value={fullName} onChange={(e) => setFullName(e.target.value)} /></div>
|
||||
<div><Label>Email</Label><Input type="email" required value={email} onChange={(e) => setEmail(e.target.value)} /></div>
|
||||
<div><Label>Password</Label><Input type="password" required minLength={6} value={password} onChange={(e) => setPassword(e.target.value)} /></div>
|
||||
<Button type="submit" className="w-full" disabled={loading}>Create account</Button>
|
||||
<p className="text-xs text-muted-foreground">The first account becomes admin. Other accounts default to parent — an admin can change your role.</p>
|
||||
</form>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<div className="my-4 flex items-center gap-3 text-xs text-muted-foreground">
|
||||
<div className="flex-1 border-t" /> OR <div className="flex-1 border-t" />
|
||||
</div>
|
||||
<Button variant="outline" className="w-full" onClick={onGoogle}>Continue with Google</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user