
# IGF-EMS Project Context Document

**Purpose:** System design and planning reference. Use this when discussing architecture, DB design, or features with external tools (e.g. ChatGPT).

**Last updated:** From codebase snapshot (backend `dashboard/`, frontend `src/`, middleware).

---

## 1. High-Level Overview

- **Project:** IGF Event Management System (IGF-EMS) — event/competition and participant management for International Golf Federation (IGF).
- **Stack:** React (CRA) frontend, Express (Node) backend, MongoDB (Mongoose). JWT auth. Optional: Formio for forms, Bootstrap/React-Bootstrap, axios.
- **Entry points:** Frontend `src/App.js`; Backend `dashboard/server.js`.

---

## 2. Architecture

### 2.1 Frontend (React)

- **Router:** React Router v6. Single app with three main areas:
  - **Admin / CRM** — path `/` (and nested). Layout: `DashboardLayout`. For staff managing persons, organisations, committees, delivery partners, roles.
  - **EMS (Event Management)** — path `/ems/*`. Layout: `EmsDashboardLayout`. For competitions, participants, forms, hotels, rooms, bookings, templates, changelog, image approval, etc.
  - **User (participant-facing)** — path `/user/*`. Layout: `UserDashboard`. For logged-in persons (e.g. forms, progress, introduction).
- **Auth:** Login state in React state + `localStorage` (`token`, `username`, `role`, `personId`). Protected routes redirect unauthenticated users to `/admin-login` or `/user-login`. Inactivity logout (e.g. 15 min) can be configured.
- **API calls:** Shared axios instance in `src/api/axiosInstance.js` with `baseURL: process.env.REACT_APP_API_URL` and Bearer token from `localStorage`. Some legacy code uses raw `axios` and `REACT_APP_API_URL` directly (e.g. parts of EMS hotel/room UI).
- **Key frontend paths:**
  - `src/components/crm/` — CRM (PersonList, OrganisationList, AddEditPersonForm, etc.).
  - `src/components/ems/` — EMS (CompetitionList, ParticipantsList, HotelList, RoomList, RoomsInformation/Bookings, FormsList, FormBuilderView, TemplateList, AssignRooms, ImageApproval, ChangeLog, etc.).
  - `src/components/` — Login, UserLogin, Register, UserDashboard, UserFormView, ProgressOverview, etc.
- **Full frontend route table:**

| Path | Component / Purpose |
|------|---------------------|
| `/` | Redirect to admin-login if not logged in; else DashboardLayout (CRM). |
| `/` (index) | PersonList |
| `/add-role`, `/list-roles`, `/individuals`, `/user-list`, `/organizations`, `/committees`, `/delivery-partners`, `/edit-person/:id`, `/add-person`, `/organisation/:id`, `/organisation` | AddRole, ListRoles, PersonList, UserList, OrganisationList, CommitteeList, DeliveryPartnerList, AddEditPersonForm, AddEditOrganizationForm |
| `/admin-login`, `/user-login`, `/register` | Login, UserLogin, Register |
| `/ems` | EmsDashboardLayout; redirect to /ems/home-page |
| `/ems/home-page`, `/ems/competitions`, `/ems/participants`, `/ems/templates`, `/ems/hotels`, `/ems/rooms`, `/ems/forms`, `/ems/changelog`, `/ems/groups`, `/ems/bookings`, `/ems/image-approval` | EmsHome, CompetitionList, ParticipantsList, TemplateList, HotelList, RoomList, FormsList, ChangeLog, GroupList, RoomsInformation, ImageApproval |
| `/ems/competitions/add-competition`, `/ems/competitions/:id`, `/ems/groups/add-group`, `/ems/groups/:id/edit`, `/ems/participants/add-participant`, `/ems/participants/edit-participant/:id`, `/ems/persons/add-person`, `/ems/templates/add-template`, `/ems/templates/:id/edit`, `/ems/hotels/add-hotel`, `/ems/hotels/:id`, `/ems/rooms/add-room`, `/ems/rooms/:id`, `/ems/assign-rooms` | AddCompetitionForm, EditCompetitionForm, AddGroupForm, AddEditGroupForm, AddParticipantForm, ParticipantEdit, AddPersonForm, AddTemplateForm, EditTemplate, AddHotelForm, EditHotelForm, AddRoom, EditRoom, AssignRooms |
| `/ems/search`, `/ems/send-emails`, `/ems/communication-history`, `/ems/forms/add`, `/ems/forms/:id`, `/ems/forms/edit/:id`, `/ems/submissions/:id`, `/ems/forms/view/:id` | SearchTable, SendEmails, EmailHistory, FormBuilderView, FormRenderer, EditFormBuilder, FormSubmissions, FormViewer |
| `/user` | UserDashboard (requires user role) |
| `/user/dashboard`, `/user/forms/:id`, `/user/form/view/:pathSegment/:id`, `/user/home`, `/user/introduction` | ProgressOverview, FormRenderer, UserFormView, UserHome, Introduction |
| `*` | Navigate to /admin-login |

