Skip to content

Repository files navigation

🧭 BD Travel Spirit Guide System

A full-stack platform for managing tour guide operations in Bangladesh, built with Next.js 16 (Beta), MongoDB, and NextAuth v5. It provides a public landing page for travelers and a role-based dashboard for Guides and Assistants to manage tours, bookings, reviews, employees, payments, and real-time communication.


πŸ“š Table of Contents


πŸ›  Tech Stack

Core Framework & Runtime
Technology Version Role
Next.js ^16.0.0-beta.0 App Router framework with Turbopack, hosts both pages and API route handlers
React ^19.2.3 UI library
TypeScript ^5 Static typing across the stack
Data & Infrastructure
  • Database: MongoDB via Mongoose (^8.19.1)
  • Caching & Rate Limiting: Upstash Redis (^1.36.0)
  • Authentication: NextAuth.js v5 (Credentials + Google OAuth)
  • Media Pipeline: Cloudinary
  • State Management: Zustand (^5.0.8)
  • Payments: Stripe
UI & UX Engine
  • Styling: Tailwind CSS 4.0
  • Components: Radix UI primitives (shadcn/ui style)
  • Animations: Framer Motion
  • Visualization: Recharts, React Leaflet
  • Real-time: Socket.IO

πŸ— System Architecture

graph TB
    subgraph "Client Layer"
        UI["React 19 Components"]
        STORE["Zustand Stores"]
    end
    subgraph "Application Layer"
        ROUTES["src/app (Pages)"]
        API["src/app/api (Route Handlers)"]
    end
    subgraph "Middleware"
        PROXY["src/proxy.ts"]
    end
    subgraph "Service Layer"
        HANDLERS["src/lib/handler"]
        BUILDERS["src/lib/build-responses"]
    end
    subgraph "Data Layer"
        MONGO[("MongoDB")]
        REDIS[("Upstash Redis")]
        CLOUDINARY[("Cloudinary")]
    end
    UI --> STORE
    UI --> ROUTES
    ROUTES --> API
    PROXY --> API
    API --> HANDLERS
    HANDLERS --> BUILDERS
    HANDLERS --> MONGO
    HANDLERS --> REDIS
    HANDLERS --> CLOUDINARY
Loading

Every API route is wrapped with a shared withErrorHandler higher-order function that standardizes success/error JSON shapes, and src/proxy.ts applies rate limiting and session/role gating before requests reach the route handlers.


πŸ‘₯ User Roles & Permissions

Role Identifier Permissions
Guide USER_ROLE.GUIDE Full ownership: tours, employees, payment accounts, financial analytics
Assistant USER_ROLE.ASSISTANT Operational support: review moderation, report triage, logistics updates
Admin USER_ROLE.ADMIN Platform-level moderation (tour approval, notifications)
Traveler Public user Browse tours, leave reviews, chat with support

Login is gated at the credential-validation stage: Guides must have an APPROVED GuideModel status, and Assistants must have a non-deleted EmployeeModel record.


✨ Features

🌐 Landing Page
  • Server-rendered hero, advantages, "How it Works", testimonials, and CTA sections
  • Live stats hydrated from MongoDB
  • Sticky navbar with conditional Login/Dashboard rendering
πŸ” Authentication & Authorization
  • NextAuth v5 (Credentials + Google OAuth), JWT sessions (30-day max age, hourly refresh)
  • Email verification via hashed tokens
  • Rate-limited credential validation (10/min per IP, 5/min per email) via Upstash Redis
  • Role-based UI + API gating
πŸ“Š Dashboard
  • KPI grid (tours, bookings, revenue, reports, ratings, staff)
  • Charts for bookings/reviews via Recharts
  • Per-tab date-range filtering, cursor-paginated transactions, CSV export
  • TTL-based client-side caching (useDashboardStore)
πŸ—ΊοΈ Tour Management
  • Lifecycle: DRAFT β†’ SUBMITTED β†’ APPROVED β†’ ACTIVE β†’ COMPLETED/ARCHIVED/TERMINATED
  • Multi-step wizard with modular PATCH endpoints per schema section
  • Bangladesh-specific geography and itinerary modeling
  • Moderation workflow (archive/terminate/re-approval)
⭐ Review & 🚩 Report Management
  • Threaded replies with approval gating
  • Report triage with priority and bulk-resolve actions
πŸ‘” Employee Management & πŸ’³ Payments
  • Payroll, shifts, soft-deletion, Stripe payment account linkage
