Initial commit: silicondeck/shadcn-dashboard-landing-template (nextjs-version) + CLAUDE.md
- Next.js 16.1.1 + React 19.2.3 + Tailwind v4 + shadcn/ui v3 - Template scaffold (App Router with (auth)/(dashboard)/landing route groups) - pnpm v10 lockfile - CLAUDE.md describing multi-tenant Appwrite architecture, 8 modules, Gitea+Coolify deploy
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
"use client"
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { z } from "zod"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
|
||||
const signupFormSchema = z.object({
|
||||
firstName: z.string().min(1, "First name is required"),
|
||||
lastName: z.string().min(1, "Last name is required"),
|
||||
email: z.string().email("Invalid email address"),
|
||||
password: z.string().min(6, "Password must be at least 6 characters"),
|
||||
confirmPassword: z.string().min(6, "Please confirm your password"),
|
||||
terms: z.boolean().refine(val => val === true, "You must agree to the terms"),
|
||||
}).refine((data) => data.password === data.confirmPassword, {
|
||||
message: "Passwords don't match",
|
||||
path: ["confirmPassword"],
|
||||
})
|
||||
|
||||
type SignupFormValues = z.infer<typeof signupFormSchema>
|
||||
|
||||
export function SignupForm1({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
const form = useForm<SignupFormValues>({
|
||||
resolver: zodResolver(signupFormSchema),
|
||||
defaultValues: {
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
terms: false,
|
||||
},
|
||||
})
|
||||
|
||||
function onSubmit(data: SignupFormValues) {
|
||||
console.log("Signup attempt:", data)
|
||||
// Here you would typically handle the signup
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<Card>
|
||||
<CardHeader className="text-center">
|
||||
<CardTitle className="text-xl">Create Account</CardTitle>
|
||||
<CardDescription>
|
||||
Enter your information to create a new account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<div className="grid gap-6">
|
||||
<div className="grid gap-4">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="firstName"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>First Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="John" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="lastName"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Last Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Doe" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="confirmPassword"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Confirm Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="terms"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-start space-x-2">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
className="mt-0.5"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel className="text-sm">
|
||||
I agree to the terms of service and privacy policy
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button type="submit" className="w-full cursor-pointer">
|
||||
Create Account
|
||||
</Button>
|
||||
|
||||
<Button variant="outline" className="w-full cursor-pointer" type="button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Sign up with Google
|
||||
</Button>
|
||||
</div>
|
||||
<div className="text-center text-sm">
|
||||
Already have an account?{" "}
|
||||
<a href="/auth/sign-in" className="underline underline-offset-4">
|
||||
Sign in
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="text-muted-foreground *:[a]:hover:text-primary text-center text-xs text-balance *:[a]:underline *:[a]:underline-offset-4">
|
||||
By clicking continue, you agree to our <a href="#">Terms of Service</a>{" "}
|
||||
and <a href="#">Privacy Policy</a>.
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { SignupForm1 } from "./components/signup-form-1"
|
||||
import { Logo } from "@/components/logo"
|
||||
import Link from "next/link"
|
||||
|
||||
export default function SignUpPage() {
|
||||
return (
|
||||
<div className="bg-muted flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
|
||||
<div className="flex w-full max-w-sm flex-col gap-6">
|
||||
<Link href="/" className="flex items-center gap-2 self-center font-medium">
|
||||
<div className="bg-primary text-primary-foreground flex size-9 items-center justify-center rounded-md">
|
||||
<Logo size={24} />
|
||||
</div>
|
||||
ShadcnStore
|
||||
</Link>
|
||||
<SignupForm1 />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user