12 — Consumer Meta Web Specification
Surface: Consumer Meta Web (Next.js 14+ App Router, PWA) BFF:
bff-consumer-serviceAudience: Guests browsing across tenants, discovering and comparing properties before booking Runtime: Browser (Chrome, Firefox, Safari, Edge); PWA-installed on desktop + mobile
1. Overview
The Consumer Meta Web is the Ghasi Melmastoon discovery layer — a Trivago-style meta-search experience where guests find, compare, and shortlist properties across all tenants, then hand off into the chosen tenant's booking flow without leaving the platform.
What makes it unique:
- The guest never leaves the platform to book — the booking transition is a runtime context switch, not a redirect.
- Unified analytics across discovery and booking (same
gms_idsession). - RTL-first (Pashto / Dari default layouts).
- Offline browse cache: last search results survive a 30-second outage (PWA).
- Shortlist persists: cookie pre-auth, account post-auth, merged on login.
2. Tech stack
| Concern | Choice |
|---|---|
| Framework | Next.js 14+ App Router |
| Rendering | RSC default; "use client" only for interactive islands |
| Styling | TailwindCSS + @ghasi/ui-melmastoon design tokens |
| State — server | TanStack Query v5 (React Query) |
| State — URL | nuqs (type-safe URL search params) |
| State — client | Zustand (shortlist, map viewport, filter panel) |
| Offline | PWA — next-pwa with workbox. Browse cache strategy: StaleWhileRevalidate for search results (30 s TTL), CacheFirst for property photos (7 d TTL) |
| Maps | Leaflet + OpenStreetMap (no Google Maps API key cost) |
| i18n | next-intl with ICU MessageFormat |
| Auth | gms_id cookie from BFF; no login required for discovery |
| BFF | bff-consumer-service via /api/consumer/ Next.js route handlers |
3. Information architecture
3.1 Route tree
/ (root)
├── / → redirect to /[locale]/
├── /[locale]/
│ ├── / ← Home (hero search, featured properties)
│ ├── /search ← Search results (list + map)
│ ├── /property/[propertyId] ← Property detail
│ ├── /shortlist ← Saved properties
│ ├── /account/ ← Guest account (login-gated)
│ │ ├── /login
│ │ ├── /register
│ │ ├── /bookings ← Past/upcoming bookings
│ │ └── /profile
│ └── /[tenantSlug]/ ← Tenant booking context (handoff from search)
│ └── /book/[propertyId] ← Booking funnel (rendered in tenant theme)
[locale] segment: ps, fa, ar, en (language only; region resolved by tenant or property).
3.2 URL state (search page)
/ps/search?dest=Kabul&from=2026-05-01&to=2026-05-03&adults=2&children=0&rooms=1
&stars=3,4,5&amenities=halal_kitchen,prayer_room&cancel=free
&sort=distance&view=list&page=1
&bbox=69.1,34.5,69.3,34.6 (map mode only)
All params type-safe via nuqs. Shareable and deep-linkable.
4. Key screens
4.1 Home
Purpose: Entry point for first-time and returning guests. Inspire discovery; enable quick search.
Sections:
- Hero search bar — destination autocomplete + date range + guests. On submit →
/search. - "Popular destinations" — curated city tiles (static content, editable in control plane).
- "Featured properties" — promoted by super-admin;
GET /consumer/v1/featured. - "Near me" — requires geolocation permission; shows 5 nearest available properties.
- "Your shortlist" — shown if shortlist is non-empty; last 3 saved properties.
RSC: Hero search is a client component (interactive). Other sections are RSC + streaming.
4.2 Search results
Layout:
- Desktop: split panel — filters sidebar (left 280 px) + results list/map (right).
- Mobile: full-width, with filter bottom sheet and toggle between list/map.
- Map: Leaflet full-viewport with results overlay list (scrollable drawer on mobile).
Data flow:
URL params → useSearchResults() → React Query → GET /consumer/v1/search → search-aggregation-service → results
Pagination: Infinite scroll (IntersectionObserver); 20 results per page; max 200.
Empty state: C3 E-01 (zero results), C3 E-03 (offline), C3 L-01 (loading).
4.3 Property detail
URL: /[locale]/property/[propertyId]
Sections:
- Photo gallery — hero carousel + thumbnail strip. AVIF/WebP, LQIP. Tap to expand lightbox.
- Property header — name, star rating, location, review score (if any).
- Map thumbnail — Leaflet mini-map centered on property.
- Amenities grid — icon + label per amenity. Grouped: essentials, room, safety, services.
- Rate & availability panel — (requires dates in URL; falls back to "Enter dates to see prices") — shows room types, rate plans, lowest price per night. Book CTA → handoff (J-02).
- Room type cards — photo, name, capacity, amenities, rate. "Select" triggers hold + funnel.
- About / description — multi-locale. Collapsible after 3 lines.
- Policies — check-in time, cancellation, pets, smoking, payment.
- Location section — area description, nearby landmarks, transport.
- Reviews section — (Phase 2, once review service exists).
4.4 Shortlist
URL: /[locale]/shortlist
State: Zustand + React Query backed by GET /consumer/v1/shortlist (BFF-side cookie / account).
Contents:
- Property cards in shortlist order.
- Side-by-side comparison toggle (compare up to 3).
- Remove from shortlist.
- "Book" CTA on each card → handoff.
4.5 Booking handoff
URL transition: /[locale]/[tenantSlug]/book/[propertyId]?rate=...&from=...&to=...&adults=...
This URL segment switches the runtime theme from the meta theme to the tenant's theme (via GET /tenant-booking/v1/bootstrap). The user sees the tenant's brand colors and logo without a page reload — same Next.js App Router; just the <ThemeProvider> context changes.
5. PWA / offline behavior
| Scenario | Behavior |
|---|---|
| First visit with network | All critical paths cached by service worker |
| Repeat visit, network present | StaleWhileRevalidate — instant render from cache, background refresh |
| Network lost during browsing | Serve cached search results; show offline banner (C3 ER-07); new searches blocked (not cached) |
| Network lost mid-search | Cache last successful result page; toast "Offline — showing last results" |
| PWA installed on device | App icon, splash screen, standalone mode (no browser chrome) |
Cached assets: App shell, fonts, last 5 search pages, last viewed property detail pages (3).
Not cached: Payment flows, tenant bootstrap (requires fresh token), auth flows.
6. Performance budget
See ../common/09-non-functional-requirements.md §1.1 for canonical web targets. Home and search result pages are the primary targets.
Additional meta-web-specific targets:
- Search query response (BFF → results rendered): < 800 ms p95
- Property detail hydration: < 300 ms p95
- Map tile load (LTE): < 2 s for all visible tiles
7. SEO
- All property detail pages are SSR (Next.js App Router Server Components) with
generateMetadata(). - Metadata:
og:title,og:description,og:image(hero photo),og:type: "place". - Structured data:
schema.org/HotelJSON-LD on property detail pages. - Canonical URLs include
[locale]prefix but have<link rel="alternate" hreflang>for all supported locales. - Search results page:
noindex(paginated, query-driven). - Sitemap: auto-generated nightly for all published properties.
8. Accessibility
- WCAG 2.2 AA minimum.
- Map: keyboard-navigable pins; list view always available as alternative.
- Date picker: ARIA grid pattern; keyboard date entry supported.
- Filter chips:
role="checkbox"group. - Photo gallery lightbox: focus trap, ESC to close,
aria-labelon navigation arrows. - Search bar:
role="combobox"for destination autocomplete.
9. State management conventions
| State type | Tool | Storage |
|---|---|---|
| Search results | TanStack Query | In-memory + IndexedDB (PWA cache) |
| Active filters / sort | nuqs (URL) | URL |
| Map viewport (bounds, zoom) | nuqs (URL) | URL |
| Shortlist | Zustand + TanStack Query | Cookie (pre-auth) → server (post-auth) |
| Autocomplete suggestions | TanStack Query | In-memory, short TTL |
| Guest session | BFF cookie (gms_id) | HttpOnly cookie |
10. Open Questions
- Home "Near me" section: request geolocation permission proactively or only when tapped? (Preference: only when tapped.)
- Property reviews: Phase 2. Placeholder design needed for property detail — should it show "Reviews coming soon" or hide the section entirely in R1?
- Sitemap: should it include all published properties across all tenants, or only properties with at least one published rate plan?
- PWA install prompt: when to show the add-to-home-screen prompt? After 2 visits (Google's heuristic) or on shortlist save?
References
../common/01-product-overview-frontend.md../catalogs/C8-search-and-filter-patterns.md../journeys/guest/J-01-discover-and-compare-on-meta-layer.md../journeys/guest/J-02-booking-handoff-to-tenant-site.md../../03-microservices/bff-consumer-service.md../../03-microservices/search-aggregation-service.md