Skip to content

Rafin31/Europe-Tour-Attraction

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

113 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TourHill — Europe Tour & Experience Booking

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


Tech Stack

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

Quick Start

git clone https://github.com/Rafin31/Europe-Tour-Attraction.git
cd Europe-Tour-Attraction
npm install
cp .env.example .env.local
npm run dev

Open http://localhost:3000

All features work without a backend — mock data is used by default.


Environment Variables

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


Architecture

Three-layer data architecture

/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.

Directory Structure

/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

Features

User flows

  • 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

UX

  • 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

i18n & Localisation

  • 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)

Backend Integration

When backend is ready, switching from mock to live data:

  1. Set NEXT_PUBLIC_API_URL in .env.local
  2. In each /lib/api/*.ts file: uncomment the fetch() block, remove the mock return
  3. npm run build — Zod schemas surface any shape mismatches automatically

Zero component changes. Zero page changes. Zero type changes.

Auth contract

Backend must set auth_token as an httpOnly cookie on login/register. Frontend reads no tokens — Redux stores only user metadata (name, email, id).

OAuth contract

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.


i18n — Adding a New Language

  1. Add messages/{locale}.json with same key structure as messages/en.json
  2. Add locale to locales array in i18n/routing.ts
  3. Add option to LANGUAGE_OPTIONS in components/common/LanguageSwitcher.tsx

Zero component changes needed.


Git Workflow

development   — integration branch. All features merge here.
feature/*     — one branch per feature. PR into development when complete.
hotfix/*      — urgent fixes off development.

Commit format (Conventional Commits)

feat(scope): description
fix(scope): description
chore|docs|style|refactor|perf|test|security

Security

  • Security headers in next.config.ts (CSP, HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy)
  • unsafe-eval in 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 state param stored in sessionStorage
  • All forms validated with Zod before submission
npm audit                          # before every deploy

Verify headers at securityheaders.com after deployment.


Build Status

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

Definition of Done

  • 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

About

Spain tour and experience booking website. Curated skip-the-line tickets and expert guided tours for Spain's top attractions, starting with Barcelona. Built with Next.js 15, TypeScript (strict), Tailwind CSS, Zustand, TanStack Query, Zod, React Hook Form, Framer Motion, and next-intl.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages