2 Commits

10 changed files with 41 additions and 138 deletions

View File

@@ -17,7 +17,6 @@ function statusFromError(error: AppError): ContentfulStatusCode {
case 'unexpected': case 'unexpected':
return 500; return 500;
default: default:
return 500; return 500;
} }
} }

View File

@@ -0,0 +1,15 @@
export function Background() {
return (
<div className="pointer-events-none fixed inset-0 -z-10 overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-b from-[#040b11] to-[#09131c]" />
<div className="absolute left-0 top-0 h-[420px] w-[420px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary/20 blur-3xl" />
<div className="absolute right-0 top-24 h-[360px] w-[360px] translate-x-1/3 rounded-full bg-reserved/15 blur-3xl" />
<div className="absolute bottom-0 left-1/2 h-[300px] w-[300px] -translate-x-1/2 translate-y-1/3 rounded-full bg-accent/10 blur-3xl" />
<div className="absolute inset-0 bg-[linear-gradient(to_right,oklch(1_0_0/0.055)_1px,transparent_1px),linear-gradient(to_bottom,oklch(1_0_0/0.055)_1px,transparent_1px)] bg-[size:64px_64px]" />
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,transparent_0%,#09131c_78%)]" />
</div>
);
}

View File

@@ -14,7 +14,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
import PlayzerIcon from '@/assets/playzer-favicon-512-transparent.png'; import PlayzerIcon from '@/assets/playzer-favicon-512-transparent.png';
import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useQuery, useQueryClient } from '@tanstack/react-query';
import { Link, useLocation, useNavigate } from '@tanstack/react-router'; import { Link, useNavigate } from '@tanstack/react-router';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
@@ -35,40 +35,6 @@ import { acceptStoredPendingInvite } from '@/lib/invitations';
import { useCurrentComplexStore } from '@/lib/stores/current-complex-store'; import { useCurrentComplexStore } from '@/lib/stores/current-complex-store';
import { useTheme } from '@/lib/theme'; import { useTheme } from '@/lib/theme';
interface NavItemProps {
to: '/' | '/about';
label: string;
isMobile?: boolean;
onClick?: () => void;
}
function NavItem({ to, label, isMobile, onClick }: NavItemProps) {
const location = useLocation();
const currentPath = location.pathname;
const isActive = to === '/' ? currentPath === '/' : currentPath.startsWith(to);
return (
<Link to={to} onClick={onClick}>
<Button
variant="ghost"
size={isMobile ? 'default' : 'sm'}
className={[
'rounded-none transition-all duration-200 hover:bg-transparent',
'focus-visible:ring-2 focus-visible:ring-primary/50',
isMobile && 'w-full justify-start rounded-2xl',
isActive
? 'text-primary relative after:absolute after:bottom-0 after:left-0 after:right-0 after:h-0.5 after:bg-primary'
: 'text-muted-foreground hover:text-foreground relative hover:after:absolute hover:after:bottom-0 hover:after:left-0 hover:after:right-0 hover:after:h-0.5 hover:after:bg-primary/50',
]
.filter(Boolean)
.join(' ')}
>
{label}
</Button>
</Link>
);
}
export function Header() { export function Header() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
@@ -113,11 +79,6 @@ export function Header() {
return selected?.complexName ?? complexes[0]?.complexName ?? 'Mi complejo'; return selected?.complexName ?? complexes[0]?.complexName ?? 'Mi complejo';
}, [complexSlug, myComplexesQuery.data]); }, [complexSlug, myComplexesQuery.data]);
const menuItems = [
{ label: 'Inicio', to: '/' },
{ label: 'About', to: '/about' },
] as const;
const handleSelectComplex = async (complexId: string, nextSlug: string) => { const handleSelectComplex = async (complexId: string, nextSlug: string) => {
await apiClient.complexes.select({ complexId }); await apiClient.complexes.select({ complexId });
setCurrentComplex(nextSlug); setCurrentComplex(nextSlug);
@@ -154,12 +115,6 @@ export function Header() {
</span> </span>
</Link> </Link>
<nav className="hidden absolute left-1/2 -translate-x-1/2 items-center gap-1 md:flex">
{menuItems.map((item) => (
<NavItem key={item.label} to={item.to} label={item.label} />
))}
</nav>
<div className="flex-1" /> <div className="flex-1" />
<div className="hidden items-center gap-2 md:flex"> <div className="hidden items-center gap-2 md:flex">
@@ -336,18 +291,6 @@ export function Header() {
</div> </div>
</div> </div>
<nav className="flex flex-col gap-1">
{menuItems.map((item) => (
<NavItem
key={item.label}
to={item.to}
label={item.label}
isMobile
onClick={() => setOpen(false)}
/>
))}
</nav>
<Button <Button
className="rounded-2xl shadow-lg shadow-primary/20" className="rounded-2xl shadow-lg shadow-primary/20"
onClick={() => { onClick={() => {

View File

@@ -1,3 +1,4 @@
import { Background } from '@/components/background';
import { useIsMobile } from '@/hooks/use-mobile'; import { useIsMobile } from '@/hooks/use-mobile';
import { useLocation } from '@tanstack/react-router'; import { useLocation } from '@tanstack/react-router';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
@@ -28,19 +29,3 @@ export function Layout({ children }: LayoutProps) {
</div> </div>
); );
} }
function Background() {
return (
<div className="pointer-events-none fixed inset-0 -z-10 overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-b from-[#040b11] to-[#09131c]" />
<div className="absolute left-0 top-0 h-[420px] w-[420px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary/20 blur-3xl" />
<div className="absolute right-0 top-24 h-[360px] w-[360px] translate-x-1/3 rounded-full bg-reserved/15 blur-3xl" />
<div className="absolute bottom-0 left-1/2 h-[300px] w-[300px] -translate-x-1/2 translate-y-1/3 rounded-full bg-accent/10 blur-3xl" />
<div className="absolute inset-0 bg-[linear-gradient(to_right,oklch(1_0_0/0.055)_1px,transparent_1px),linear-gradient(to_bottom,oklch(1_0_0/0.055)_1px,transparent_1px)] bg-[size:64px_64px]" />
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,transparent_0%,#09131c_78%)]" />
</div>
);
}

View File

@@ -1,12 +0,0 @@
export function AboutPage() {
return (
<main className="mx-auto flex min-h-[60vh] w-full max-w-6xl items-center justify-center px-3 sm:px-6">
<section className="w-full max-w-3xl rounded-xl border bg-card p-6 text-card-foreground shadow-sm">
<h2 className="text-2xl font-semibold">About</h2>
<p className="mt-2 text-sm text-muted-foreground">
Ruta de ejemplo funcionando con TanStack Router.
</p>
</section>
</main>
);
}

View File

@@ -209,29 +209,6 @@ function MobileTopBar() {
</div> </div>
</div> </div>
<nav className="flex flex-col gap-1">
<Button
variant="ghost"
className="justify-start rounded-lg"
onClick={() => {
setOpen(false);
void navigate({ to: '/' });
}}
>
Inicio
</Button>
<Button
variant="ghost"
className="justify-start rounded-lg"
onClick={() => {
setOpen(false);
void navigate({ to: '/about' });
}}
>
About
</Button>
</nav>
<Button <Button
className="rounded-lg shadow-lg shadow-primary/20" className="rounded-lg shadow-lg shadow-primary/20"
onClick={() => { onClick={() => {

View File

@@ -1,3 +1,4 @@
import PlayzerIcon from '@/assets/playzer-favicon-512-transparent.png';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Field, FieldError, FieldLabel } from '@/components/ui/field'; import { Field, FieldError, FieldLabel } from '@/components/ui/field';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
@@ -78,7 +79,15 @@ export function LoginPage({ redirectTo = '/' }: LoginPageProps) {
}; };
return ( return (
<main className="flex min-h-screen w-full items-center justify-center px-3 sm:px-6"> <main className="flex min-h-screen w-full flex-col items-center justify-center gap-6 px-3 sm:px-6">
<Link to="/" className="group flex items-center gap-3">
<div className="flex size-10 items-center justify-center">
<img src={PlayzerIcon} alt="Playzer" className="size-7" />
</div>
<span className="bg-gradient-to-r from-primary via-primary to-reserved bg-clip-text text-xl font-bold tracking-tight text-transparent">
Playzer
</span>
</Link>
<section className="w-full max-w-sm rounded-xl border bg-card p-6 text-card-foreground shadow-sm"> <section className="w-full max-w-sm rounded-xl border bg-card p-6 text-card-foreground shadow-sm">
<h1 className="mb-2 text-2xl font-semibold">Iniciar sesión</h1> <h1 className="mb-2 text-2xl font-semibold">Iniciar sesión</h1>
<p className="mb-4 text-sm text-muted-foreground"> <p className="mb-4 text-sm text-muted-foreground">

View File

@@ -22,7 +22,6 @@ import { Route as OnboardVerifyRouteImport } from './routes/onboard/verify'
import { Route as OnboardCreateComplexRouteImport } from './routes/onboard/create-complex' import { Route as OnboardCreateComplexRouteImport } from './routes/onboard/create-complex'
import { Route as OnboardCompleteRouteImport } from './routes/onboard/complete' import { Route as OnboardCompleteRouteImport } from './routes/onboard/complete'
import { Route as AppProfileRouteImport } from './routes/_app/profile' import { Route as AppProfileRouteImport } from './routes/_app/profile'
import { Route as AppAboutRouteImport } from './routes/_app/about'
import { Route as ComplexSlugBookingRouteImport } from './routes/$complexSlug/booking' import { Route as ComplexSlugBookingRouteImport } from './routes/$complexSlug/booking'
import { Route as AppAuthenticatedRouteRouteImport } from './routes/_app/_authenticated/route' import { Route as AppAuthenticatedRouteRouteImport } from './routes/_app/_authenticated/route'
import { Route as AppAuthenticatedIndexRouteImport } from './routes/_app/_authenticated/index' import { Route as AppAuthenticatedIndexRouteImport } from './routes/_app/_authenticated/index'
@@ -94,11 +93,6 @@ const AppProfileRoute = AppProfileRouteImport.update({
path: '/profile', path: '/profile',
getParentRoute: () => AppRouteRoute, getParentRoute: () => AppRouteRoute,
} as any) } as any)
const AppAboutRoute = AppAboutRouteImport.update({
id: '/about',
path: '/about',
getParentRoute: () => AppRouteRoute,
} as any)
const ComplexSlugBookingRoute = ComplexSlugBookingRouteImport.update({ const ComplexSlugBookingRoute = ComplexSlugBookingRouteImport.update({
id: '/$complexSlug/booking', id: '/$complexSlug/booking',
path: '/$complexSlug/booking', path: '/$complexSlug/booking',
@@ -140,7 +134,6 @@ export interface FileRoutesByFullPath {
'/reset-password': typeof ResetPasswordRoute '/reset-password': typeof ResetPasswordRoute
'/select-complex': typeof SelectComplexRoute '/select-complex': typeof SelectComplexRoute
'/$complexSlug/booking': typeof ComplexSlugBookingRouteWithChildren '/$complexSlug/booking': typeof ComplexSlugBookingRouteWithChildren
'/about': typeof AppAboutRoute
'/profile': typeof AppProfileRoute '/profile': typeof AppProfileRoute
'/onboard/complete': typeof OnboardCompleteRoute '/onboard/complete': typeof OnboardCompleteRoute
'/onboard/create-complex': typeof OnboardCreateComplexRoute '/onboard/create-complex': typeof OnboardCreateComplexRoute
@@ -158,7 +151,6 @@ export interface FileRoutesByTo {
'/login': typeof LoginRoute '/login': typeof LoginRoute
'/reset-password': typeof ResetPasswordRoute '/reset-password': typeof ResetPasswordRoute
'/select-complex': typeof SelectComplexRoute '/select-complex': typeof SelectComplexRoute
'/about': typeof AppAboutRoute
'/profile': typeof AppProfileRoute '/profile': typeof AppProfileRoute
'/onboard/complete': typeof OnboardCompleteRoute '/onboard/complete': typeof OnboardCompleteRoute
'/onboard/create-complex': typeof OnboardCreateComplexRoute '/onboard/create-complex': typeof OnboardCreateComplexRoute
@@ -180,7 +172,6 @@ export interface FileRoutesById {
'/select-complex': typeof SelectComplexRoute '/select-complex': typeof SelectComplexRoute
'/_app/_authenticated': typeof AppAuthenticatedRouteRouteWithChildren '/_app/_authenticated': typeof AppAuthenticatedRouteRouteWithChildren
'/$complexSlug/booking': typeof ComplexSlugBookingRouteWithChildren '/$complexSlug/booking': typeof ComplexSlugBookingRouteWithChildren
'/_app/about': typeof AppAboutRoute
'/_app/profile': typeof AppProfileRoute '/_app/profile': typeof AppProfileRoute
'/onboard/complete': typeof OnboardCompleteRoute '/onboard/complete': typeof OnboardCompleteRoute
'/onboard/create-complex': typeof OnboardCreateComplexRoute '/onboard/create-complex': typeof OnboardCreateComplexRoute
@@ -203,7 +194,6 @@ export interface FileRouteTypes {
| '/reset-password' | '/reset-password'
| '/select-complex' | '/select-complex'
| '/$complexSlug/booking' | '/$complexSlug/booking'
| '/about'
| '/profile' | '/profile'
| '/onboard/complete' | '/onboard/complete'
| '/onboard/create-complex' | '/onboard/create-complex'
@@ -221,7 +211,6 @@ export interface FileRouteTypes {
| '/login' | '/login'
| '/reset-password' | '/reset-password'
| '/select-complex' | '/select-complex'
| '/about'
| '/profile' | '/profile'
| '/onboard/complete' | '/onboard/complete'
| '/onboard/create-complex' | '/onboard/create-complex'
@@ -242,7 +231,6 @@ export interface FileRouteTypes {
| '/select-complex' | '/select-complex'
| '/_app/_authenticated' | '/_app/_authenticated'
| '/$complexSlug/booking' | '/$complexSlug/booking'
| '/_app/about'
| '/_app/profile' | '/_app/profile'
| '/onboard/complete' | '/onboard/complete'
| '/onboard/create-complex' | '/onboard/create-complex'
@@ -359,13 +347,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AppProfileRouteImport preLoaderRoute: typeof AppProfileRouteImport
parentRoute: typeof AppRouteRoute parentRoute: typeof AppRouteRoute
} }
'/_app/about': {
id: '/_app/about'
path: '/about'
fullPath: '/about'
preLoaderRoute: typeof AppAboutRouteImport
parentRoute: typeof AppRouteRoute
}
'/$complexSlug/booking': { '/$complexSlug/booking': {
id: '/$complexSlug/booking' id: '/$complexSlug/booking'
path: '/$complexSlug/booking' path: '/$complexSlug/booking'
@@ -428,13 +409,11 @@ const AppAuthenticatedRouteRouteWithChildren =
interface AppRouteRouteChildren { interface AppRouteRouteChildren {
AppAuthenticatedRouteRoute: typeof AppAuthenticatedRouteRouteWithChildren AppAuthenticatedRouteRoute: typeof AppAuthenticatedRouteRouteWithChildren
AppAboutRoute: typeof AppAboutRoute
AppProfileRoute: typeof AppProfileRoute AppProfileRoute: typeof AppProfileRoute
} }
const AppRouteRouteChildren: AppRouteRouteChildren = { const AppRouteRouteChildren: AppRouteRouteChildren = {
AppAuthenticatedRouteRoute: AppAuthenticatedRouteRouteWithChildren, AppAuthenticatedRouteRoute: AppAuthenticatedRouteRouteWithChildren,
AppAboutRoute: AppAboutRoute,
AppProfileRoute: AppProfileRoute, AppProfileRoute: AppProfileRoute,
} }

View File

@@ -1,3 +1,4 @@
import { Background } from '@/components/background';
import { Layout } from '@/components/layout'; import { Layout } from '@/components/layout';
import { NotFoundPage } from '@/features/not-found/not-found-page'; import { NotFoundPage } from '@/features/not-found/not-found-page';
import { ServerErrorPage } from '@/features/server-error/server-error-page'; import { ServerErrorPage } from '@/features/server-error/server-error-page';
@@ -16,14 +17,27 @@ export const Route = createRootRouteWithContext<RouterContext>()({
notFoundComponent: NotFoundPage, notFoundComponent: NotFoundPage,
}); });
const AUTH_PAGE_REGEX =
/^\/(login|select-complex|reset-password|onboard|invite|auth-callback)(?:\/|$)/;
function RootRoute() { function RootRoute() {
const location = useLocation(); const location = useLocation();
const isPublicBookingRoute = /^\/[^/]+\/booking(?:\/|$)/.test(location.pathname); const isPublicBookingRoute = /^\/[^/]+\/booking(?:\/|$)/.test(location.pathname);
const isAuthPage = AUTH_PAGE_REGEX.test(location.pathname);
if (isPublicBookingRoute) { if (isPublicBookingRoute) {
return <Outlet />; return <Outlet />;
} }
if (isAuthPage) {
return (
<div className="relative min-h-dvh overflow-x-hidden bg-background text-foreground">
<Background />
<Outlet />
</div>
);
}
return ( return (
<Layout> <Layout>
<div className="flex flex-col"> <div className="flex flex-col">

View File

@@ -1,6 +0,0 @@
import { AboutPage } from '@/features/about/about-page';
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/_app/about')({
component: AboutPage,
});