Production-quality frontend for a tour and experience booking platform. Curated skip-the-line tickets and guided experiences across Europe's top destinations, starting with Barcelona.
Live: tourhill.com
| Area | Technology |
|---|---|
| Framework | Next.js 16.2 (App Router) |
| Language | TypeScript (strict) |
| Styling | Tailwind CSS v4 |
| State | Redux Toolkit + redux-persist |
| Data fetching | TanStack Query v5 |
| Forms | React Hook Form + Zod |
| Animation | Framer Motion |
| i18n | next-intl (7 locales) |
| Payments | Stripe (frontend contract ready) |
| Toasts | react-hot-toast |
git clone https://github.com/Rafin31/Europe-Tour-Attraction.git
cd Europe-Tour-Attraction
npm install
cp .env.example .env.local
npm run devAll features work without a backend — mock data is used by default.
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_API_URL |
No | Backend REST API base URL. Empty = mock mode. |
NEXT_PUBLIC_SITE_URL |
Production | Canonical URL for OG tags and sitemap |
NEXT_PUBLIC_STRIPE_KEY |
Checkout | Stripe publishable key (pk_...) |
For local backend dev: NEXT_PUBLIC_API_URL=http://localhost:4000
/data/mock/ ← Layer 1: Raw hardcoded data (only place mock data lives)
/lib/api/ ← Layer 2: API abstraction (ONLY layer components call)
/components/ /app/ ← Layer 3: UI (never imports from /data/mock/ directly)
Components call /lib/api/ functions. They have no idea whether data comes from mock or real backend. Switching to real backend = set NEXT_PUBLIC_API_URL, zero component changes.
/app/[locale]/ — pages with locale routing (next-intl)
/components/
/layout/ — Navbar, Footer, MobileMenu
/product/ — PackageCard, BookingBox, RecentlyViewed, ShareButton
/checkout/ — CheckoutStepper, GuestDetailsForm
/cart/ — CartDrawer, CartItemRow, CartBadge
/auth/ — LoginForm, RegisterForm, AuthModal, UserMenu, AuthGuard
/account/ — AccountNav, BookingHistoryCard, ProfileForm
/favorites/ — FavoriteCard, GuestFavoritesGrid
/blog/ — BlogCard, TagFilter
/common/ — LocaleModal, FavoriteButton, NewsletterSignup, ErrorBoundary
/homepage/ — HeroSection, FeaturedPackages, CitySelector, etc.
/providers/ — ReduxProvider, QueryProvider, CurrencyRateSync
/lib/
/api/ — All API abstraction functions (mock + real paths)
/hooks/ — usePrice, useFavorites, useRecentlyViewed
/store/slices/ — authSlice, cartSlice, favoritesSlice, currencySlice, recentlyViewedSlice
/utils/ — formatPrice, formatDate, cn
/constants/ — routes, currencies
/data/mock/ — Mock products, blog posts, reviews, cities, bookings
/types/ — TypeScript interfaces
/messages/ — i18n JSON files (en, es, fr, de, it, nl, pt)
/i18n/ — next-intl routing, request config, navigation exports
/public/ — Static assets
- Browse — Homepage, city hub pages, product detail pages
- Auth — Register, login, forgot password, OAuth-ready (Google/Apple/Facebook contract built)
- Favorites — Works without login, syncs to account on login
- Cart — Multi-item, persisted across sessions
- Checkout — 4-step flow: cart → details → payment → confirmation. Promo codes supported.
- Account — Dashboard, order history, order detail, favorites, profile settings
- Recently Viewed — Persisted strip on homepage + product pages
- Urgency badge on low-stock slots (
spotsLeft ≤ 5) - Native Web Share API with clipboard fallback
- Locale modal (Viator-style) — Language + Currency in one modal
- Error boundaries at global, page, and section level
- Shape-matched skeletons on all data fetches
- Optimistic updates on favorites
- 7 locales:
en(default),es,fr,de,it,nl,pt - Locale-aware navigation via
i18n/navigation.ts(createNavigation) — locale persists across all page navigations localeDetection: false— locale set by URL only, never by browser header- 10 currencies: EUR, GBP, USD, AUD, CAD, CHF, AED, SAR, QAR, KWD
- GCC format:
500 AED(amount before code) — standard in UAE/Saudi/Qatar/Kuwait - Live exchange rates via open.er-api.com (free, no API key)
When backend is ready, switching from mock to live data:
- Set
NEXT_PUBLIC_API_URLin.env.local - In each
/lib/api/*.tsfile: uncomment thefetch()block, remove the mock return npm run build— Zod schemas surface any shape mismatches automatically
Zero component changes. Zero page changes. Zero type changes.
Backend must set auth_token as an httpOnly cookie on login/register. Frontend reads no tokens — Redux stores only user metadata (name, email, id).
GET /auth/oauth/:provider?redirect_uri=... → redirect to provider
GET /auth/oauth/:provider/callback?code=... → set cookie, redirect back
Frontend callback page (app/[locale]/auth/callback/page.tsx) is already built.
- Add
messages/{locale}.jsonwith same key structure asmessages/en.json - Add locale to
localesarray ini18n/routing.ts - Add option to
LANGUAGE_OPTIONSincomponents/common/LanguageSwitcher.tsx
Zero component changes needed.
development — integration branch. All features merge here.
feature/* — one branch per feature. PR into development when complete.
hotfix/* — urgent fixes off development.
feat(scope): description
fix(scope): description
chore|docs|style|refactor|perf|test|security
- Security headers in
next.config.ts(CSP, HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy) unsafe-evalin CSP only in development (React source maps requirement)- Auth tokens in httpOnly cookies only — never localStorage
- Open redirect protection on all
?redirect=params - OAuth CSRF protection via
stateparam stored insessionStorage - All forms validated with Zod before submission
npm audit # before every deployVerify headers at securityheaders.com after deployment.
| Phase | Scope | Status |
|---|---|---|
| 1 | Foundation — Redux store, auth slice, cart slice, persist | ✅ Complete |
| 2 | Auth UI — Login, Register, AuthModal, UserMenu, OAuth contract | ✅ Complete |
| 3 | Favorites — Guest + authed, merge on login, navbar badge | ✅ Complete |
| 4 | Cart — Multi-item drawer, booking auth gate, promo codes | ✅ Complete |
| 5 | Account — Dashboard, orders, settings, profile | ✅ Complete |
| 6 | Error handling — ErrorBoundary, error.tsx, not-found.tsx, Loader2 polish | ✅ Complete |
| 7 | Recently Viewed, urgency badge, share button, CSP fix, favicon | ✅ Complete |
| 8 | GCC — AED/SAR/QAR/KWD currencies, locale modal, locale-aware navigation | ✅ Complete |
| 9 | Arabic (RTL) | ✅ Complete |
- Zero TypeScript errors:
npx tsc --noEmit - Zero console errors in production build
- All security headers present
- Mobile responsive: 375px, 768px, 1280px
- Mock data only accessed through
/lib/api/— never imported directly in components