### 2.2 Backend (Express)

- **Server:** `dashboard/server.js`. Express app, CORS, body-parser (50mb limit), `trimRequestParams` middleware, multer for uploads. MongoDB via Mongoose (`process.env.MONGO_URI`). Env: `.env` (dev) or `.env.production` (prod) via `NODE_ENV`.
- **Static/uploads:** `/api/ems/uploads`, `/uploads`, `/uploads/templates`, `/documents`, `/api/documents`, `/images` served from `dashboard/uploads` and `dashboard/public`. React build served from `../build`; catch-all sends `index.html` for SPA (excluding static assets).
- **Security:** Global safe URL decoder first; no `authMiddleware` applied to most API routes (auth is frontend-only for now). Path params should be validated (e.g. reject `..`).
- **File uploads:** Multer in `server.js`; default storage under `dashboard/uploads` (unique filename: timestamp-random-originalname). POST `/api/ems/uploads` accepts single file; returns JSON with URL (baseUrl currently hardcoded as `https://erp.igfdigital.com`). DELETE `/api/ems/uploads/:filename` with `..` rejected. Static serve at `/api/ems/uploads` and `/uploads`. Global-hotels photos use subdir `uploads/global-hotels` (in GlobalHotelRoutes).

### 2.3 API Route Mount Points

| Mount path | Purpose |
|------------|--------|
| `/api/auth` | AuthRoutes — admin login, user login, register, refresh (JWT). |
| `/api/roles` | RoleRoutes |
| `/api` | OrganizationRoutes (organisations) |
| `/api/countries` | CountryRoutes |
| `/api/org-types` | OrgTypeRoutes |
| `/api/admins` | AdminRoutes (CRUD admins, soft delete) |
| `/api/feedback` | FeedbackRoutes |
| `/api/persons` | PersonRoutes |
| `/api/personRoles` | PersonRoleRoutes |
| `/api/personAssistantRelations` | PersonAssistantRelationRoutes |
| `/api/relationship-types` | RelationshipTypeRoutes |
| `/api/panel-progress` | PanelProgressRoutes |
| `/api/ems` | SendEmailRoutes (and overlapping EMS prefix) |
| `/api/ems/competitions` | CompetitionRoutes |
| `/api/ems/groups` | GroupRoutes |
| `/api/ems/participants` | ParticipantRoutes |
| `/api/ems/templates` | TemplateRoutes |
| `/api/ems/forms` | FormRoutes |
| `/api/ems/communication-history` | HistoryRoutes |
| `/api/ems/changelogs` | ChangelogRoutes |
| `/api/ems/hotels` | HotelRoutes (competition–hotel link) |
| `/api/ems/rooms` | RoomRoutes |
| `/api/ems/assign-rooms` | AssignRoomRoutes |
| `/api/ems/global-hotels` | GlobalHotelRoutes |

---

## 3. Authentication

- **Admin login:** POST `/api/auth/admin-login`. Body: `email`, `password`. Backend: Admin model, MD5 password comparison, JWT access + refresh; returns `token`, `accessToken`, `refreshToken`, `username`, `role: 'admin'`, `personId`, `email`, `msg`. No auth middleware on this route.
- **User (person) login:** POST `/api/auth/user-login`. Person model, bcrypt compare; returns token and user info. Redirect after login: admin → `/`, user → `/user/dashboard`.
- **Tokens:** JWT signed with `process.env.JWT_SECRET` (and refresh with `JWT_REFRESH_SECRET`). Stored in `localStorage` as `token`; optional `refreshToken`. Do not hardcode fallback secrets in code.
- **Middleware:** `middleware/authMiddleware.js` — verifies Bearer token, sets `req.user` (decoded payload). Not currently applied to EMS or most API routes; can be added per-route for protected APIs.

### 3.1 Auth flow (step-by-step)

