Skip to content

torontoNights/web

Repository files navigation

TorontoNights Web

The nightlife-vibe web frontend for the TorontoNights knowledge engine. It is a completely separate app from the tn/ engine — it never touches the database. All data comes from the engine's read-only HTTP API.

Stack: Next.js 16 (App Router) · React 19 · TypeScript · Tailwind v4 · Framer Motion · Zod.

Quick start

# 1. Start the tn engine (separate terminal, in the tn/ folder)
cd ..\tn
.\.venv\Scripts\Activate.ps1
tn serve                        # http://localhost:8000

# 2. In this folder — install and configure
cd ..\tn-web
npm install
Copy-Item .env.local.example .env.local   # already committed by default

# 3. Run
npm run dev                     # http://localhost:3000

How the data flows

Browser ──▶ Next.js Route Handler (/api/*)  ──▶ tn engine (localhost:8000)
                                                 (server-side only)
  • The browser never talks to the engine directly.
  • TN_API_URL lives in .env.local and is only read on the server.
  • Every request has a hard timeout (TN_API_TIMEOUT_MS, default 8s) so a stalled engine cannot hang a page render.
  • Upstream errors are translated to safe generic messages before reaching the browser (see src/lib/api/http.ts).

Layout

src/
├── app/                     Next.js App Router
│   ├── api/                 Server-side proxy to the tn engine
│   │   └── media/images/    Streams venue photos from the engine with long-lived cache headers
│   ├── venues/[id]/         Venue detail page
│   ├── search/              Explore / filter page
│   ├── events/              Upcoming events page
│   ├── layout.tsx           Root layout — theme, fonts, nav, footer
│   ├── page.tsx             Home ("Where should we go?")
│   ├── loading.tsx          Route-level Suspense fallback
│   ├── error.tsx            Route-level error boundary
│   └── not-found.tsx
├── components/
│   ├── ui/                  Primitives (Button, Card, Input, Badge, …)
│   ├── layout/              Navbar, Footer
│   ├── home/                Hero, SearchBar, LocationButton, HomeExplorer
│   ├── venues/              VenueCard, VenueImage, VenueSheet, VenueHours, VenueTrust
│   ├── nearby/              NearbyExplorer, NearbyVenueRow, RadiusSlider
│   └── map/                 VenueMap (MapLibre), MapFilters
├── hooks/                   useGeolocation, useVenueSheet
├── lib/
│   ├── api/                 tn engine client + Route Handler helpers
│   ├── geo.ts               haversine, venuesWithinRadius, formatDistance
│   ├── env.ts               Zod-validated server env
│   └── utils.ts             cn(), formatConfidence(), …
└── types/                   Enums + wire types (mirrors tn schemas)

Every file is intentionally kept small and single-purpose.

Images

Venue photos are stored on the engine's disk and streamed through Next:

Browser  ──▶  /api/media/images/venues/<id>/<hash>.jpg  ──▶  tn engine /media/images/…
              (7-day cache headers, force-static)

<VenueImage> renders the first photo via next/image (unoptimized) if the venue has one, otherwise falls back to a category-coloured gradient tile with the category emoji. To populate the cache on the engine side: tn images-fetch-google --max 3 --width 1600 (see the engine RUNBOOK).

Location UX

  • The home page auto-requests browser geolocation on mount.
  • We show the "you are here" marker + radius ring only when the user granted permission and their coords fall inside the GTA bounding box.
  • Anyone outside Toronto (or who denies / times out) sees a plain "Around downtown" view with every Toronto venue on the map — no radius filter, no radius slider.
  • The radius slider defaults to 1 km and caps at 10 km.

Security

  • Strong headers set in next.config.ts (CSP, HSTS, X-Frame, Referrer, Permissions).
  • TN_API_URL is a server-only env var — src/lib/env.ts throws if any var is malformed at startup.
  • API client is wrapped with import "server-only" so it can never be bundled into the client.
  • Every proxy route validates query params before forwarding.

Design system

Dark-first "nightlife" theme built with Tailwind v4 tokens in src/app/globals.css:

  • --neon-pink, --neon-cyan, --neon-violet, --gold
  • .surface-glass — frosted glass surface for cards/navbar
  • .text-neon — gradient text used sparingly on hero headlines
  • Motion respects prefers-reduced-motion.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors