ALL PROJECTS/HAKEEM JAN TRAVELS

Overview

Hakeem Jan Travels is a travel and pilgrimage booking product for a working travel agency. The public site presents destinations, tour and Umrah packages, and a travel blog; behind it, a Django service records bookings and enquiries and pushes them into Google Calendar and Google Sheets. It serves two audiences at once — prospective travellers browsing packages, and agency staff who need every enquiry to land somewhere they already look.

Problem

Most booking systems ask an agency to abandon its existing workflow. Hakeem Jan Travels ran its scheduling out of Google Calendar and kept booking records in Google Sheets, and had no appetite for a second source of truth that someone would have to reconcile by hand. The brief was therefore narrower and harder than "build a booking site": the online booking flow had to terminate inside the agency's existing tools, not beside them. A submitted booking needed to become a calendar entry and a spreadsheet row without a staff member re-typing it, and the marketing surface — destinations, packages, blog — had to be editable by non-engineers through an interface they could learn in an afternoon.

System Design

The system is split across two repositories. The front end is a React 18 single-page application built with Vite 4 and TypeScript 5, styled with Tailwind CSS and PostCSS/Autoprefixer. Routing is handled by react-router-dom v6, iconography by lucide-react, and destination and package carousels by Swiper. It is a static build (tsc && vite build) deployed to Vercel — a vercel.json handles SPA rewrites, and .env.production carries the API base URL so the same bundle can point at a local or deployed backend. Keeping the front end fully static means the marketing surface stays fast and cheap to serve, with all dynamic content arriving over HTTP from the Django API.

The backend is a Django 5.2 project organised into focused apps that map cleanly onto the domain: destinations for the catalogue, blogs for editorial content, book_services for the booking and enquiry models, apis as the HTTP surface that the SPA consumes, and two dedicated integration apps — google_calender and google_sheet — that own all outbound traffic to Google. Isolating the integrations in their own apps rather than scattering API calls through view code means the Google credential handling, client construction and error paths live in one place, and the booking models stay ignorant of how they are ultimately persisted downstream. django-cors-headers allows the Vercel-hosted origin to call the API; whitenoise serves the admin and template static assets; Pillow backs image fields on destinations and blog posts.

Google access is server-to-server via a service account rather than per-user OAuth, so no staff member has to hold a session for a booking to sync. The stack uses google-api-python-client with google-auth and google-auth-oauthlib for the Calendar API, and gspread as a higher-level client for Sheets — a sensible split, since Sheets work is row append and read, while Calendar needs the full resource model. Credentials live in a credentials/ directory, with configuration loaded from the environment via python-dotenv. Deployment is containerised: a Dockerfile and docker-compose.yml bring up the app behind an nginx reverse proxy, entrypoint.sh runs migrations and collects static files on boot, gunicorn serves WSGI, and a .gitlab-ci.yml drives the pipeline. Operational detail is documented in-repo (RUN_MIGRATIONS.md, HERO_SECTION_API.md), and one-off management scripts — seed_enquiry.py, update_enquiry_colors.py — seed and backfill the enquiry data used by the admin.

How It Works

01/03
01

Content Delivery

A visitor browses destinations, packages and blog posts served from the Django API into the static React SPA.

  1. 1A visitor loads the Vercel-hosted SPA; Vite's static bundle is served and React Router resolves the route client-side.
  2. 2The page component calls the Django `apis` app over HTTPS using the base URL baked in at build time from `.env.production`.
  3. 3Django resolves the request against the `destinations` or `blogs` app, serialising catalogue entries, package detail or blog posts.
  4. 4`django-cors-headers` validates the requesting origin before the response is returned.
  5. 5React renders the content; Swiper drives destination and package carousels, and images are served from Django's Pillow-backed media fields.
02

Booking & Enquiry Capture

A traveller's booking submission is validated, persisted to the book_services models, and surfaced to agency staff in the Django admin.

  1. 1A visitor selects an Umrah or tour package and completes the booking form in the React front end.
  2. 2The SPA POSTs the booking payload to the `apis` endpoint exposed by the Django backend.
  3. 3Django validates the payload and persists it against the `book_services` models, creating the booking or enquiry record.
  4. 4The `book_services` layer hands the persisted record to the integration apps for downstream sync.
  5. 5The record becomes immediately visible in the Django admin, where agency staff manage it — enquiry states carry the colour coding introduced by `update_enquiry_colors.py`.
03

Google Calendar & Sheets Sync

Each persisted booking propagates outward into the agency's existing Google Calendar and Google Sheets via server-to-server service-account auth.

  1. 1The `google_calender` and `google_sheet` apps load service-account credentials from the `credentials/` directory, with paths and IDs resolved from environment variables via python-dotenv.
  2. 2`google-auth` mints an authorised transport, used to build a Calendar client through `google-api-python-client` and a Sheets client through `gspread`.
  3. 3The booking is translated into a Calendar event — dates, package, traveller details — and inserted into the agency's calendar.
  4. 4The same booking is appended as a row to the agency's Google Sheet, giving staff their existing tabular record without manual entry.
  5. 5Because the sync happens downstream of the database write, the Django record remains the system's own source of truth independent of the Google call.

Key Features

  • Destination & package browsing
  • Umrah & tour booking flow
  • Google Calendar scheduling
  • Google Sheets record-keeping
  • Travel blog
  • Django admin backend

Outcomes

  • Bookings flow into existing Google tools
  • Less manual record-keeping
  • Content-rich marketing site

More work