Portfolio
Loading Shiham's portfolio
Preparing the latest projects, skills, and contact paths with a quiet production-ready polish.
Initializing portfolio
Projects ยท Skills ยท Contact
Portfolio
Preparing the latest projects, skills, and contact paths with a quiet production-ready polish.
Initializing portfolio
Projects ยท Skills ยท Contact
A full-stack travel booking playground for developers learning Playwright E2E testing through authentication, reservations, API mocking, and CI workflows.
Built as a personal learning project, Smart Travel provides a realistic full-stack target for practising browser automation rather than presenting itself as a deployed travel service. It is intended for developers exploring Playwright against authenticated, data-backed user journeys.
Users can register and log in, search hotel and vehicle listings, submit date-based reservations, and review stored bookings in a protected dashboard. The interface also includes responsive navigation, dark and light themes, and a concierge widget whose responses are explicitly static.
The React and TypeScript client communicates with an Express REST API backed by MongoDB. The Playwright suite exercises real authentication and booking flows, isolates test accounts with reusable fixtures, intercepts APIs for deterministic UI scenarios, and is wired into a GitHub Actions workflow. The project was completed independently.
Related projects
Jan 2026 โ Jun 2026
A multi-vendor commerce platform connecting buyers, sellers, and administrators through dedicated storefronts, dashboards, payments, chat, and personalized discovery.
A React and TypeScript single-page client calls an Express REST API through Axios. Mongoose persists users, hotels, vehicles, and embedded booking records in MongoDB, while Playwright orchestrates the client and API for E2E tests and GitHub Actions supplies the CI environment.
A full-stack travel booking web application purpose-built as a Playwright E2E testing playground. Covers authentication flows, hotel & vehicle bookings, API mocking, UI validation, custom fixtures, and CI/CD integration with GitHub Actions.
Smart Travel is a full-stack travel booking platform that lets users browse luxury hotels, rent vehicles, register and log in with JWT authentication, and track their bookings in a personal dashboard. The primary purpose of this project is to serve as a realistic, real-world playground for learning and demonstrating Playwright E2E automation testing techniques.
The project demonstrates:
page.route()| Feature | Description |
|---|---|
| Home Page | Cinematic parallax hero, destination search bar, featured hotels section |
| Hotels Page | Browse hotel cards with location, price, and ratings; booking modal with date selection |
| Vehicles Page | Browse vehicle listings; rental modal with date selection |
| Authentication | Register and login with JWT tokens stored in localStorage; protected route guard |
| Dashboard | User portal displaying profile info and full booking history |
| Responsive Navbar | Desktop + mobile navigation, dark/light mode toggle, scroll-aware glass effect |
| AI Concierge Chat | Floating chat widget (simulated static responses) |
| Dark Mode | Full dark/light theme via React Context |
Note: Social login buttons (GitHub/Google), OAuth, payment processing, and real AI chat responses are not implemented. These are visual placeholders only.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MONOREPO ROOT โ
โ playwright.config.ts ยท package.json ยท .env โ
โโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ CLIENT โ SERVER โ
โ React 19 + TypeScript โ Express + MongoDB โ
โ Vite + Tailwind CSS โ Mongoose + JWT + bcrypt โ
โ Port: 5173 โ Port: 5005 โ
โ โ โ
โ /src/pages/ โ /routes/auth.routes.js โ
โ Home.tsx โ /routes/booking.routes.js โ
โ Hotels.tsx โ /routes/user.routes.js โ
โ Vehicles.tsx โ /routes/test.routes.js โ
โ AuthPage.tsx โ โ
โ Dashboard.tsx โ /models/ User / Hotel / โ
โ โ Vehicle โ
โ /src/components/ โ โ
โ Navbar.tsx โ /middleware/ auth / validate โ
โ ChatAssistant.tsx โ / errorHandler โ
โ โ โ
โ /src/context/ โ MongoDB โ
โ AuthContext โ smart-travel (dev) โ
โ AuthProvider โ smart-travel-test (test) โ
โ ThemeContext โ โ
โ ThemeProvider โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ PLAYWRIGHT TESTS โ
โ tests/ โ
โ login.test.ts โ Auth flows โ
โ booking.test.ts โ Hotel & vehicle booking โ
โ api-mock.test.ts โ Network interception โ
โ ui-validation.test.ts โ UI & responsiveness โ
โ fixtures.ts โ Custom test fixtures โ
โ test-data.ts โ User creation & cleanup โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/client)| Technology | Version | Purpose |
|---|---|---|
| React | 19 | UI component library |
| TypeScript | ~5.9 | Type safety across components |
| Vite | 7 | Dev server and production bundler |
| Tailwind CSS | 3.4 | Utility-first styling |
| Framer Motion | 11 | Page and component animations |
| React Router DOM | 6 | Client-side routing |
| Axios | 1.7 | HTTP client with interceptors |
| React Hot Toast | 2.4 | Toast notification system |
| Lucide React |
/server)| Technology | Version | Purpose |
|---|---|---|
| Node.js | 20 | JavaScript runtime |
| Express | 4.19 | HTTP server and routing |
| Mongoose | 8.4 | MongoDB ODM |
| MongoDB | 7 | NoSQL database |
| bcryptjs | 2.4 | Password hashing |
| jsonwebtoken | 9.0 | JWT creation and verification |
| dotenv | 16.4 | Environment variable loading |
| cors | 2.8 | Cross-Origin Resource Sharing |
| nodemon | 3.1 |
| Technology | Version | Purpose |
|---|---|---|
| Playwright | 1.58.2 | E2E testing framework |
@playwright/test | 1.58.2 | Test runner, assertions, fixtures |
| Tool | Purpose |
|---|---|
| GitHub Actions | CI pipeline |
| concurrently | Run client + server simultaneously in dev |
Ensure the following are installed before getting started:
node -vnpm -v27017 (Download) or a remote MongoDB URI (e.g. MongoDB Atlas)To install Playwright's browser binaries (required once after cloning):
npx playwright install --with-deps chromium
git clone https://github.com/theShihamAhamed/Smart-Travel_Playwright_PlayGround.git
cd Smart-Travel_Playwright_PlayGround
The root package.json provides a single command that installs dependencies for root, client, and server:
npm run install:all
This is equivalent to running the following three commands in sequence:
npm install --legacy-peer-deps
cd client && npm install --legacy-peer-deps
cd ../server && npm install --legacy-peer-deps
--legacy-peer-depsis required because React 19 and some tooling packages have not yet published peer-dep declarations for it.
Copy the provided example files for each workspace:
macOS / Linux:
cp .env.example .env
cp client/.env.example client/.env
cp server/.env.example server/.env
Windows (Command Prompt):
copy .env.example .env
copy client\.env.example client\.env
copy server\.env.example server\.env
Windows (PowerShell):
Copy-Item .env.example .env
Copy-Item client\.env.example client\.env
Copy-Item server\.env.example server\.env
.env (Playwright + test server defaults)# URL Playwright navigates to in browser
PLAYWRIGHT_BASE_URL=http://localhost:5173
# URL used inside test-data.ts for direct API calls
API_BASE_URL=http://localhost:5005/api
# Separate database used only during Playwright runs
MONGODB_URI=mongodb://127.0.0.1:27017/smart-travel-test
# Must match the server's JWT_SECRET
JWT_SECRET=replace-with-a-local-secret
# Forwarded to the test server for CORS allow-list
CLIENT_URL=http://localhost:5173
CORS_ORIGIN=http://localhost:5173,http://127.0.0.1:5173
server/.envPORT=5005
MONGODB_URI=mongodb://127.0.0.1:27017/smart-travel
JWT_SECRET=replace-with-a-local-secret
# Allowed origins for CORS (comma-separated)
CLIENT_URL=http://localhost:5173
CORS_ORIGIN=http://localhost:5173,http://127.0.0.1:5173
# Set to false to skip demo user seeding
SEED_DEMO_USER=true
โ ๏ธ
JWT_SECRETis required. The server will throw an error and exit on startup if it is missing.
client/.envVITE_API_BASE_URL=http://localhost:5005/api
npm run dev
This uses concurrently to run both:
cd client && npm run dev โ Vite dev server at http://localhost:5173cd server && npm run dev โ Express server with nodemon at http://localhost:5005# Terminal 1 โ Backend
npm run dev --prefix server
# Terminal 2 โ Frontend
npm run dev --prefix client
# Build the React app
npm run build --prefix client
# Start the backend in production mode
npm run start --prefix server
# Lint the frontend
npm run lint --prefix client
# Lint the backend
npm run lint --prefix server
Playwright is configured in playwright.config.ts to automatically start both the backend and frontend before running any tests. This means you do not need to manually start the application before running tests.
npx playwright test
โ
โโโ Starts: npm run start --prefix server (port 5005)
โ waits for: http://127.0.0.1:5005/api/health
โ
โโโ Starts: npm run dev --prefix client (port 5173)
โ waits for: http://127.0.0.1:5173
โ
โโโ Runs all test files in /tests/
The test suite uses a dedicated test database (smart-travel-test) that is separate from the development database (smart-travel). This ensures tests never pollute or depend on your local development data.
All authenticated tests create unique isolated users per test run (via makeTestUser + createTestUser) and clean them up in a finally block through the /api/test/cleanup-user route (which is only registered when NODE_ENV=test).
tests/login.test.ts โ Authentication FlowsTests the full register โ login journey and invalid credentials handling.
| Test | Description |
|---|---|
User can register and login successfully | Fills the registration form, submits, gets redirected to /login, logs in, and verifies the dashboard renders the correct user name |
Shows error for invalid credentials | Submits wrong credentials on /login and asserts that the react-hot-toast error message contains "Invalid credentials" |
Key techniques used:
page.getByTestId() for test-id-based selector queriesexpect(page).toHaveURL() for navigation assertionstest.info() passed to makeTestUser to generate unique emails per testfinally block guarantees test user deletion even if assertions failtests/booking.test.ts โ Hotel & Vehicle Reservation FlowsTests the end-to-end hotel and vehicle booking process using a pre-authenticated page session.
| Test | Description |
|---|---|
User can book a hotel and see it in the dashboard | Navigates to /hotels, locates the "Grand Luxury Resort" card, clicks "BOOK NOW", fills the date, confirms, and then verifies the booking appears on /dashboard |
User can rent a vehicle successfully | Navigates to /vehicles, locates the "Toyota Camry" card, reserves it with a date, and verifies it appears on the dashboard |
Key techniques used:
loggedInPage custom fixture (see Fixtures section) for pre-authenticated sessionsgetByTestId('hotel-grid').locator('[data-testid^="hotel-card-"]').filter({ hasText: '...' })page.locator('div[role="status"]').first()tests/api-mock.test.ts โ Network Interception & API MockingTests Playwright's ability to intercept HTTP requests and return custom responses, completely decoupling UI rendering from the real backend.
| Test | Description |
|---|---|
Mock hotel listing API and display custom results | Intercepts **/api/hotels and fulfills it with a custom JSON payload; asserts the mocked hotel name "Playwright Mock Hotel" appears on the hotels page |
Simulate server error and verify alert message | Intercepts **/api/vehicles with a status: 500 response; asserts the UI displays "Failed to load vehicles" |
Key techniques used:
page.route(pattern, handler) for request interceptionroute.fulfill({ json }) for returning a custom response bodyroute.fulfill({ status: 500, body: ... }) for simulating server errorstests/ui-validation.test.ts โ UI Elements & ResponsivenessTests static UI elements, navigation links, and mobile menu behavior.
| Test | Description |
|---|---|
Verify home page title and hero section elements | Asserts <title> matches /SmartTravel/, hero title is visible, search input placeholder is correct, and the search button is enabled |
Validate navigation menu links and active states | Clicks the Hotels and Vehicles nav links and verifies URL changes and page titles; clicks the logo and verifies return to / |
Check mobile toggle visibility on small screens | Sets viewport to 375ร667, asserts the mobile hamburger is visible and desktop links are hidden; clicks toggle and verifies mobile menu items appear |
Key techniques used:
expect(page).toHaveTitle() for document title assertionspage.setViewportSize({ width, height }) for mobile simulation.toBeVisible(), .not.toBeVisible()page.getByRole('button', { name }) for accessible queriesFile: tests/fixtures.ts
Playwright fixtures extend the base test object to inject shared, reusable state into any test that requests it. This project defines two custom fixtures:
// Extend the base test with custom fixtures
export const test = base.extend<MyFixtures>({
// testUser: Creates a real test user before the test and deletes it after
testUser: async ({ request }, use, testInfo) => {
const user = makeTestUser(testInfo);
await createTestUser(request, user);
try {
await use(user); // <-- test runs here
} finally {
await deleteTestUser(request, user.email); // guaranteed cleanup
}
},
// loggedInPage: Returns a page that is already logged in as testUser
loggedInPage: async ({ page, testUser }, use) => {
await page.goto('/login');
await page.getByTestId('auth-email-input').fill(testUser.email);
await page.getByTestId('auth-password-input').fill(testUser.password);
await page.getByTestId('auth-submit-btn').click();
await expect(page).toHaveURL('/dashboard');
await use(page); // <-- test runs here with authenticated session
},
});
Usage in tests:
import { test, expect } from './fixtures';
// Request loggedInPage โ Playwright automatically:
// 1. Creates a real user in the DB
// 2. Navigates to /login and performs login
// 3. Hands the authenticated page to the test
// 4. Cleans up the user after the test finishes
test('User can book a hotel', async ({ loggedInPage }) => {
await loggedInPage.goto('/hotels');
// ...
});
File: tests/test-data.ts
| Export | Description |
|---|---|
API_BASE_URL | Falls back to http://localhost:5005/api if env var not set |
TEST_PASSWORD | Shared constant PlaywrightPass123! used across all test users |
makeTestUser(testInfo, name?) | Generates a unique user object with a deterministic email derived from the test title, worker index, timestamp, and a random suffix โ ensuring no email collisions during parallel runs |
createTestUser(request, user) | POSTs to /api/auth/register and asserts response.ok() |
deleteTestUser(request, email) | DELETEs via /api/test/cleanup-user โ only available when NODE_ENV=test |
Email uniqueness formula:
pw-{sanitized-test-title}-{workerIndex}-{timestamp}-{random}@playwright.test
Example: pw-user-can-book-a-hotel-0-1719385200000-a3x9b@playwright.test
npx playwright test
# or via the root script
npm test
npx playwright test tests/login.test.ts
npx playwright test tests/booking.test.ts
npx playwright test tests/api-mock.test.ts
npx playwright test tests/ui-validation.test.ts
npx playwright test --grep "login"
npx playwright test --grep "mobile"
npx playwright test --grep "hotel"
npx playwright test --headed
npx playwright test --ui
npx playwright show-report
# Run a single test in debug mode (pause at each step in browser)
npx playwright test tests/login.test.ts --debug
# Run with Playwright Inspector (step through actions manually)
PWDEBUG=1 npx playwright test
# Run with verbose console output
npx playwright test --reporter=list
# Print all collected test names without running them
npx playwright test --list
# Run only on a specific browser (chromium is configured by default)
npx playwright test --project=chromium
# Simulate a specific device
npx playwright test --device="iPhone 13"
# Run in slow motion (useful for visual debugging)
npx playwright test --headed --slow-mo=500
# Run with a single worker (serial execution)
npx playwright test --workers=1
# Run with maximum parallelism
npx playwright test --workers=4
# Limit to only failed tests from the previous run
npx playwright test --last-failed
# Retry failed tests up to 3 times
npx playwright test --retries=3
# Output results as a JSON file
npx playwright test --reporter=json > results.json
# Output results in JUnit XML format (for CI integration)
npx playwright test --reporter=junit > results.xml
# Use dot reporter (compact, one dot per test)
npx playwright test --reporter=dot
# Use multiple reporters at once
npx playwright test --reporter=list,html
# Record user interactions and generate test code automatically
npx playwright codegen http://localhost:5173
# Codegen with a specific device
npx playwright codegen --device="Pixel 5" http://localhost:5173
# Codegen and save directly to a file
npx playwright codegen --output=tests/new-test.ts http://localhost:5173
# Capture a screenshot from a specific test
# (use page.screenshot() inside a test, or configure in playwright.config.ts)
# Open a recorded trace file in the Trace Viewer
npx playwright show-trace path/to/trace.zip
# Install all browsers
npx playwright install
# Install only Chromium
npx playwright install chromium
# Install Chromium along with system dependencies (required on CI)
npx playwright install --with-deps chromium
# List installed browser versions
npx playwright --version
# Create a new Playwright project from scratch (interactive wizard)
npm init playwright@latest
# Generate a new test file template in an existing project
npx playwright test --init
The playwright.config.ts file at the root controls all test behaviour:
export default defineConfig({
testDir: './tests', // Location of test files
fullyParallel: true, // Run tests within files in parallel
forbidOnly: !!process.env.CI,// Fail CI if test.only is left in code
retries: process.env.CI ? 2 : 0, // 2 retries on CI, 0 locally
workers: process.env.CI ? 1 : undefined, // Serial on CI, parallel locally
reporter: [['html'], ['list']], // HTML report + live list output
use: {
baseURL: 'http://127.0.0.1:5173', // All page.goto('/path') resolve here
trace: 'on-first-retry', // Capture trace on first test failure
screenshot: 'only-on-failure', // Screenshot captured on failure
video: 'on-first-retry', // Video recorded on first retry
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
// Firefox and Safari are commented out โ uncomment to enable
],
// Auto-start backend and frontend before tests
webServer: [
{
command: 'npm run start --prefix server',
url: 'http://127.0.0.1:5005/api/health', // Waits for health check
reuseExistingServer: !isCI, // Reuse if already running locally
env: { NODE_ENV: 'test', PORT: '5005', MONGODB_URI: '...', JWT_SECRET: '...' }
},
{
command: 'npm run dev --prefix client -- --host 127.0.0.1 --port 5173',
url: 'http://127.0.0.1:5173',
reuseExistingServer: !isCI,
}
],
});
Reports and artifacts location after a test run:
| Path | Contents |
|---|---|
playwright-report/ | HTML report (open with npx playwright show-report) |
test-results/ | Trace zips, screenshots, and videos for failed tests |
Both directories are in .gitignore and are not committed.
All routes are prefixed with /api.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /health | None | Returns { status: "ok" } โ used by Playwright webServer readiness check |
| Method | Endpoint | Auth | Body | Description |
|---|---|---|---|---|
POST | /auth/register | None | { name, email, password } | Creates a new user. Password must be โฅ 8 characters. Returns 201 on success. |
POST | /auth/login | None | { email, password } | Validates credentials and returns { token, user }. Token is a 1-hour JWT. |
| Method | Endpoint | Auth | Body | Description |
|---|---|---|---|---|
GET | /hotels | None | โ | Returns array of all hotel documents |
POST | /hotels/book | Bearer JWT | { hotelId, date } | Books a hotel for the authenticated user. Appends booking to user.bookings[]. |
| Method | Endpoint | Auth | Body | Description |
|---|---|---|---|---|
GET | /vehicles | None | โ | Returns array of all vehicle documents |
POST | /vehicles/book | Bearer JWT | { vehicleId, date } | Rents a vehicle for the authenticated user. Appends booking to user.bookings[]. |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /user/profile | Bearer JWT | Returns the authenticated user's profile and bookings array (password excluded) |
Available only when NODE_ENV=test. This route is not registered in production.
| Method | Endpoint | Auth | Body | Description |
|---|---|---|---|---|
DELETE | /test/cleanup-user | None | { email } | Deletes a user by email. Used by Playwright deleteTestUser helper for test isolation. |
{
name: String, // required
email: String, // required, unique, stored lowercase
password: String, // required, bcrypt hash (never returned in API responses)
role: String, // default: 'user'
bookings: [ // embedded array โ no separate bookings collection
{
hotel: String, // hotel name at time of booking
vehicle: String, // vehicle name at time of booking
date: Date
}
],
createdAt: Date, // auto (timestamps: true)
updatedAt: Date // auto (timestamps: true)
}
{
name: String, // required
location: String, // required
price: Number, // required (USD per night)
image: String, // Unsplash URL
rating: Number, // 0-5
description: String
}
{
name: String, // required (e.g. "Toyota Camry")
type: String, // required (e.g. "Car", "SUV", "Bike")
price_per_day: Number, // required (USD)
image: String, // Unsplash URL
available: Boolean // default: true
}
The server seeds the database on every startup if collections are empty (server/seed.js). This ensures the app always has data to display and tests can reliably target specific records by name.
| Name | Location | Price/Night | Rating |
|---|---|---|---|
| Grand Luxury Resort | Sri Lanka | $250 | 4.8 |
| Mountain View Inn | Sri Lanka | $120 | 4.2 |
| Urban City Hotel | Sri Lanka | $90 | 4.0 |
| Name | Type | Price/Day |
|---|---|---|
| Toyota Camry | Car | $50 |
| Honda CR-V | SUV | $80 |
| Vespa Scooter | Bike | $25 |
Seeded when NODE_ENV !== 'production' and SEED_DEMO_USER !== 'false':
| Field | Value |
|---|---|
| Name | John Doe |
john@example.com | |
| Password | password123 |
Set
SEED_DEMO_USER=falseinserver/.envif you don't want this user created.
The GitHub Actions workflow at .github/workflows/ci.yml runs on every push to main/master and on all pull requests.
1. Checkout repository
2. Setup Node.js 20 (with npm cache for root, client, and server lock files)
3. Start MongoDB 7 service container on port 27017 (with healthcheck)
4. npm ci --legacy-peer-deps (root)
5. npm ci --legacy-peer-deps (client)
6. npm ci --legacy-peer-deps (server)
7. npm run lint (client)
8. npm run lint (server)
9. npm run build (client) โ validates TypeScript compilation
10. npx playwright install --with-deps chromium
11. npx playwright test (full suite, Chromium only, 1 worker)
12. Upload playwright-report/ as artifact (retained 30 days)
CI: true
NODE_ENV: test
MONGODB_URI: mongodb://127.0.0.1:27017/smart-travel-test
JWT_SECRET: smart-travel-ci-test-secret
PORT: 5005
API_BASE_URL: http://127.0.0.1:5005/api
VITE_API_BASE_URL: http://127.0.0.1:5005/api
PLAYWRIGHT_BASE_URL: http://127.0.0.1:5173
When CI=true is set, Playwright:
retries: 2 (two retry attempts on failure)workers: 1 (serial execution to avoid race conditions)forbidOnly: true (fails the build if test.only is accidentally committed)reuseExistingServer: false)The HTML test report is always uploaded as a GitHub Actions artifact under the name playwright-report, making it easy to inspect failures from any CI run.
| Feature | Status |
|---|---|
| Social login (GitHub / Google) | Visual placeholder only โ OAuth is not implemented |
| AI concierge chat | Returns a hardcoded static response โ not connected to any AI backend |
| Payment processing | Simulated โ booking confirmation does not involve real payments |
| Firefox / Safari testing | Commented out in playwright.config.ts โ only Chromium runs by default |
| Vehicle availability | The available field is stored on Vehicle but not enforced during booking |
| Booking cancellation | Not implemented โ bookings can be viewed but not cancelled |
react typescript vite tailwindcss framer-motion express mongodb mongoose
jwt bcrypt playwright e2e-testing api-mocking network-interception
custom-fixtures test-isolation github-actions ci-cd full-stack travel-booking
The case-study preview is collapsed. Activate the button to make the full content available.
Aug 2025 โ Oct 2025
A marketplace for verified Sri Lankan homestays, supporting guest bookings, host onboarding, admin approval, and test-mode payments.
Oct 2025 โ Nov 2025
A full-stack developer Q&A platform where programmers can ask, answer, vote, save questions, explore tags, and generate AI-assisted responses.
| 0.395 |
| Icon library |
| Dev server auto-restart |
Smart-Travel_Playwright_PlayGround/
โ
โโโ .env.example # Root-level env vars (Playwright + test server)
โโโ .gitignore
โโโ package.json # Root scripts: install:all, dev, test
โโโ package-lock.json
โโโ playwright.config.ts # Playwright configuration (browsers, web servers, reporters)
โ
โโโ .github/
โ โโโ workflows/
โ โโโ ci.yml # GitHub Actions CI pipeline
โ
โโโ client/ # React frontend (Vite)
โ โโโ .env.example # VITE_API_BASE_URL
โ โโโ index.html
โ โโโ vite.config.ts
โ โโโ tailwind.config.js
โ โโโ tsconfig.app.json
โ โโโ package.json
โ โโโ src/
โ โโโ App.tsx # Root component with Router, Toaster, global layout
โ โโโ main.tsx # React DOM entry point
โ โโโ index.css # Global styles and Tailwind directives
โ โโโ App.css
โ โโโ api/
โ โ โโโ api.ts # Axios instance with base URL + JWT interceptor
โ โโโ assets/
โ โโโ components/
โ โ โโโ Navbar.tsx # Fixed navbar: logo, nav links, auth actions, dark mode
โ โ โโโ ChatAssistant.tsx # Floating AI concierge widget
โ โโโ context/
โ โ โโโ AuthContext.ts # Auth context definition + useAuth hook
โ โ โโโ AuthProvider.tsx # Auth state: user, login(), logout(), loading
โ โ โโโ ThemeContext.ts # Theme context definition + useTheme hook
โ โ โโโ ThemeProvider.tsx # Dark/light mode state management
โ โโโ pages/
โ โ โโโ Home.tsx # Landing page with hero, search, featured hotels
โ โ โโโ Hotels.tsx # Hotel listing + booking modal
โ โ โโโ Vehicles.tsx # Vehicle listing + rental modal
โ โ โโโ AuthPage.tsx # Combined login/register page
โ โ โโโ Dashboard.tsx # Protected user portal + booking history
โ โโโ types/
โ โโโ index.ts # TypeScript interfaces: User, Hotel, Vehicle, Booking, Profile
โ
โโโ server/ # Express backend
โ โโโ .env.example # Server env vars
โ โโโ index.js # Server bootstrap: env check, DB connect, seed, listen
โ โโโ app.js # Express app: middleware, CORS, route registration
โ โโโ seed.js # Initial data seeding (hotels, vehicles, demo user)
โ โโโ package.json
โ โโโ config/
โ โ โโโ db.js # Mongoose connection
โ โโโ middleware/
โ โ โโโ auth.js # JWT verification middleware (authenticateToken)
โ โ โโโ validate.js # Request body validation rules
โ โ โโโ errorHandler.js # Centralized error handler
โ โโโ models/
โ โ โโโ User.js # User schema: name, email, password, role, bookings[]
โ โ โโโ Hotel.js # Hotel schema: name, location, price, image, rating
โ โ โโโ Vehicle.js # Vehicle schema: name, type, price_per_day, image, available
โ โโโ routes/
โ โ โโโ auth.routes.js # POST /api/auth/register, POST /api/auth/login
โ โ โโโ booking.routes.js # GET/POST /api/hotels, GET/POST /api/vehicles
โ โ โโโ user.routes.js # GET /api/user/profile
โ โ โโโ test.routes.js # DELETE /api/test/cleanup-user (test env only)
โ โโโ utils/
โ โโโ sanitizeUser.js # Strip password before returning user objects
โ
โโโ tests/ # Playwright test suite
โโโ fixtures.ts # Custom fixtures: testUser, loggedInPage
โโโ test-data.ts # Helpers: makeTestUser, createTestUser, deleteTestUser
โโโ login.test.ts # Auth tests: register, login, invalid credentials
โโโ booking.test.ts # Booking tests: hotels, vehicles, dashboard verification
โโโ api-mock.test.ts # API mocking: intercept routes, simulate errors
โโโ ui-validation.test.ts # UI tests: home page, navigation, mobile responsiveness