πŸ’¬ Real-time Communication
  • Socket.IO presence tracking and system notifications with priority-based TTL

πŸ“ Project Structure

Root Structure
bd-travel-spirit-guide-system/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ app/                # Routes + API handlers
β”‚   β”œβ”€β”€ components/         # React components
β”‚   β”œβ”€β”€ config/             # Stripe/DB config
β”‚   β”œβ”€β”€ constants/          # Enums
β”‚   β”œβ”€β”€ lib/                # Business logic, builders, handlers, helpers
β”‚   β”œβ”€β”€ models/              # Mongoose schemas
β”‚   β”œβ”€β”€ socket/              # Socket.IO helpers
β”‚   β”œβ”€β”€ store/                # Zustand stores
β”‚   β”œβ”€β”€ types/                # Shared types/DTOs
β”‚   β”œβ”€β”€ utils/                 # API clients, validators
β”‚   └── proxy.ts               # Middleware (rate-limit + auth guard)
β”œβ”€β”€ public/
└── package.json
src/app/api β€” API Route Handlers
src/app/api/
β”œβ”€β”€ auth/
β”‚   β”œβ”€β”€ [...nextauth]/           # NextAuth handler (signIn/signOut/session)
β”‚   β”œβ”€β”€ token/v1/                 # Token-based flows
β”‚   └── user/v1/
β”‚       β”œβ”€β”€ validate/             # Pre-NextAuth credential validation
β”‚       β”œβ”€β”€ password/             # Password change/reset
β”‚       β”œβ”€β”€ audits/               # Audit log queries
β”‚       β”œβ”€β”€ employee/, owner/, name/
β”‚       └── route.ts              # User CRUD (register)
β”œβ”€β”€ dashboard/v1/
β”‚   β”œβ”€β”€ stats/, tours/, bookings/, reviews/, reports/
β”‚   β”œβ”€β”€ employees/, running-tours/, faqs/, refunds/, transactions/
β”‚   β”œβ”€β”€ profile/, export/, search/
β”œβ”€β”€ operations/
β”‚   β”œβ”€β”€ tours/v1/
β”‚   β”‚   β”œβ”€β”€ route.ts                        # GET list / POST create
β”‚   β”‚   └── [tourId]/
β”‚   β”‚       β”œβ”€β”€ route.ts                    # GET detail / POST submit-for-approval
β”‚   β”‚       β”œβ”€β”€ bangladesh-fields/          # PATCH step-1
β”‚   β”‚       β”œβ”€β”€ logistics/                  # PATCH step-3
β”‚   β”‚       β”œβ”€β”€ pricing/                    # PATCH step-4
β”‚   β”‚       β”œβ”€β”€ gallery/                    # PATCH gallery images
β”‚   β”‚       β”œβ”€β”€ destinations/images-bulk/   # PATCH destination images
β”‚   β”‚       β”œβ”€β”€ destinations/attractions/images-bulk/
β”‚   β”‚       └── moderation-status/
β”‚   β”‚           β”œβ”€β”€ archive/                # DELETE (soft-delete/archive)
β”‚   β”‚           └── terminate/              # DELETE (terminate + refunds)
β”‚   β”œβ”€β”€ bookings/v1/
β”‚   β”‚   β”œβ”€β”€ route.ts
β”‚   β”‚   └── summary/
β”‚   β”œβ”€β”€ reviews/v1/
β”‚   β”‚   β”œβ”€β”€ route.ts
β”‚   β”‚   └── [reviewId]/replies/[replyId]/   # PATCH/DELETE reply
β”‚   └── reports/v1/
β”‚       β”œβ”€β”€ route.ts
β”‚       β”œβ”€β”€ [reportId]/
β”‚       └── bulk-resolve/
β”œβ”€β”€ users/employees/v1/
β”‚   β”œβ”€β”€ route.ts               # POST create employee
β”‚   β”œβ”€β”€ [employeeId]/          # GET/PATCH/DELETE
β”‚   └── payroll/
β”œβ”€β”€ settings/payment-accounts/v1/
β”‚   β”œβ”€β”€ route.ts
β”‚   └── [id]/
β”œβ”€β”€ support/
β”‚   β”œβ”€β”€ password-requests/v1/  # Employee forgot-password workflow
β”‚   └── tour-faq/
β”‚       β”œβ”€β”€ route.ts
β”‚       β”œβ”€β”€ [faqId]/
β”‚       └── stats/
β”œβ”€β”€ notifications/guide/v1/
β”‚   β”œβ”€β”€ route.ts
β”‚   └── [id]/
└── (mock)/mock/                # Mock/dev-only endpoints

πŸ”Œ API Usage

All API routes live under src/app/api following Next.js App Router conventions (route.ts files exporting GET/POST/PATCH/DELETE). Every handler is wrapped by withErrorHandler.

Response & Error Contract

Every route handler returns a HandlerResult<T> ({ data, status? }), and withErrorHandler converts it into a NextResponse:

  • Success: { "data": <T> } with the given HTTP status (default 200)
  • Failure: { "error": "<message>" } with a status derived from a thrown ApiError(message, status), or 500 for unhandled errors
// Example handler shape
export const PATCH = withErrorHandler(async (req, { params }) => {
  // ...validation, DB ops...
  return { data: result, status: 200 };
});

Throwing throw new ApiError("Tour not found", 404) anywhere inside a handler is automatically caught and serialized.

Authentication Flow
  1. Credential pre-validation β€” POST /api/auth/user/v1/validate checks email/password against UserModel, enforces IP- and email-based rate limiting (10/min per IP, 5/min per email) via authRateLimit (Upstash Redis), and confirms role eligibility (GUIDE must be APPROVED, ASSISTANT must not be soft-deleted) before returning a minimal { id, email, role } payload.
  2. NextAuth session issuance β€” src/app/api/auth/[...nextauth]/ (backed by src/lib/auth/options.auth.ts) re-runs the same checks inside its CredentialsProvider.authorize and signIn callback (for Google OAuth), then issues a JWT session (30-day max age, refreshed hourly).
  3. Session consumption β€” Downstream API routes call requireSessionUserId() / getUserIdFromSession() to identify the caller, and VERIFY_USER_ROLE.GUIDE(...) / .MULTIPLE([...]) helpers to enforce role-based authorization per endpoint.
