Files
DynamoDNS/client/src/pages/auth-page.tsx
T
alphaeusmote fac4492050 Update application name to DynamoDNS
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9111ef36-26c8-4085-84ca-a35dc1fec1b5
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7083d608-d6d3-4a6a-9a27-6286c5109627/7a5b96f8-9042-44cf-bea7-814540e9a60d.jpg
2025-04-10 02:09:58 +00:00

307 lines
13 KiB
TypeScript

import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { useAuth } from "@/hooks/use-auth";
import { useOrganization } from "@/context/organization-context";
import { Redirect } from "wouter";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
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 { ThemeToggle } from "@/components/shared/theme-toggle";
import { useTheme } from "@/hooks/use-theme";
import { Loader2 } from "lucide-react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
const loginSchema = z.object({
username: z.string().min(3, "Username must be at least 3 characters"),
password: z.string().min(8, "Password must be at least 8 characters"),
});
const registerSchema = z.object({
username: z.string().min(3, "Username must be at least 3 characters"),
email: z.string().email("Please enter a valid email"),
password: z.string().min(8, "Password must be at least 8 characters"),
fullName: z.string().optional(),
organizationId: z.string().optional(),
});
export default function AuthPage() {
const { user, loginMutation, registerMutation } = useAuth();
const { theme } = useTheme();
const { organizations, isLoading: orgsLoading } = useOrganization();
const loginForm = useForm<z.infer<typeof loginSchema>>({
resolver: zodResolver(loginSchema),
defaultValues: {
username: "",
password: "",
},
});
const registerForm = useForm<z.infer<typeof registerSchema>>({
resolver: zodResolver(registerSchema),
defaultValues: {
username: "",
email: "",
password: "",
fullName: "",
organizationId: undefined,
},
});
const onLoginSubmit = (values: z.infer<typeof loginSchema>) => {
loginMutation.mutate(values);
};
const onRegisterSubmit = (values: z.infer<typeof registerSchema>) => {
registerMutation.mutate(values);
};
// Redirect if already logged in
if (user) {
return <Redirect to="/" />;
}
return (
<div className="min-h-screen flex flex-col lg:flex-row">
{/* Auth Form */}
<div className="flex-1 flex items-center justify-center p-6 bg-background">
<div className="absolute top-4 right-4">
<ThemeToggle />
</div>
<Card className="w-full max-w-md">
<CardHeader className="space-y-1">
<div className="flex items-center mb-2">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="w-6 h-6 text-primary mr-2"
>
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"></path>
</svg>
<span className="font-semibold text-xl">DynamoDNS</span>
</div>
<CardTitle className="text-2xl">Welcome</CardTitle>
<CardDescription>
Sign in to your account or create a new one
</CardDescription>
</CardHeader>
<CardContent>
<Tabs defaultValue="login" className="w-full">
<TabsList className="grid w-full grid-cols-2 mb-4">
<TabsTrigger value="login">Login</TabsTrigger>
<TabsTrigger value="register">Register</TabsTrigger>
</TabsList>
<TabsContent value="login">
<Form {...loginForm}>
<form onSubmit={loginForm.handleSubmit(onLoginSubmit)} className="space-y-4">
<FormField
control={loginForm.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username or Email</FormLabel>
<FormControl>
<Input placeholder="Enter your username or email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={loginForm.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input type="password" placeholder="••••••••" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button
type="submit"
className="w-full"
disabled={loginMutation.isPending}
>
{loginMutation.isPending ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Logging in...
</>
) : (
"Login"
)}
</Button>
</form>
</Form>
</TabsContent>
<TabsContent value="register">
<Form {...registerForm}>
<form onSubmit={registerForm.handleSubmit(onRegisterSubmit)} className="space-y-4">
<FormField
control={registerForm.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="Choose a username" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={registerForm.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type="email" placeholder="Enter your email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={registerForm.control}
name="fullName"
render={({ field }) => (
<FormItem>
<FormLabel>Full Name (Optional)</FormLabel>
<FormControl>
<Input placeholder="Enter your full name" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={registerForm.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input type="password" placeholder="Create a password" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={registerForm.control}
name="organizationId"
render={({ field }) => (
<FormItem>
<FormLabel>Organization</FormLabel>
<FormControl>
<Select
disabled={orgsLoading}
onValueChange={(value) => field.onChange(value)}
value={field.value || ""}
>
<SelectTrigger>
<SelectValue placeholder="Select an organization (optional)" />
</SelectTrigger>
<SelectContent>
{organizations.map((org) => (
<SelectItem key={org.id} value={org.id.toString()}>
{org.name}
</SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button
type="submit"
className="w-full"
disabled={registerMutation.isPending}
>
{registerMutation.isPending ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Creating account...
</>
) : (
"Create Account"
)}
</Button>
</form>
</Form>
</TabsContent>
</Tabs>
</CardContent>
</Card>
</div>
{/* Hero Section */}
<div className="hidden lg:flex lg:flex-1 bg-primary text-primary-foreground">
<div className="max-w-lg mx-auto p-10 flex flex-col justify-center">
<h1 className="text-4xl font-bold mb-6">Dynamic DNS Management System</h1>
<p className="text-xl mb-8">
Keep your DNS records up to date across multiple providers with our powerful management platform.
</p>
<div className="space-y-6">
<div className="flex items-start">
<div className="mr-4 p-2 rounded-full bg-primary-foreground/10">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"></path>
<path d="m9 12 2 2 4-4"></path>
</svg>
</div>
<div>
<h3 className="text-lg font-semibold">Automatic Updates</h3>
<p>Keep your DNS records in sync with your changing IP addresses.</p>
</div>
</div>
<div className="flex items-start">
<div className="mr-4 p-2 rounded-full bg-primary-foreground/10">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect width="18" height="18" x="3" y="3" rx="2" ry="2"></rect>
<path d="M3 9h18"></path>
<path d="M9 21V9"></path>
</svg>
</div>
<div>
<h3 className="text-lg font-semibold">Multi-Provider Support</h3>
<p>Manage DNS records across Cloudflare, Route53, GoDaddy, and more.</p>
</div>
</div>
<div className="flex items-start">
<div className="mr-4 p-2 rounded-full bg-primary-foreground/10">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M3 3v18h18"></path>
<path d="m19 9-5 5-4-4-3 3"></path>
</svg>
</div>
<div>
<h3 className="text-lg font-semibold">Comprehensive Metrics</h3>
<p>View performance metrics and history for all your DNS records.</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}