1. User hits `/admin-login` or `/user-login`; if `localStorage.token` exists, redirect to `/` or `/user/dashboard`.
2. User submits email + password (admin: plus reCAPTCHA on frontend; backend does not validate captcha).
3. Frontend POSTs to `/api/auth/admin-login` or `/api/auth/user-login`; receives JSON with `token`, `username`, `role`, and optionally `personId`, `refreshToken`.
4. Frontend stores in `localStorage`: `token`, `username`, `role`, `personId`. Login component currently does not call App’s `handleLogin` — it does its own storage and `navigate("/")`, so App state may lag until refresh or next mount.
5. Protected routes: if no token, redirect to `/admin-login`. Else render layout (CRM or EMS or User). Admin can access both `/` and `/ems/*`; role check for `/user/*` is implicit (user login redirects to `/user/dashboard`).

### 3.2 Key API request/response shapes

- **POST /api/auth/admin-login**  
  Request: `{ email, password }` (frontend may send `captcha`; backend ignores it).  
  Success 200: `{ username, role: 'admin', token, accessToken, refreshToken, personId, email, msg }`.  
  Error 400: `{ msg: 'Admin not found' | 'Invalid credentials' }`.

- **POST /api/ems/hotels**  
  Request: `{ global_hotel_id, competition_id, competition_specific_contact_person?, event_specific_contact_email?, event_specific_contact_phone?, event_specific_notes? }`.  
  Success 201: populated Hotel doc. Note: Hotel schema uses `competition_specific_*`; route body uses `event_specific_*` for email/phone/notes — values may not persist unless aligned.

- **POST /api/ems/assign-rooms**  
  Request: `{ room_id, hotel_id, participant_ids: ObjectId[] }`.  
  Success 201: `{ success: true, data: assignment }`. Assignment has `number_of_rooms` set to `participant_ids.length`, `assigned_by: null`.

- **GET /api/ems/assign-rooms**  
  Response: array of AssignRoom docs populated with `hotel_id.global_hotel_id.global_hotel_name`, `room_id` (room_type, price).  
  **GET /api/ems/assign-rooms/participant/:id** — single assignment for that participant (person ID).

---

## 4. Database (MongoDB / Mongoose)

**Connection:** Single MongoDB; `process.env.MONGO_URI`. All models in `dashboard/models/`.

### 4.1 Core Entities and Relationships

- **Admin** — Staff accounts. Fields: `username`, `email`, `password`, `role_name`, `refreshToken`, `deletedAt`, timestamps. Used for admin login and as `assigned_by` in AssignRoom.
- **Person** — Individuals (athletes, officials, etc.). Rich profile: name, lname, email, password, country_id, address, phones, social, image, status, etc. Refs: Country. Collection: `people`. Used as participants and in PersonRole.
- **Organisation** — Organizations. Refs: OrgType, Country. Used in PersonRole (org_id).
- **Role** — Role definition (role_name, role_type). Linked to Person via PersonRole.
- **PersonRole** — Links Person, Role, Organisation (person_id, role_id, org_id), with start/end dates and main_role.
- **Country** — name, code, continent. Referenced by Person, Organisation.
- **OrgType** — org_type. Referenced by Organisation.

### 4.2 Event / EMS

- **Competition** — Event. Fields: name, venue, start_date, end_date, status, location, code, logo, timezone, odf_*, timestamps. One competition can be “active” (status: true) used for default selection in EMS.
- **Group** — Group name (e.g. for grouping participants). Referenced by Participant (group_id).
- **Participant** — Links Person to Competition(s) and Group. Fields: person_id (ref Person), competition_id (array ref Competition), group_id (ref Group), person_data (object), registration_status ('Added'|'Pending'|'Registered'|'Rejected'), approval_status ('Pending'|'Approved'|'Rejected'), is_deleted, progress_breakdown (Map). Timestamps.
- **Form** — Form definition. Fields: form_name, schema (object). Used for dynamic forms (Formio-style).
- **Template** — Email template. name, body, from_name, from_email, subject, competition_id (ref Competition), attachments. Used for communication.
- **Changelog** — Audit trail. Refs: edited_person_id (Person), edited_org_id (Organisation), edited_template_id (Template), edited_competition_id (Competition), edited_by_admin (Admin). Fields: edited_by, field, prev_value, new_value, deletedAt, timestamps.

### 4.3 Hotels and Rooms (Accommodation / Bookings)

