blank slate

This commit is contained in:
2026-02-24 11:45:25 +01:00
parent 47f9a25834
commit 0856e154b9
14 changed files with 257 additions and 700 deletions

View File

@@ -1,30 +0,0 @@
import { Link } from "@tanstack/react-router";
import UserMenu from "./user-menu";
export default function Header() {
const links = [
{ to: "/", label: "Home" },
{ to: "/dashboard", label: "Dashboard" },
] as const;
return (
<div>
<div className="flex flex-row items-center justify-between px-2 py-1">
<nav className="flex gap-4 text-lg">
{links.map(({ to, label }) => {
return (
<Link key={to} to={to}>
{label}
</Link>
);
})}
</nav>
<div className="flex items-center gap-2">
<UserMenu />
</div>
</div>
<hr />
</div>
);
}

View File

@@ -0,0 +1,111 @@
"use client";
import { useState } from "react";
export default function EventRegistrationForm() {
const [formData, setFormData] = useState({
name: "",
email: "",
phone: "",
company: "",
dietaryRequirements: "",
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log("Form submitted:", formData);
};
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
setFormData((prev) => ({
...prev,
[e.target.name]: e.target.value,
}));
};
return (
<section
style={{
minHeight: "100vh",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
>
<h2>Register for the Event</h2>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name">Full Name *</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
required
placeholder="Enter your full name"
/>
</div>
<div>
<label htmlFor="email">Email Address *</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
required
placeholder="Enter your email"
/>
</div>
<div>
<label htmlFor="phone">Phone Number</label>
<input
type="tel"
id="phone"
name="phone"
value={formData.phone}
onChange={handleChange}
placeholder="Enter your phone number"
/>
</div>
<div>
<label htmlFor="company">Company / Organization</label>
<input
type="text"
id="company"
name="company"
value={formData.company}
onChange={handleChange}
placeholder="Enter your company or organization"
/>
</div>
<div>
<label htmlFor="dietaryRequirements">
Dietary Requirements / Special Requests
</label>
<textarea
id="dietaryRequirements"
name="dietaryRequirements"
value={formData.dietaryRequirements}
onChange={handleChange}
rows={3}
placeholder="Any dietary restrictions or special requests?"
/>
</div>
<button type="submit">Submit Registration</button>
<p>* Required fields</p>
</form>
</section>
);
}

View File

@@ -0,0 +1,15 @@
export default function Footer() {
return (
<footer>
<div>
<div>© 2024 My App. All rights reserved.</div>
<div>
<a href="/privacy">Privacy Policy</a>
<a href="/terms">Terms of Service</a>
<a href="/contact">Contact</a>
</div>
</div>
</footer>
);
}

View File

@@ -0,0 +1,22 @@
export default function Hero() {
return (
<section
style={{
minHeight: "100vh",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
>
<h1>Welcome to Our Event</h1>
<p>
Join us for an amazing experience. Register now to secure your spot.
</p>
<div>
<button type="button">Register Now</button>
<button type="button">Learn More</button>
</div>
</section>
);
}

View File

@@ -0,0 +1,48 @@
export default function Info() {
return (
<section
style={{
minHeight: "100vh",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
>
<div>
<div>
<h3>Event Details</h3>
<p>
Date: TBD
<br />
Location: TBD
<br />
Duration: Full Day
</p>
</div>
<div>
<h3>What to Expect</h3>
<p>
Keynote speakers
<br />
Networking opportunities
<br />
Workshops & sessions
</p>
</div>
<div>
<h3>Who Should Attend</h3>
<p>
Industry professionals
<br />
Students & educators
<br />
Anyone interested
</p>
</div>
</div>
</section>
);
}

View File

@@ -1,137 +0,0 @@
import { useForm } from "@tanstack/react-form";
import { useNavigate } from "@tanstack/react-router";
import { toast } from "sonner";
import z from "zod";
import { authClient } from "@/lib/auth-client";
import Loader from "./loader";
import { Button } from "./ui/button";
import { Input } from "./ui/input";
import { Label } from "./ui/label";
export default function SignInForm({ onSwitchToSignUp }: { onSwitchToSignUp: () => void }) {
const navigate = useNavigate({
from: "/",
});
const { isPending } = authClient.useSession();
const form = useForm({
defaultValues: {
email: "",
password: "",
},
onSubmit: async ({ value }) => {
await authClient.signIn.email(
{
email: value.email,
password: value.password,
},
{
onSuccess: () => {
navigate({
to: "/dashboard",
});
toast.success("Sign in successful");
},
onError: (error) => {
toast.error(error.error.message || error.error.statusText);
},
},
);
},
validators: {
onSubmit: z.object({
email: z.email("Invalid email address"),
password: z.string().min(8, "Password must be at least 8 characters"),
}),
},
});
if (isPending) {
return <Loader />;
}
return (
<div className="mx-auto w-full mt-10 max-w-md p-6">
<h1 className="mb-6 text-center text-3xl font-bold">Welcome Back</h1>
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
form.handleSubmit();
}}
className="space-y-4"
>
<div>
<form.Field name="email">
{(field) => (
<div className="space-y-2">
<Label htmlFor={field.name}>Email</Label>
<Input
id={field.name}
name={field.name}
type="email"
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
{field.state.meta.errors.map((error) => (
<p key={error?.message} className="text-red-500">
{error?.message}
</p>
))}
</div>
)}
</form.Field>
</div>
<div>
<form.Field name="password">
{(field) => (
<div className="space-y-2">
<Label htmlFor={field.name}>Password</Label>
<Input
id={field.name}
name={field.name}
type="password"
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
{field.state.meta.errors.map((error) => (
<p key={error?.message} className="text-red-500">
{error?.message}
</p>
))}
</div>
)}
</form.Field>
</div>
<form.Subscribe>
{(state) => (
<Button
type="submit"
className="w-full"
disabled={!state.canSubmit || state.isSubmitting}
>
{state.isSubmitting ? "Submitting..." : "Sign In"}
</Button>
)}
</form.Subscribe>
</form>
<div className="mt-4 text-center">
<Button
variant="link"
onClick={onSwitchToSignUp}
className="text-indigo-600 hover:text-indigo-800"
>
Need an account? Sign Up
</Button>
</div>
</div>
);
}

View File

@@ -1,162 +0,0 @@
import { useForm } from "@tanstack/react-form";
import { useNavigate } from "@tanstack/react-router";
import { toast } from "sonner";
import z from "zod";
import { authClient } from "@/lib/auth-client";
import Loader from "./loader";
import { Button } from "./ui/button";
import { Input } from "./ui/input";
import { Label } from "./ui/label";
export default function SignUpForm({ onSwitchToSignIn }: { onSwitchToSignIn: () => void }) {
const navigate = useNavigate({
from: "/",
});
const { isPending } = authClient.useSession();
const form = useForm({
defaultValues: {
email: "",
password: "",
name: "",
},
onSubmit: async ({ value }) => {
await authClient.signUp.email(
{
email: value.email,
password: value.password,
name: value.name,
},
{
onSuccess: () => {
navigate({
to: "/dashboard",
});
toast.success("Sign up successful");
},
onError: (error) => {
toast.error(error.error.message || error.error.statusText);
},
},
);
},
validators: {
onSubmit: z.object({
name: z.string().min(2, "Name must be at least 2 characters"),
email: z.email("Invalid email address"),
password: z.string().min(8, "Password must be at least 8 characters"),
}),
},
});
if (isPending) {
return <Loader />;
}
return (
<div className="mx-auto w-full mt-10 max-w-md p-6">
<h1 className="mb-6 text-center text-3xl font-bold">Create Account</h1>
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
form.handleSubmit();
}}
className="space-y-4"
>
<div>
<form.Field name="name">
{(field) => (
<div className="space-y-2">
<Label htmlFor={field.name}>Name</Label>
<Input
id={field.name}
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
{field.state.meta.errors.map((error) => (
<p key={error?.message} className="text-red-500">
{error?.message}
</p>
))}
</div>
)}
</form.Field>
</div>
<div>
<form.Field name="email">
{(field) => (
<div className="space-y-2">
<Label htmlFor={field.name}>Email</Label>
<Input
id={field.name}
name={field.name}
type="email"
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
{field.state.meta.errors.map((error) => (
<p key={error?.message} className="text-red-500">
{error?.message}
</p>
))}
</div>
)}
</form.Field>
</div>
<div>
<form.Field name="password">
{(field) => (
<div className="space-y-2">
<Label htmlFor={field.name}>Password</Label>
<Input
id={field.name}
name={field.name}
type="password"
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
{field.state.meta.errors.map((error) => (
<p key={error?.message} className="text-red-500">
{error?.message}
</p>
))}
</div>
)}
</form.Field>
</div>
<form.Subscribe>
{(state) => (
<Button
type="submit"
className="w-full"
disabled={!state.canSubmit || state.isSubmitting}
>
{state.isSubmitting ? "Submitting..." : "Sign Up"}
</Button>
)}
</form.Subscribe>
</form>
<div className="mt-4 text-center">
<Button
variant="link"
onClick={onSwitchToSignIn}
className="text-indigo-600 hover:text-indigo-800"
>
Already have an account? Sign In
</Button>
</div>
</div>
);
}

View File

@@ -1,63 +0,0 @@
import { Link, useNavigate } from "@tanstack/react-router";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { authClient } from "@/lib/auth-client";
import { Button } from "./ui/button";
import { Skeleton } from "./ui/skeleton";
export default function UserMenu() {
const navigate = useNavigate();
const { data: session, isPending } = authClient.useSession();
if (isPending) {
return <Skeleton className="h-9 w-24" />;
}
if (!session) {
return (
<Link to="/login">
<Button variant="outline">Sign In</Button>
</Link>
);
}
return (
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="outline" />}>
{session.user.name}
</DropdownMenuTrigger>
<DropdownMenuContent className="bg-card">
<DropdownMenuGroup>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>{session.user.email}</DropdownMenuItem>
<DropdownMenuItem
variant="destructive"
onClick={() => {
authClient.signOut({
fetchOptions: {
onSuccess: () => {
navigate({
to: "/",
});
},
},
});
}}
>
Sign Out
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
}