BarBar288's picture
Upload 122 files
27127dd verified
import { useState } from "react";
import { useAuth } from "@/hooks/use-auth.tsx";
import { Redirect, Link } from "wouter";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Loader2 } from "lucide-react";
import { useToast } from "@/hooks/use-toast";
// Form validation schemas
const loginSchema = z.object({
username: z.string().min(3, "Username must be at least 3 characters"),
password: z.string().min(6, "Password must be at least 6 characters"),
});
const registerSchema = z.object({
username: z.string().min(3, "Username must be at least 3 characters"),
password: z
.string()
.min(6, "Password must be at least 6 characters")
.max(100, "Password must be less than 100 characters"),
confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
message: "Passwords do not match",
path: ["confirmPassword"],
});
type LoginFormValues = z.infer<typeof loginSchema>;
type RegisterFormValues = z.infer<typeof registerSchema>;
export default function AuthPage() {
const [activeTab, setActiveTab] = useState<"login" | "register">("login");
const { user, loginMutation, registerMutation, isLoading } = useAuth();
const { toast } = useToast();
// Initialize form objects up-front to avoid the hooks rules violation
const loginForm = useForm<LoginFormValues>({
resolver: zodResolver(loginSchema),
defaultValues: {
username: "",
password: "",
},
});
const registerForm = useForm<RegisterFormValues>({
resolver: zodResolver(registerSchema),
defaultValues: {
username: "",
password: "",
confirmPassword: "",
},
});
// If user is already logged in, redirect to home page
if (user) {
return <Redirect to="/" />;
}
// Form submission handlers
const onLoginSubmit = async (data: LoginFormValues) => {
try {
await loginMutation.mutateAsync(data);
toast({
title: "Welcome back!",
description: "You've successfully logged in.",
});
} catch (error) {
console.error("Login error:", error);
// Error handling is done in the loginMutation itself
}
};
const onRegisterSubmit = async (data: RegisterFormValues) => {
try {
const { confirmPassword, ...userData } = data;
await registerMutation.mutateAsync(userData);
toast({
title: "Account created!",
description: "Your account has been created successfully.",
});
} catch (error) {
console.error("Registration error:", error);
// Error handling is done in the registerMutation itself
}
};
const isPending = loginMutation.isPending || registerMutation.isPending || isLoading;
return (
<div className="container flex min-h-screen w-full items-center justify-center px-4 py-6 overflow-y-auto">
<div className="flex w-full max-w-6xl flex-col md:flex-row gap-8 my-8">
{/* Auth Form Section */}
<Card className="w-full max-w-md mx-auto">
<CardHeader className="space-y-1">
<CardTitle className="text-2xl font-bold">Welcome</CardTitle>
<CardDescription>
Sign in to your account or create a new one
</CardDescription>
</CardHeader>
<CardContent>
<Tabs defaultValue="login" value={activeTab} onValueChange={(v) => setActiveTab(v as "login" | "register")}>
<TabsList className="grid w-full grid-cols-1">
<TabsTrigger value="login" className="w-full">Login with Replit</TabsTrigger>
</TabsList>
{/* Login with Replit */}
<TabsContent value="login">
<div className="space-y-4 mt-4">
<Button
onClick={onLoginSubmit}
className="w-full"
disabled={isPending}
>
{isPending ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Connecting to Replit...
</>
) : (
"Continue with Replit"
)}
</Button>
</div>
</TabsContent>
</Tabs>
</CardContent>
<CardFooter className="flex justify-center">
<p className="text-sm text-muted-foreground">
{activeTab === "login" ? (
<>
Don't have an account?{" "}
<Button variant="link" className="p-0" onClick={() => window.location.href = '/register'}>
Sign up
</Button>
</>
) : (
<>
Already have an account?{" "}
<Button variant="link" className="p-0" onClick={() => setActiveTab("login")}>
Log in
</Button>
</>
)}
</p>
</CardFooter>
</Card>
{/* Hero Section */}
<div className="flex flex-col justify-center p-6 text-center md:text-left md:w-1/2">
<h1 className="text-4xl font-bold tracking-tight">AI Assistant</h1>
<p className="mt-4 text-lg text-muted-foreground">
A powerful AI assistant that helps you with conversations,
generates images, and creates videos based on your prompts.
</p>
<div className="mt-8 space-y-4">
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="h-5 w-5 text-primary"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M8.625 12a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 01-2.555-.337A5.972 5.972 0 015.41 20.97a5.969 5.969 0 01-.474-.065 4.48 4.48 0 00.978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25z"
/>
</svg>
</div>
<div className="space-y-1">
<h3 className="text-base font-medium leading-none">Smart Conversations</h3>
<p className="text-sm text-muted-foreground">
Chat with an AI that understands context and provides helpful responses.
</p>
</div>
</div>
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="h-5 w-5 text-primary"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"
/>
</svg>
</div>
<div className="space-y-1">
<h3 className="text-base font-medium leading-none">Image Generation</h3>
<p className="text-sm text-muted-foreground">
Create stunning images from text descriptions using FLUX.1-dev.
</p>
</div>
</div>
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="h-5 w-5 text-primary"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M3.375 19.5h17.25m-17.25 0a1.125 1.125 0 01-1.125-1.125M3.375 19.5h1.5C5.496 19.5 6 18.996 6 18.375m-3.75 0V5.625m0 12.75v-1.5c0-.621.504-1.125 1.125-1.125m18.375 2.625V5.625m0 12.75c0 .621-.504 1.125-1.125 1.125m1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125m0 3.75h-1.5A1.125 1.125 0 0118 18.375M20.625 4.5H3.375m17.25 0c.621 0 1.125.504 1.125 1.125M20.625 4.5h-1.5C18.504 4.5 18 5.004 18 5.625m3.75 0v1.5c0 .621-.504 1.125-1.125 1.125M3.375 4.5c-.621 0-1.125.504-1.125 1.125M3.375 4.5h1.5C5.496 4.5 6 5.004 6 5.625m-3.75 0v1.5c0 .621.504 1.125 1.125 1.125m0 0h1.5m-1.5 0c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125m1.5-3.75C5.496 8.25 6 7.746 6 7.125v-1.5M4.875 8.25C5.496 8.25 6 8.754 6 9.375v1.5m0-5.25v5.25m0-5.25C6 5.004 6.504 4.5 7.125 4.5h9.75c.621 0 1.125.504 1.125 1.125m1.125 2.625h1.5m-1.5 0A1.125 1.125 0 0118 7.125v-1.5m1.125 2.625c-.621 0-1.125.504-1.125 1.125v1.5m2.625-2.625c.621 0 1.125.504 1.125 1.125v1.5c0 .621-.504 1.125-1.125 1.125M18 5.625v5.25M7.125 12h9.75m-9.75 0A1.125 1.125 0 016 10.875M7.125 12C6.504 12 6 12.504 6 13.125m0-2.25C6 11.496 5.496 12 4.875 12M18 10.875c0 .621-.504 1.125-1.125 1.125M18 10.875c0 .621.504 1.125 1.125 1.125m-2.25 0c.621 0 1.125.504 1.125 1.125m-12 5.25v-5.25m0 5.25c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125m-12 0v-1.5c0-.621-.504-1.125-1.125-1.125M18 18.375v-5.25m0 5.25v-1.5c0-.621.504-1.125 1.125-1.125M18 13.125v1.5c0 .621.504 1.125 1.125 1.125M18 13.125c0-.621.504-1.125 1.125-1.125M6 13.125v1.5c0 .621-.504 1.125-1.125 1.125M6 13.125C6 12.504 5.496 12 4.875 12m-1.5 0h1.5m-1.5 0c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125M19.125 12h1.5m0 0c.621 0 1.125.504 1.125 1.125v1.5c0 .621-.504 1.125-1.125 1.125m-17.25 0h1.5m14.25 0h1.5"
/>
</svg>
</div>
<div className="space-y-1">
<h3 className="text-base font-medium leading-none">Video Creation</h3>
<p className="text-sm text-muted-foreground">
Transform your ideas into short videos with AI-powered generation.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}