Seed: education template (18 files)
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Menu, GraduationCap } from "lucide-react";
|
||||
import { ThemeToggle } from "@/components/ThemeToggle";
|
||||
import { useAuth } from "@/hooks/use-tygarun";
|
||||
import { toast } from "sonner";
|
||||
|
||||
function AuthDialog({ trigger }: { trigger: React.ReactNode }) {
|
||||
const { signin, signup, isSignedIn, signout, user } = useAuth();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [mode, setMode] = useState<"signin" | "signup">("signin");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
if (isSignedIn) {
|
||||
return (
|
||||
<Button variant="ghost" size="sm" className="rounded-full" onClick={() => { signout(); toast.success("Signed out."); }}>
|
||||
{user?.name || user?.email || "Account"} · Sign Out
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
const submit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setBusy(true);
|
||||
try {
|
||||
if (mode === "signup") await signup({ email, password });
|
||||
else await signin(email, password);
|
||||
toast.success(mode === "signup" ? "Account created!" : "Welcome back!");
|
||||
setOpen(false);
|
||||
} catch { toast.error("Authentication failed. Please try again."); }
|
||||
finally { setBusy(false); }
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>{trigger}</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{mode === "signup" ? "Create your account" : "Sign in"}</DialogTitle>
|
||||
<DialogDescription>{mode === "signup" ? "Start learning in minutes." : "Access your courses and progress."}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form onSubmit={submit} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="auth-email">Email</Label>
|
||||
<Input id="auth-email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="you@example.com" required />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="auth-password">Password</Label>
|
||||
<Input id="auth-password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="••••••••" required />
|
||||
</div>
|
||||
<Button type="submit" disabled={busy} className="w-full bg-indigo-600 hover:bg-indigo-700 text-white rounded-full">
|
||||
{busy ? "Please wait…" : mode === "signup" ? "Create Account" : "Sign In"}
|
||||
</Button>
|
||||
</form>
|
||||
<button type="button" className="text-sm text-indigo-600 hover:underline" onClick={() => setMode(mode === "signup" ? "signin" : "signup")}>
|
||||
{mode === "signup" ? "Already have an account? Sign in" : "New here? Create an account"}
|
||||
</button>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Header() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const fn = () => setScrolled(window.scrollY > 20);
|
||||
window.addEventListener("scroll", fn);
|
||||
return () => window.removeEventListener("scroll", fn);
|
||||
}, []);
|
||||
|
||||
const links = [
|
||||
{ label: "Courses", to: "/courses" },
|
||||
{ label: "Instructors", to: "/instructors" },
|
||||
{ label: "Pricing", to: "/courses" },
|
||||
{ label: "Contact", to: "/contact" },
|
||||
];
|
||||
|
||||
return (
|
||||
<header className={`fixed top-0 inset-x-0 z-50 transition-all duration-300 \${scrolled ? "bg-white/90 dark:bg-slate-950/95 backdrop-blur-lg border-b border-slate-200 dark:border-slate-800 shadow-sm" : "bg-transparent"}`}>
|
||||
<div className="mx-auto max-w-7xl flex items-center justify-between px-6 py-4">
|
||||
<Link to="/" className="flex items-center gap-2 group">
|
||||
<div className="relative">
|
||||
<GraduationCap className={`h-7 w-7 transition-colors \${scrolled ? "text-indigo-600" : "text-white"}`} />
|
||||
</div>
|
||||
<span className={`text-lg font-bold tracking-tight transition-colors \${scrolled ? "text-slate-900 dark:text-white" : "text-white"}`}>Luminar Academy</span>
|
||||
</Link>
|
||||
<nav className="hidden md:flex items-center gap-8">
|
||||
{links.map((l) => (
|
||||
<Link key={l.to} to={l.to} className={`text-sm font-medium transition-colors hover:text-indigo-500 \${scrolled ? "text-slate-600 dark:text-slate-300" : "text-white/90"}`}>{l.label}</Link>
|
||||
))}
|
||||
</nav>
|
||||
<div className="hidden md:flex items-center gap-3">
|
||||
<ThemeToggle />
|
||||
<AuthDialog trigger={<Button variant="ghost" size="sm" className={`rounded-full \${scrolled ? "text-slate-700 dark:text-slate-200" : "text-white hover:text-white"}`}>Sign In</Button>} />
|
||||
<Button asChild size="sm" className="bg-indigo-600 hover:bg-indigo-700 text-white rounded-full px-6 shadow-lg shadow-indigo-500/20 active:scale-[0.97] transition-all">
|
||||
<Link to="/courses">Enroll Now</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<Sheet open={open} onOpenChange={setOpen}>
|
||||
<SheetTrigger asChild className="md:hidden">
|
||||
<Button variant="ghost" size="icon" className={`\${scrolled ? "text-slate-900 dark:text-white" : "text-white"}`}><Menu className="h-5 w-5" /></Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="right" className="w-72 bg-white dark:bg-slate-950 border-slate-200 dark:border-slate-800">
|
||||
<nav className="mt-8 flex flex-col gap-4">
|
||||
{links.map((l) => (
|
||||
<Link key={l.to} to={l.to} className="text-lg font-medium text-slate-700 dark:text-slate-200 hover:text-indigo-500 transition-colors" onClick={() => setOpen(false)}>{l.label}</Link>
|
||||
))}
|
||||
<Button asChild className="mt-4 w-full bg-indigo-600 hover:bg-indigo-700 text-white rounded-full active:scale-[0.97] transition-all">
|
||||
<Link to="/courses" onClick={() => setOpen(false)}>Enroll Now</Link>
|
||||
</Button>
|
||||
<AuthDialog trigger={<Button variant="outline" className="w-full rounded-full">Sign In</Button>} />
|
||||
<ThemeToggle />
|
||||
</nav>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user