Initial build: Great Bear microbrewery site
- 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
This commit is contained in:
commit
7b070fde81
66 files changed
+8375
No files matched your search
@@ -0,0 +1,107 @@
|
||||
import * as React from 'react'
|
||||
import type { MenuCategory, MenuItem } from '~/lib/types'
|
||||
|
||||
export function MenuBrowser({
|
||||
categories,
|
||||
items,
|
||||
}: {
|
||||
categories: MenuCategory[]
|
||||
items: MenuItem[]
|
||||
}) {
|
||||
const [active, setActive] = React.useState<string>('all')
|
||||
|
||||
const visible = items.filter((item) => active === 'all' || item.category === active)
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Sticky Menu Navigation Filter */}
|
||||
<section className="sticky top-20 z-40 bg-surface/90 py-space-md shadow-md backdrop-blur-md">
|
||||
<div className="no-scrollbar mx-auto flex max-w-7xl items-center justify-start gap-space-sm overflow-x-auto px-grid-margin md:justify-center">
|
||||
<FilterTab
|
||||
key="all"
|
||||
label="All Offerings"
|
||||
active={active === 'all'}
|
||||
onClick={() => setActive('all')}
|
||||
/>
|
||||
{categories.map((category) => (
|
||||
<FilterTab
|
||||
key={category.slug}
|
||||
label={category.name}
|
||||
active={active === category.slug}
|
||||
onClick={() => setActive(category.slug)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Main Menu Grid */}
|
||||
<section className="mx-auto w-full max-w-7xl px-grid-margin py-space-xl">
|
||||
<div className="grid grid-cols-1 gap-grid-gutter md:grid-cols-2">
|
||||
{visible.map((item) => (
|
||||
<div
|
||||
key={item.uid}
|
||||
className="flex flex-col overflow-hidden rounded-xl bg-surface-container shadow-md transition-all duration-300 hover:shadow-xl"
|
||||
>
|
||||
<div
|
||||
className="h-64 w-full bg-cover bg-center"
|
||||
style={{ backgroundImage: `url('${item.image}')` }}
|
||||
/>
|
||||
<div className="flex flex-grow flex-col justify-between">
|
||||
<div>
|
||||
<div className="mb-space-xs flex items-center justify-between">
|
||||
<h3 className="font-headline-md text-on-surface">{item.name}</h3>
|
||||
<span className="font-headline-md text-primary">₹{item.price}</span>
|
||||
</div>
|
||||
<p className="mb-space-md text-body-md text-on-surface-variant">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className="-mx-space-lg -mb-space-lg flex items-center justify-between bg-surface-container-low p-space-md pt-space-md">
|
||||
<div className="flex items-center gap-space-sm">
|
||||
<span
|
||||
className="material-symbols-outlined text-[20px] text-primary"
|
||||
style={{ fontVariationSettings: "'FILL' 1" }}
|
||||
>
|
||||
sports_bar
|
||||
</span>
|
||||
<span className="text-label-md text-on-surface">
|
||||
Perfect Pairing:{' '}
|
||||
<strong className="text-primary">{item.pairing}</strong>
|
||||
</span>
|
||||
</div>
|
||||
<span className="rounded bg-surface-container-highest px-space-sm py-space-xs text-label-md uppercase text-on-surface-variant">
|
||||
{item.tag}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function FilterTab({
|
||||
label,
|
||||
active,
|
||||
onClick,
|
||||
}: {
|
||||
label: string
|
||||
active: boolean
|
||||
onClick: () => void
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className={
|
||||
active
|
||||
? 'whitespace-nowrap rounded-lg bg-primary-container px-space-md py-space-sm text-label-lg font-bold text-on-primary-container transition-all'
|
||||
: 'whitespace-nowrap rounded-lg bg-surface-container px-space-md py-space-sm text-label-lg text-on-surface-variant transition-all hover:bg-surface-container-high'
|
||||
}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user