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>
);
}

View File

@@ -1,128 +1 @@
@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";
@custom-variant dark (&:is(.dark *));
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.58 0.22 27);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.809 0.105 251.813);
--chart-2: oklch(0.623 0.214 259.815);
--chart-3: oklch(0.546 0.245 262.881);
--chart-4: oklch(0.488 0.243 264.376);
--chart-5: oklch(0.424 0.199 265.638);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.87 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.371 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.809 0.105 251.813);
--chart-2: oklch(0.623 0.214 259.815);
--chart-3: oklch(0.546 0.245 262.881);
--chart-4: oklch(0.488 0.243 264.376);
--chart-5: oklch(0.424 0.199 265.638);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
}
@theme inline {
--font-sans: "Inter Variable", sans-serif;
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--color-foreground: var(--foreground);
--color-background: var(--background);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--radius-2xl: calc(var(--radius) + 8px);
--radius-3xl: calc(var(--radius) + 12px);
--radius-4xl: calc(var(--radius) + 16px);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply font-sans bg-background text-foreground;
}
html {
@apply font-sans;
}
}

View File

@@ -9,22 +9,10 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
import { Route as LoginRouteImport } from './routes/login'
import { Route as DashboardRouteImport } from './routes/dashboard'
import { Route as IndexRouteImport } from './routes/index'
import { Route as ApiRpcSplatRouteImport } from './routes/api/rpc/$'
import { Route as ApiAuthSplatRouteImport } from './routes/api/auth/$'
const LoginRoute = LoginRouteImport.update({
id: '/login',
path: '/login',
getParentRoute: () => rootRouteImport,
} as any)
const DashboardRoute = DashboardRouteImport.update({
id: '/dashboard',
path: '/dashboard',
getParentRoute: () => rootRouteImport,
} as any)
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
@@ -43,58 +31,36 @@ const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/dashboard': typeof DashboardRoute
'/login': typeof LoginRoute
'/api/auth/$': typeof ApiAuthSplatRoute
'/api/rpc/$': typeof ApiRpcSplatRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/dashboard': typeof DashboardRoute
'/login': typeof LoginRoute
'/api/auth/$': typeof ApiAuthSplatRoute
'/api/rpc/$': typeof ApiRpcSplatRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/dashboard': typeof DashboardRoute
'/login': typeof LoginRoute
'/api/auth/$': typeof ApiAuthSplatRoute
'/api/rpc/$': typeof ApiRpcSplatRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/dashboard' | '/login' | '/api/auth/$' | '/api/rpc/$'
fullPaths: '/' | '/api/auth/$' | '/api/rpc/$'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/dashboard' | '/login' | '/api/auth/$' | '/api/rpc/$'
id: '__root__' | '/' | '/dashboard' | '/login' | '/api/auth/$' | '/api/rpc/$'
to: '/' | '/api/auth/$' | '/api/rpc/$'
id: '__root__' | '/' | '/api/auth/$' | '/api/rpc/$'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
DashboardRoute: typeof DashboardRoute
LoginRoute: typeof LoginRoute
ApiAuthSplatRoute: typeof ApiAuthSplatRoute
ApiRpcSplatRoute: typeof ApiRpcSplatRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/login': {
id: '/login'
path: '/login'
fullPath: '/login'
preLoaderRoute: typeof LoginRouteImport
parentRoute: typeof rootRouteImport
}
'/dashboard': {
id: '/dashboard'
path: '/dashboard'
fullPath: '/dashboard'
preLoaderRoute: typeof DashboardRouteImport
parentRoute: typeof rootRouteImport
}
'/': {
id: '/'
path: '/'
@@ -121,8 +87,6 @@ declare module '@tanstack/react-router' {
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
DashboardRoute: DashboardRoute,
LoginRoute: LoginRoute,
ApiAuthSplatRoute: ApiAuthSplatRoute,
ApiRpcSplatRoute: ApiRpcSplatRoute,
}

View File

@@ -1,61 +1,60 @@
import type { QueryClient } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { HeadContent, Outlet, Scripts, createRootRouteWithContext } from "@tanstack/react-router";
import {
createRootRouteWithContext,
HeadContent,
Outlet,
Scripts,
} from "@tanstack/react-router";
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
import { Toaster } from "@/components/ui/sonner";
import type { orpc } from "@/utils/orpc";
import { Toaster } from "@/components/ui/sonner";
import Header from "../components/header";
import appCss from "../index.css?url";
export interface RouterAppContext {
orpc: typeof orpc;
queryClient: QueryClient;
orpc: typeof orpc;
queryClient: QueryClient;
}
export const Route = createRootRouteWithContext<RouterAppContext>()({
head: () => ({
meta: [
{
charSet: "utf-8",
},
{
name: "viewport",
content: "width=device-width, initial-scale=1",
},
{
title: "My App",
},
],
links: [
{
rel: "stylesheet",
href: appCss,
},
],
}),
head: () => ({
meta: [
{
charSet: "utf-8",
},
{
name: "viewport",
content: "width=device-width, initial-scale=1",
},
{
title: "My App",
},
],
links: [
{
rel: "stylesheet",
href: appCss,
},
],
}),
component: RootDocument,
component: RootDocument,
});
function RootDocument() {
return (
<html lang="en" className="dark">
<head>
<HeadContent />
</head>
<body>
<div className="grid h-svh grid-rows-[auto_1fr]">
<Header />
<Outlet />
</div>
<Toaster richColors />
<TanStackRouterDevtools position="bottom-left" />
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
<Scripts />
</body>
</html>
);
return (
<html lang="en">
<head>
<HeadContent />
</head>
<body>
<Outlet />
<Toaster />
<TanStackRouterDevtools position="bottom-left" />
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
<Scripts />
</body>
</html>
);
}

View File

@@ -1,34 +0,0 @@
import { useQuery } from "@tanstack/react-query";
import { createFileRoute, redirect } from "@tanstack/react-router";
import { getUser } from "@/functions/get-user";
import { orpc } from "@/utils/orpc";
export const Route = createFileRoute("/dashboard")({
component: RouteComponent,
beforeLoad: async () => {
const session = await getUser();
return { session };
},
loader: async ({ context }) => {
if (!context.session) {
throw redirect({
to: "/login",
});
}
},
});
function RouteComponent() {
const { session } = Route.useRouteContext();
const privateData = useQuery(orpc.privateData.queryOptions());
return (
<div>
<h1>Dashboard</h1>
<p>Welcome {session?.user.name}</p>
<p>API: {privateData.data?.message}</p>
</div>
);
}

View File

@@ -1,51 +1,21 @@
import { useQuery } from "@tanstack/react-query";
import { createFileRoute } from "@tanstack/react-router";
import { orpc } from "@/utils/orpc";
import EventRegistrationForm from "@/components/homepage/EventRegistrationForm";
import Footer from "@/components/homepage/Footer";
import Hero from "@/components/homepage/Hero";
import Info from "@/components/homepage/Info";
export const Route = createFileRoute("/")({
component: HomeComponent,
component: HomePage,
});
const TITLE_TEXT = `
██████╗ ███████╗████████╗████████╗███████╗██████╗
██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝
██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗
██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║
╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗
╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
██║ ███████╗ ██║ ███████║██║ █████╔╝
██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗
██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗
╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
`;
function HomeComponent() {
const healthCheck = useQuery(orpc.healthCheck.queryOptions());
return (
<div className="container mx-auto max-w-3xl px-4 py-2">
<pre className="overflow-x-auto font-mono text-sm">{TITLE_TEXT}</pre>
<div className="grid gap-6">
<section className="rounded-lg border p-4">
<h2 className="mb-2 font-medium">API Status</h2>
<div className="flex items-center gap-2">
<div
className={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
/>
<span className="text-muted-foreground text-sm">
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
</span>
</div>
</section>
</div>
</div>
);
function HomePage() {
return (
<main>
<Hero />
<Info />
<EventRegistrationForm />
<Footer />
</main>
);
}

View File

@@ -1,19 +0,0 @@
import { createFileRoute } from "@tanstack/react-router";
import { useState } from "react";
import SignInForm from "@/components/sign-in-form";
import SignUpForm from "@/components/sign-up-form";
export const Route = createFileRoute("/login")({
component: RouteComponent,
});
function RouteComponent() {
const [showSignIn, setShowSignIn] = useState(false);
return showSignIn ? (
<SignInForm onSwitchToSignUp={() => setShowSignIn(false)} />
) : (
<SignUpForm onSwitchToSignIn={() => setShowSignIn(true)} />
);
}