- TanStack Start (React 19, Tailwind 4) frontend recreated from Stitch design - PocketBase backend with schema migration + idempotent content seed - Single Docker image running both services (non-root, healthchecked) - deploy.sh to build & push to registry.tanshu.com - Ansible playbook (roles: app, caddy) deploying to www.greatbear.in with Caddy TLS entries for the site and the PocketBase admin
160 lines
7.5 KiB
TypeScript
160 lines
7.5 KiB
TypeScript
import { createFileRoute } from '@tanstack/react-router'
|
||
import { getVisitData } from '~/lib/data'
|
||
import { ReservationForm } from '~/components/reservation-form'
|
||
|
||
export const Route = createFileRoute('/visit')({
|
||
head: () => ({
|
||
meta: [
|
||
{ title: 'Visit & Contact | The Great Bear Microbrewery' },
|
||
{
|
||
name: 'description',
|
||
content:
|
||
'Reserve your table at Chandigarh’s premier microbrewery — SCO 32, Sector 26, Madhya Marg. Open daily 12:00 PM – 1:00 AM.',
|
||
},
|
||
],
|
||
}),
|
||
loader: () => getVisitData(),
|
||
component: VisitPage,
|
||
})
|
||
|
||
function VisitPage() {
|
||
const settings = Route.useLoaderData()
|
||
const directionsUrl = `https://maps.google.com/?q=${encodeURIComponent(settings.maps_query)}`
|
||
const embedUrl = `https://maps.google.com/maps?q=${encodeURIComponent(settings.maps_query)}&output=embed`
|
||
|
||
return (
|
||
<div className="flex w-full flex-col">
|
||
{/* Hero Section with Immersive Atmosphere */}
|
||
<div className="relative flex min-h-[500px] w-full items-center justify-center overflow-hidden bg-surface">
|
||
<div
|
||
className="absolute inset-0 bg-cover bg-center opacity-40 mix-blend-luminosity"
|
||
style={{ backgroundImage: "url('/images/visit-hero.jpg')" }}
|
||
/>
|
||
<div className="absolute inset-0 bg-gradient-to-t from-surface via-surface/60 to-transparent" />
|
||
<div className="relative z-10 mx-auto flex max-w-7xl flex-col items-center gap-space-md px-grid-margin py-space-2xl text-center">
|
||
<span className="text-label-lg uppercase tracking-widest text-primary">
|
||
Get in Touch & Experience The Craft
|
||
</span>
|
||
<h1 className="font-headline-xl text-headline-xl text-on-surface">
|
||
Visit The Great Bear
|
||
</h1>
|
||
<p className="max-w-2xl font-body-lg text-body-lg text-on-surface-variant">
|
||
Step into Chandigarh's premier microbrewery. Whether you're planning an evening with
|
||
close friends or reserving a table for a celebration, our doors are open daily.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Main Content Grid: Reservation & Quick Info */}
|
||
<section className="mx-auto grid w-full max-w-7xl grid-cols-1 items-start gap-grid-gutter px-grid-margin py-space-xl lg:grid-cols-12">
|
||
{/* Left Column: Reservation Form */}
|
||
<div className="flex flex-col gap-space-lg rounded-xl bg-surface-container p-space-lg shadow-xl lg:col-span-7">
|
||
<div className="flex flex-col gap-space-xs">
|
||
<span className="text-label-lg uppercase text-primary">Table Booking</span>
|
||
<h2 className="font-headline-lg text-on-surface">Reserve Your Experience</h2>
|
||
<p className="text-body-sm text-on-surface-variant">
|
||
Fill out the details below and our host will confirm your table promptly.
|
||
</p>
|
||
</div>
|
||
<ReservationForm />
|
||
</div>
|
||
|
||
{/* Right Column: Location, Hours, Contact Card */}
|
||
<div className="flex flex-col gap-space-lg lg:col-span-5">
|
||
<div className="flex flex-col gap-space-md rounded-xl bg-surface-container p-space-lg shadow-xl">
|
||
<div className="flex items-center gap-space-md border-b border-outline-variant/20 pb-space-md">
|
||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-primary/20 text-primary">
|
||
<span className="material-symbols-outlined">schedule</span>
|
||
</div>
|
||
<div>
|
||
<h3 className="font-headline-md text-on-surface">Operating Hours</h3>
|
||
<p className="text-body-sm font-medium text-primary">{settings.hours_line}</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-space-md border-b border-outline-variant/20 pb-space-md">
|
||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-primary/20 text-primary">
|
||
<span className="material-symbols-outlined">location_on</span>
|
||
</div>
|
||
<div>
|
||
<h3 className="font-headline-md text-on-surface">Location</h3>
|
||
<p className="text-body-sm text-on-surface-variant">
|
||
{settings.address_line}
|
||
<br />
|
||
{settings.city_line}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-space-md">
|
||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-primary/20 text-primary">
|
||
<span className="material-symbols-outlined">call</span>
|
||
</div>
|
||
<div>
|
||
<h3 className="font-headline-md text-on-surface">Direct Lines</h3>
|
||
<p className="text-body-sm text-on-surface-variant">
|
||
{settings.phone_primary}
|
||
<br />
|
||
{settings.phone_secondary}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Quick Social Callout */}
|
||
<a
|
||
className="group flex items-center justify-between rounded-xl bg-surface-container p-space-lg shadow-xl transition-all hover:bg-surface-container-high"
|
||
href={settings.instagram_url}
|
||
rel="noopener noreferrer"
|
||
target="_blank"
|
||
>
|
||
<div className="flex items-center gap-space-md">
|
||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-gradient-to-tr from-amber-600 via-rose-600 to-purple-600 text-white">
|
||
<span className="material-symbols-outlined">photo_camera</span>
|
||
</div>
|
||
<div>
|
||
<h3 className="font-headline-md text-on-surface transition-colors group-hover:text-primary">
|
||
Instagram Feed
|
||
</h3>
|
||
<p className="text-body-sm text-on-surface-variant">{settings.instagram_handle}</p>
|
||
</div>
|
||
</div>
|
||
<span className="material-symbols-outlined text-on-surface-variant transition-transform group-hover:translate-x-1">
|
||
arrow_forward
|
||
</span>
|
||
</a>
|
||
</div>
|
||
</section>
|
||
|
||
{/* Interactive Map Section */}
|
||
<section className="w-full bg-surface-container-low py-space-xl">
|
||
<div className="mx-auto flex max-w-7xl flex-col gap-space-lg px-grid-margin">
|
||
<div className="flex flex-col justify-between gap-space-md md:flex-row md:items-end">
|
||
<div>
|
||
<span className="text-label-lg uppercase text-primary">Find Us Easily</span>
|
||
<h2 className="font-headline-lg text-on-surface">Sector 26, Madhya Marg Hub</h2>
|
||
</div>
|
||
<a
|
||
className="flex items-center gap-space-xs self-start rounded-lg border border-primary bg-transparent px-space-md py-space-sm text-label-lg text-primary transition-all hover:bg-primary hover:text-on-primary md:self-auto"
|
||
href={directionsUrl}
|
||
rel="noopener noreferrer"
|
||
target="_blank"
|
||
>
|
||
<span className="material-symbols-outlined text-[18px]">directions</span>
|
||
Get Directions
|
||
</a>
|
||
</div>
|
||
<div className="h-[450px] w-full overflow-hidden rounded-xl shadow-xl">
|
||
<iframe
|
||
title="The Great Bear Microbrewery location map"
|
||
src={embedUrl}
|
||
className="h-full w-full border-0"
|
||
loading="lazy"
|
||
referrerPolicy="no-referrer-when-downgrade"
|
||
allowFullScreen
|
||
/>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
)
|
||
}
|