# Training Module — Architecture Map

> **Last updated:** 2026-06-10
> **Status:** Living document — incrementally updated as new information is discovered

---

## 1. Purpose & Responsibilities

The Training module is a **focused, single-purpose module** that handles **session feedback** — allowing players to rate training sessions and providing coaches/admins with feedback reports.

It does **NOT** manage:
- Training sessions themselves (Sessions module owns those)
- Training groups (TrainingGroups module)
- Coaches (Coaches module)
- Session scheduling or attendance (Sessions module)

**Note:** There is also a `Sessions` module that contains its own `SessionFeedbackController` and `SessionFeedbackService` — these may be a parallel/duplicate implementation. The Training module operates on the same `session_feedback` table and `sessions` table as the Sessions module's feedback features.

---

## 2. Directory & File Structure

```
app/Modules/Training/
├── bootstrap.php                              # Permission registration only (no menu)
├── Routes.php                                 # 4 routes for session feedback
├── Controllers/
│   └── SessionFeedbackController.php         # Feedback submission & reporting
├── Models/
│   └── SessionFeedback.php                   # Model for session_feedback table
├── Services/
│   └── SessionFeedbackService.php            # Submit feedback, generate reports
└── Views/
    ├── feedback_form.php                     # Player feedback submission form
    └── feedback_report.php                   # Feedback report (per-session or per-coach)
```

---

## 3. Database Schema (Production — Source of Truth)

### 3.1 `session_feedback` Table

| Column | Type | Nullable | Key | Default | Notes |
|--------|------|----------|-----|---------|-------|
| id | int unsigned | NO | PRI | auto_increment | |
| session_id | int unsigned | NO | MUL | | FK to sessions |
| player_id | int unsigned | NO | MUL | | FK to players/members |
| rating | tinyint unsigned | NO | | 3 | 1-5 scale |
| energy_level | enum('low','medium','high') | NO | | medium | Player energy after session |
| enjoyment_level | tinyint unsigned | NO | | 3 | 1-5 scale |
| difficulty_level | tinyint unsigned | NO | | 3 | 1-5 scale |
| comments_ar | text | YES | | NULL | Free-text Arabic comments |
| is_anonymous | tinyint(1) | NO | | 0 | Hide player name in reports |
| created_at | datetime | NO | | CURRENT_TIMESTAMP | |

**Unique constraint:** (session_id, player_id) — enforced at application level via `hasPlayerRated()`.

### 3.2 Related Tables (owned by other modules)

| Table | Owner Module | Relationship |
|-------|--------------|--------------|
| sessions | Sessions | session_id FK — provides session context (date, coach_id, group_id) |
| coaches | Coaches | Referenced via sessions.coach_id for coach summary |
| players/members | Players/Members | player_id FK — the person providing feedback |

---

## 4. Routes

| Method | Path | Handler | Permission |
|--------|------|---------|------------|
| GET | `/session-feedback/{sessionId}/create` | SessionFeedbackController@create | training.submit_feedback |
| POST | `/session-feedback/{sessionId}` | SessionFeedbackController@store | training.submit_feedback |
| GET | `/session-feedback/{sessionId}/report` | SessionFeedbackController@report | training.view_feedback |
| GET | `/session-feedback/coach/{coachId}` | SessionFeedbackController@coachSummary | training.view_feedback |

---

## 5. Events Dispatched

| Event | Payload | Dispatched From |
|-------|---------|-----------------|
| `session.feedback_submitted` | `{feedback_id, session_id, player_id, rating}` | SessionFeedbackService::submitFeedback |

---

## 6. Events Consumed (by other modules)

| Event | Listener Module | Action |
|-------|----------------|--------|
| `session.feedback_submitted` | Notifications | Triggers notification via NotificationTriggerService |

---

## 7. Cross-Module Dependencies

### 7.1 Training module IMPORTS FROM (via direct DB queries):

| Module/Table | What it uses |
|--------------|-------------|
| Sessions | `sessions` table (session data, coach_id, session_date, group_id) |
| Coaches | `coaches` table (coach name for summary view) |
| Members | `members` table (player name for non-anonymous feedback display, uses `name_ar` column) |

### 7.2 Other modules that reference Training data:

| Module | What it references |
|--------|-------------------|
| Sessions | Has its own parallel SessionFeedbackController that also operates on `session_feedback` table |
| Notifications | Listens to `session.feedback_submitted` event |
| Disciplines (SportsDashboard) | References training session data in dashboards |

---

## 8. Permissions

| Key | Description |
|-----|-------------|
| training.submit_feedback | Submit a rating/feedback for a training session |
| training.view_feedback | View feedback reports (per-session and per-coach) |

**Note:** No sidebar menu is registered — this module is accessed via links from the Sessions module's session detail pages.

---

## 9. High-Risk Areas

### 9.1 Duplicate Implementation with Sessions Module
- Both `Training/Services/SessionFeedbackService.php` and `Sessions/Services/SessionFeedbackService.php` exist
- Both `Training/Controllers/SessionFeedbackController.php` and `Sessions/Controllers/SessionFeedbackController.php` exist
- It is unclear which is the "canonical" implementation. The Training module routes (`/session-feedback/...`) are distinct from Sessions module routes
- Risk: changes to one may not be reflected in the other

### 9.2 No Foreign Key Enforcement at DB Level
- The `session_id` and `player_id` columns have indexes but the application-level check (querying `sessions` and checking player existence) is the only guard
- If a session is deleted, orphaned feedback records would remain

### 9.3 Player Identity Resolution
- Uses `$currentUser['member_id'] ?? $currentUser['id']` which conflates member IDs with generic user IDs
- The model's `getForSession()` joins to `members.name_ar` but the `player_id` may refer to a `players` table entry rather than `members`

---

## 10. Known Patterns & Gotchas

1. **One rating per player per session**: Enforced at application level by `hasPlayerRated()` check before insertion
2. **Anonymous mode**: When `is_anonymous = 1`, the LEFT JOIN to members returns NULL for `player_name` — names are hidden in reports
3. **Coach summary uses date range**: The coach report aggregates all sessions within a date range, defaulting to the current month
4. **Feedback trend by group**: `getFeedbackTrend()` method exists for group-level trend analysis (joins sessions via `group_id`) but no route exposes it directly
5. **No menu entry**: Module is only accessible via deep links from session pages — not visible in sidebar navigation