Middleware: Rate Limiting & Route Protection (`src/proxy.ts`)
  • Public routes pass through without checks.
  • All other /api/* routes are rate-limited to 100 requests / 60 seconds per IP, returning 429 on breach.
  • Protected pages require a valid NextAuth session (redirect to / if missing).
  • Admin-tier pages (ADMIN_ROLES = [ADMIN, GUIDE]) redirect non-privileged users to /dashboard?error=unauthorized.
Tour Endpoints (/api/operations/tours/v1)
Method Path Purpose
GET / List tours (filterable)
POST / Create a new tour (DRAFT status)
GET /[tourId] Full tour detail via buildTourDetailDTO
POST /[tourId] Submit tour for moderation/re-approval (creates a SupportSystemNotification and triggers a socket event to admins)
PATCH /[tourId]/bangladesh-fields Update step-1 (division, district, accommodation, etc.), validated via Step1BangladeshSchema
PATCH /[tourId]/logistics Update step-3 logistics (main location, transport), validated via Step3LogisticsSchema
PATCH /[tourId]/pricing Update step-4 pricing/discounts/duration, validated via Step4PricingSchema
PATCH /[tourId]/gallery Replace/add gallery images (base64 or Cloudinary URL), resolves & dedupes assets outside the DB transaction
PATCH /[tourId]/destinations/images-bulk Bulk add/delete images for a destination
PATCH /[tourId]/destinations/attractions/images-bulk Bulk add/delete images for a specific attraction within a destination
DELETE /[tourId]/moderation-status/archive Soft-delete/archive a tour (Guide/Assistant only)
DELETE /[tourId]/moderation-status/terminate Terminate a tour, cascading into bookings/transactions/refunds via Stripe

All mutating endpoints share common guardrails: tours in TERMINATED, ARCHIVED, or ACTIVE/COMPLETED states reject further edits with 409 Conflict, and every mutation is wrapped in withTransaction for atomicity, followed by auditTourMutation logging.

Review Endpoints (/api/operations/reviews/v1)
Method Path Purpose
GET/other / List/query reviews
POST /[reviewId]/replies Add a reply (Guide/Assistant only), rejects if review isn't isApproved
PATCH /[reviewId]/replies/[replyId] Edit own reply
DELETE /[reviewId]/replies/[replyId] Soft-delete own reply
Booking & Report Endpoints
  • /api/operations/bookings/v1 β€” route.ts (CRUD/list) + summary/ (aggregate stats)
  • /api/operations/reports/v1 β€” route.ts (list/create), [reportId]/ (detail/update), bulk-resolve/ (batch resolution action)
Dashboard Endpoints (/api/dashboard/v1)

Sub-resources: stats/, tours/, bookings/, reviews/, reports/, employees/, running-tours/, faqs/, refunds/, transactions/, profile/, export/, search/ β€” each accepts date-range query params (e.g. statsDateRangeFrom/statsDateRangeTo) matching the DashboardFilters shape consumed by useDashboardStore on the client, and export/ produces CSV downloads.

Employee & Payment Endpoints
  • POST /api/users/employees/v1 β€” create employee (validates via Yup, requires GUIDE role, uploads avatar/documents to Cloudinary, creates linked UserModel with ASSISTANT role)
  • /api/users/employees/v1/[employeeId] β€” detail/update/terminate
  • /api/users/employees/v1/payroll β€” payroll record management
  • /api/settings/payment-accounts/v1 and [id]/ β€” Stripe payment account CRUD
Support Endpoints
  • POST /api/support/password-requests/v1 β€” employee "forgot password" request; rate-limited per email (5/min), branches by role (ASSISTANT vs GUIDE), rejects duplicate pending requests
  • /api/support/tour-faq/ β€” route.ts (list/create), [faqId]/ (update/delete), stats/ (aggregate counts)
Notification Endpoints
  • /api/notifications/guide/v1 β€” route.ts (list) + [id]/ (mark read/delete). Notifications carry priority-based TTL: LOW/MEDIUM expire after 60 days, HIGH/CRITICAL never auto-expire.
Calling Conventions Summary
  • Dynamic route params are Promise<{ id: string }>-typed (Next.js 15+/16 async params) and resolved with resolveMongoId(...).
  • Most mutation routes: (1) resolve/validate the ID, (2) resolve the acting user via requireSessionUserId(), (3) validate body with Yup/Zod, (4) run DB writes inside withTransaction, (5) call auditTourMutation/logAuditForActor for audit trail, (6) return a rebuilt DTO via buildTourDetailDTO or similar builder.
  • Cloudinary uploads are deliberately executed outside Mongo transactions to avoid the 60-second transaction timeout, with asset dedup/cleanup handled separately.

πŸš€ Getting Started

Prerequisites
Requirement Version Purpose
Node.js 20.x+ Runtime
npm 10.x+ Package management
MongoDB 7.0+ Database
Redis Upstash / Local Caching & rate limiting
Installation & Development
git clone https://github.com/ByteCrister/bd-travel-spirit-guide-system
cd bd-travel-spirit-guide-system
npm install
npm run dev     # http://localhost:3000, Turbopack
npm run build
npm run lint

Configure a .env with MongoDB, NextAuth, Cloudinary, Stripe, and Upstash Redis credentials (.env* is git-ignored).


βš™οΈ Configuration

File Purpose
tsconfig.json @/* β†’ src/* path alias
next.config.ts Image remote patterns (Cloudinary, GitHub avatars, etc.)
postcss.config.mjs Tailwind CSS 4.0
eslint.config.mjs Lint presets

πŸ—„ Data Models

erDiagram
    "UserModel" ||--o| "GuideModel" : "owns"
    "UserModel" ||--o| "EmployeeModel" : "references"
    "GuideModel" ||--o{ "TourModel" : "operates"
    "TourModel" ||--o{ "AssetModel" : "has (gallery)"
    "AssetModel" ||--|| "AssetFileModel" : "points to"
    "ReviewModel" ||--|| "TourModel" : "belongs to"
    "ReportModel" ||--|| "TourModel" : "targets"
Loading

🧩 State Management

Key stores: useDashboardStore, useTourDetailStore, useReviewsStore, useReportsStore, useEmployeeStore, useNotificationStore, useCurrentUserStore, usePaymentAccountStore β€” using TTL caching and in-flight request de-duplication.


πŸ“– Glossary

Term Meaning
Guide Tour operator/business owner role
Assistant Employee/support role
DTO Data Transfer Object returned by builder functions like buildTourDetailDTO
ApiError Custom error class carrying an HTTP status, caught by withErrorHandler
withTransaction Helper wrapping Mongo multi-document writes in a session/transaction
Asset / AssetFile Two-tier deduplicated media model

Releases

Packages

Contributors

Languages