- **GlobalHotels** — Master hotel record (shared across competitions). Collection: `globalhotels`. Fields: global_hotel_id (UUID string), global_hotel_name, global_address, photo_url, hotel_images[], contact_*, website_url, description, timestamps.
- **Hotel** — Links a GlobalHotel to a Competition (one record per global hotel per competition). Refs: global_hotel_id (GlobalHotels), competition_id (Competition). Fields: competition_specific_contact_*, competition_specific_notes (schema uses competition_specific_*; some routes use event_specific_* in body — align for consistency).
- **Room** — Room type/inventory. Refs: hotel_id (array of Hotel), competition_id (array of Competition). Fields: room_name, room_type, price, no_of_rooms, from_date, to_date, timestamps.
- **AssignRoom** — Assignment of a room in a hotel to participants. Refs: hotel_id (Hotel), room_id (Room), participant_ids (array Person), assigned_by (Admin). Fields: number_of_rooms, assigned_at, timestamps.

**Flow:** GlobalHotels are created once. Hotel links a GlobalHotel to a Competition. Room belongs to one or more Hotels and Competitions. AssignRoom assigns a Room (in a Hotel) to one or more Persons (participants).

**Denormalized references:** AssignRoom stores both `hotel_id` and `room_id` at creation time. These values are **not** updated if Room’s `hotel_id` (or Hotel data) changes later. Treat them as a snapshot of the assignment. (There is no RoomUnit model; if one is added later, any `hotel_id` on it should be set when the unit is created and not synced when Room.hotel_id changes.)

### 4.3.1 Hotel/Room API endpoint summary (for system design)

| Method | Path | Purpose |
|--------|------|---------|
| GET | /api/ems/global-hotels | List all global hotels. |
| POST | /api/ems/global-hotels | Create global hotel (multipart: photo_uploads); optionally creates Hotel for active competition. |
| PUT | /api/ems/global-hotels/:id | Update global hotel (multipart optional). No DELETE. |
| GET | /api/ems/hotels | List all Hotels (populate global_hotel_id, competition_id). |
| GET | /api/ems/hotels/competition/:competitionId | Hotels for one competition. |
| POST | /api/ems/hotels | Create Hotel (link global_hotel_id + competition_id). |
| PUT | /api/ems/hotels/:id | Update Hotel (competition-specific fields). |
| PUT | /api/ems/hotels/add-to-active-competition/:id | Set hotel’s competition_id to active competition. |
| DELETE | /api/ems/hotels/:id | Delete Hotel. |
| GET/POST/GET/:id/PUT/DELETE | /api/ems/rooms | Full CRUD; Room has hotel_id[], competition_id[]; create auto-assigns active competition. |
| GET | /api/ems/assign-rooms | List assignments (populate hotel, room). |
| GET | /api/ems/assign-rooms/participant/:id | Assignment for one participant. |
| POST | /api/ems/assign-rooms | Create assignment (room_id, hotel_id, participant_ids[]). |

### 4.4 Other Models (full list with schema summary)

| Model | Collection / refs | Key fields |
|-------|-------------------|------------|
| **User** | (if used) | Check codebase for usage. |
| **Ticket** | refs: Hotel, Guest | hotel_id[], guest_id[], competition_name, competition_date, seat_number, price, booking_date, start_date, end_date, status. |
| **Guest** | — | full_name (unique), phone, email, id_proof_type, id_proof_number, check_in_date. |
| **Document** | refs: Guest | guest_id[], document_type, file_path, uploaded_at. |
| **Report** | refs: Hotel | hotel_id[], report_type, generated_at, content. |
| **Feedback** | refs: Person | user_id (Person), comment, rating (1–5). |
| **CommunicationHistory** | — | name, email, subject, body, attachments[], sent_at. Used for email history. |
| **PersonAssistantRelation** | refs: Person (x2), RelationshipType | person_id, assistant_id, rel_id. |
| **RelationshipType** | — | rel_name. |

---

## 5. EMS Bookings / Rooms Information (Current Focus)

- **Route:** `/ems/bookings` (e.g. `http://localhost:5001/ems/bookings` when app runs on 5001).
- **Component:** `RoomsInformation.js`. Renders “Rooms information” title, “Select Hotel” dropdown, date navigation arrows, and a grid: rooms (01–07) as rows, dates as columns, cells either “available” (price only) or “booked” (standard / athlete / private with name, team, price, guests, status, beds). Styling: rounded cell corners, EMS-aligned layout, Poppins font.
- **Data:** Currently **mock/placeholder** (MOCK_HOTELS, MOCK_ROOMS, buildMockBookings). No backend wiring yet. Intended to be driven later by: GlobalHotels/Hotel for hotel list, Room for room inventory, AssignRoom (+ Participant/Person) for bookings, and optionally Competition for active event and date ranges.
- **Sidebar:** EMS has a “Bookings” nav item (icon faCalendarCheck) after “Hotels”, linking to `/ems/bookings`.

---

## 6. Environment and Configuration

- **Backend:** `.env` or `.env.production` (by `NODE_ENV`). Do not overwrite `.env` without user confirmation.
- **Frontend:** Only `REACT_APP_*` vars; no hardcoded production URLs.
- **Port:** Backend default 5000; frontend dev often 3000; production may serve React from same server (e.g. 5001).

### 6.1 Environment variables (inferred from codebase)

| Variable | Where | Purpose |
|----------|--------|---------|
| `NODE_ENV` | Backend | `production` → use `.env.production`; else `.env`. |
| `MONGO_URI` | Backend | MongoDB connection string. |
| `PORT` | Backend | Server port (default 5000). |
| `JWT_SECRET` | Backend | Sign access tokens; required. |
| `JWT_REFRESH_SECRET` | Backend | Sign refresh tokens. |
| `JWT_EXPIRES_IN` | Backend | Access token TTL (e.g. `7d`). |
| `JWT_REFRESH_EXPIRES_IN` | Backend | Refresh token TTL (e.g. `7d`). |
| `REACT_APP_API_URL` | Frontend | API base URL (e.g. `http://localhost:5000/api` or `/api` when same origin). |
| `REACT_APP_RECAPTCHA_SITE_KEY` | Frontend | reCAPTCHA v2 site key for admin login. |

---

## 7. Conventions (from project rules)

- Prefer simple solutions; avoid duplication; consider dev/test/prod.
- API: `res.status(code).json({ message: "..." })`; use `message` for user-facing text (auth may use `msg`). 400 validation, 401 auth, 403 forbidden, 404 not found, 500 server error; 201 for creation where appropriate.
- Validate and trim input; reject path params containing `..`. Try/catch in route handlers with proper status and JSON.
- Use Mongoose models from `dashboard/models/`; prefer `.lean()` for read-only; `runValidators: true`, `new: true` on updates. Exclude sensitive fields by default (e.g. password) and `.select('+password')` when needed.
- Keep files ~200–300 lines; refactor when larger. No one-off scripts in repo; never overwrite `.env` without asking.

---

## 8. Known Gaps and Inconsistencies (for planning)

- **Login → App state:** Login component does not call App’s `handleLogin`; it writes to `localStorage` and navigates. App state (`isLoggedIn`, `username`, `role`, `personId`) is not updated until next load or effect; `personId` may be missing in storage.
- **Hotel schema vs route body:** Hotel model has `competition_specific_contact_email`, `competition_specific_contact_phone`, `competition_specific_notes`. HotelRoutes create/update use `event_specific_contact_email`, `event_specific_contact_phone`, `event_specific_notes` in `req.body` — those values are not written to the schema fields; align names or schema.
- **GlobalHotels DELETE:** Frontend HotelList calls DELETE `/api/ems/global-hotels/:id`; GlobalHotelRoutes has no DELETE handler — request returns 404.
- **Auth on API:** No `authMiddleware` on EMS (or most) API routes; protection is frontend-only. Add auth to sensitive routes when required.
- **Admin register:** AuthRoutes register uses `roleId` and looks up Role by `roleName`; Admin schema has `role_name` (string), not `roleId` — confirm intended behaviour.
- **Person soft delete:** User-login filters by `deletedAt: null`; Person schema in codebase may not define `deletedAt` — add if soft delete is used.
- **EMS API calls:** Some EMS components (e.g. HotelList, RoomList) use raw `axios` and `REACT_APP_API_URL` instead of shared `axiosInstance`; token is not sent if auth is later added to backend.

---

## 9. Quick Reference: Key Files

| Area | Path |
|------|------|
| Backend entry | `dashboard/server.js` |
| Auth routes | `dashboard/Routes/AuthRoutes.js` |
| Hotel/Room/AssignRoom routes | `dashboard/Routes/HotelRoutes.js`, `RoomRoutes.js`, `AssignRoomRoutes.js`, `GlobalHotelRoutes.js` |
| Auth middleware | `middleware/authMiddleware.js` |
| Frontend entry / routes | `src/App.js` |
| EMS layout / nav | `src/components/ems/EmsDashboardLayout.js` |
| Bookings / Rooms grid | `src/components/ems/RoomsInformation.js`, `src/components/css/RoomsInformation.css` |
| Shared API client | `src/api/axiosInstance.js` |

This document is intended for planning and system design discussions; implement details from the actual code when needed.
