# CC Soccer D11 - Session Handoff

**Status:** Roster Builder complete with drag-and-drop UI, roles & permissions implemented, notification system complete

**Last Updated:** December 30, 2024

---

## **Current State - WORKING ✅**

### **Environment:**
- DDEV running Drupal 11
- Database: MySQL
- Git repo: Active, pushed to GitHub
- Site URL: http://ccsoccer-d11.ddev.site/
- Mailhog: http://ccsoccer-d11.ddev.site:8026

### **Core Systems Complete:**
- ✅ All 7 custom entities implemented and working
- ✅ Registration entity (custom, not contrib)
- ✅ Waitlist entity with override system
- ✅ Invitation entity for group/team invitations
- ✅ Commerce integration (products auto-created for seasons/tournaments)
- ✅ Notification system (email via Symfony Mailer, SMS via Clickatell)
  - 11 notification types implemented and tested
  - Override expiration reminders (12-hour window via cron)
  - Magic link invitation flow with auto-checkout
  - Waitlist join confirmations
- ✅ Complete checkout flow with custom panes
- ✅ Group management for seasons
- ✅ Team management for tournaments
- ✅ Order completion subscriber creating registrations
- ✅ **Roles & permissions system with access control**
  - Board Member role (view reports, update game status)
  - Slofriendly role (all Board Member + tournament management)
  - Captain access control (per-team field relationship)
- ✅ **Roster Builder with drag-and-drop UI and auto-save**
  - Team Balancer algorithm for auto-generating balanced rosters
  - Persistent storage via Registration.team field
  - Group visualization and handling
  - Live team statistics

### **Admin Pages Working:**
- `/admin/ccsoccer/season/{season}/roster` - **Roster Builder UI**
- `/admin/ccsoccer/overrides` - **Override management (CRUD interface)**
- `/admin/ccsoccer/seasons` - Season management
- `/admin/ccsoccer/tournaments` - Tournament management
- `/admin/ccsoccer/teams` - Team list
- `/admin/ccsoccer/games` - Game list
- `/admin/ccsoccer/registrations` - Registration list
- `/admin/ccsoccer/game-status` - Game status updates

### **User-Facing Pages Working:**
- `/register` - Main registration page with state-based cards
- `/my-registrations` - User's active registrations
- `/my-group/{registration}` - Group/team management
- `/tournament/{tournament}/team/{team}/manage` - Captain team management (UI TODO)

### **Test Data System:**
```bash
# Seed comprehensive test data (7 seasons + 3 tournaments)
ddev drush ccs-seed --test --force

# Seed with test users (board_test, slofriendly_test, etc)
ddev drush ccs-seed --users

# Enhance test users with gender/age/skill data for roster balancing
ddev drush ccsoccer:enhance-test-data

# Test user credentials:
Username: testuser0 (or board_test, slofriendly_test, etc)
Password: password
```

---

## **Recent Development Sessions**

### **Session 3 - December 30, 2024: Roster Builder Implementation**

**Accomplishment:** Built complete drag-and-drop roster builder with auto-save and team balancing algorithm.

#### **1. Roster Builder Form (`RosterBuilderForm.php`)**

Full-featured drag-and-drop interface:
- **Sticky Workbench panel** - Shows unassigned players, stays visible while scrolling
- **Team columns** - Grid of teams with player cards
- **Live statistics** - Player count, avg skill, avg age per team
- **Group visualization** - Grouped players displayed in connected containers
- **Skill filter** - Filter player visibility by skill level (1-5)
- **Auto-save** - Changes persist immediately without Save button

**Route:** `/admin/ccsoccer/season/{season}/roster`

**Key Architecture Decision:** Store team assignments in `Registration.team` field (not `Team.players`) for clean persistence and auto-save capability.

#### **2. Team Balancer Service (`TeamBalancerService.php`)**

Algorithm for auto-generating balanced rosters:

**Primary Factor:** Skill level balance
- Uses `User.field_skill_level` (admin-assigned, 0-5)
- Falls back to `Registration.self_score` (player self-assessment, 1-5)
- Default: 3 if neither set

**Secondary Factor:** Age distribution
- Calculated from `User.field_dob`
- Default weight: 90% skill / 10% age

**Hard Constraints:**
- Groups stay together (players with same `group_id`)
- Coed leagues: minimum 1 woman per team
- Maximum 1 goalie preference per team
- Even player distribution across teams

**Team Count:**
- Auto-calculated: `ceil(total_players / league.team_size)`
- Admin can override with specific number

#### **3. Drush Commands Added**

```bash
# Preview roster suggestions (dry run)
ddev drush ccsoccer:suggest-rosters 8

# Apply roster suggestions
ddev drush ccsoccer:suggest-rosters 8 --apply

# Clear all assignments to workbench
ddev drush ccsoccer:suggest-rosters 8 --clear

# Override number of teams
ddev drush ccsoccer:suggest-rosters 8 --teams=14

# Adjust balancing weights
ddev drush ccsoccer:suggest-rosters 8 --skill-weight=80 --age-weight=20

# View current roster state
ddev drush ccsoccer:roster-state 8

# Add test data (gender, age, skill) to users
ddev drush ccsoccer:enhance-test-data
```

#### **Files Created:**
- `src/Form/RosterBuilderForm.php` - Main drag-and-drop UI form
- `src/Controller/RosterBuilderController.php` - AJAX endpoints (move, save, suggest)
- `src/Service/TeamBalancerService.php` - Roster balancing algorithm
- `js/roster-builder.js` - Drag-and-drop with auto-save
- `css/roster-builder.css` - UI styling

#### **Files Modified:**
- `ccsoccer.routing.yml` - Added roster builder routes
- `ccsoccer.libraries.yml` - Added roster-builder library
- `ccsoccer.services.yml` - Registered team_balancer service
- `src/Drush/Commands/CcsoccerCommands.php` - Added roster commands
- `src/Entity/Registration.php` - Added self_score field
- `src/EventSubscriber/OrderCompleteSubscriber.php` - Saves self_score on checkout

---

### **Session 2 - December 29, 2024: Roles & Permissions Implementation**

**Accomplishment:** Implemented complete role-based access control system with Board Member, Slofriendly, and Captain access.

#### **1. Created Roles**

**Board Member Role** (`board_member`)
- View reports (teams, games, registrations, credits)
- Send notifications
- Update game status
- View but NOT edit data

**Slofriendly Role** (`slofriendly`)
- All Board Member permissions PLUS:
- Manage tournaments (create, edit, view)
- Edit team names
- Tournament director role

**Files Created:**
- `config/install/user.role.board_member.yml`
- `config/install/user.role.slofriendly.yml`

#### **2. Updated Routing Permissions**

Granular permissions mapped to roles:

| Route | Old Permission | New Permission | Access |
|-------|---------------|----------------|---------|
| `/admin/ccsoccer/seasons/*` | `administer ccsoccer` | `manage seasons` | Admin only |
| `/admin/ccsoccer/tournaments/*` | `administer ccsoccer` | `manage tournaments` | Admin, Slofriendly |
| `/admin/ccsoccer/teams` | `administer ccsoccer` | `view reports` | Admin, Board Member, Slofriendly |
| `/admin/ccsoccer/games` | `administer ccsoccer` | `view reports` | Admin, Board Member, Slofriendly |
| `/admin/ccsoccer/registrations` | `administer ccsoccer` | `manage registrations` | Admin, Board Member |
| `/admin/ccsoccer/game-status` | N/A | `update game status` | Admin, Board Member |

#### **3. Captain Access Control**

**Key Decision:** Captain is NOT a Drupal role - it's a field relationship on Team entity.

**Implementation:**
- Created `TeamCaptainAccessCheck` custom access checker
- Checks if `current_user.uid == team.captain.target_id`
- Admin always has override access
- Season teams cannot be managed by captains (only tournament teams)

**Captain Routes Added:**
- `/tournament/{tournament}/team/{team}/manage` - Manage roster
- `/tournament/{tournament}/team/{team}/invite` - Invite players (POST)
- `/tournament/{tournament}/team/{team}/remove/{user}` - Remove player (POST)
- `/tournament/{tournament}/team/{team}/notifications` - Send notifications

**Files Created:**
- `src/Access/TeamCaptainAccessCheck.php` - Custom access checker
- `src/Controller/TournamentController.php` - Stub methods for captain routes
- `src/Form/TournamentTeamNotificationForm.php` - Stub form

#### **4. Test User Seeding**

Added test users with different roles:
- `player_test` - Regular authenticated user
- `board_test` - Board Member role
- `slofriendly_test` - Slofriendly role
- `captain_test` - Regular user (becomes captain when assigned)

All passwords: `password`

**Files Modified:**
- `src/Drush/Commands/CcsoccerCommands.php` - Added `--users` flag and `seedUsers()` method

---

### **Session 1 - December 28, 2024: Notification System & Override Management**

**Accomplishment:** Completed comprehensive notification system and built override management admin interface.

#### **1. Magic Link Invitation Flow Enhancement**

Updated `RegistrationController::available()` to detect invite tokens and automatically redirect to checkout:
- Season invites → `addSeasonToCart()` → checkout
- Tournament invites → `addTournamentToCart()` → checkout
- Displays appropriate invitation context message
- Bypasses registration listing page entirely

#### **2. Player Joined Notification for Magic Links**

Added `sendPlayerJoined()` notification calls to both season and tournament registration flows in `OrderCompleteSubscriber`:
- Checks if registration was from an invitation (`invited_by` field)
- Loads inviter user
- Sends appropriate notification (season vs tournament context)

#### **3. Waitlist Join Confirmation Notification**

Added `sendWaitlistJoined()` method to `NotificationService`:
- Sends email + SMS confirmation: "You're on the waitlist for [Season]"
- Called from `WaitlistController::joinWaitlistPublic()`

#### **4. Override Expiration Reminder System**

Automated 12-hour warning before override expires:
- Added `sendOverrideExpiring()` method to `NotificationService`
- Implemented `hook_cron()` in `ccsoccer.module`:
  - Checks for overrides expiring in 11-13 hour window
  - Sends email + SMS reminder
  - Marks override as reminded to prevent duplicates
- Added `reminder_sent` field to Override entity
- Database update hook `ccsoccer_update_9006()` to add field

**Technical Decision:** 12-hour warning window with 11-13 hour range catches overrides during hourly cron runs, prevents edge cases where exact timing might miss notifications.

#### **5. Override Management Admin Interface**

Created full CRUD interface at `/admin/ccsoccer/overrides`:
- **List View:** Shows all overrides separated by status (Active, Used, Expired)
  - Auto-expires pending overrides past their date
  - Shows time remaining for active overrides
  - Edit and Delete buttons for each override
- **Create/Edit Form:** 
  - Player selection (autocomplete)
  - Season selection (autocomplete)
  - Date picker for expiration date
  - Time input (HH:MM format, 24-hour)
  - Status dropdown (Pending, Used, Expired, Cancelled)
  - "Reminder already sent" checkbox
- **Delete:** Confirmation and removal

**Files Created:**
- `src/Controller/OverrideController.php` (rebuilt from stub)
- `src/Form/OverrideForm.php`

**Files Modified:**
- `ccsoccer.routing.yml` - Added override CRUD routes
- `src/Entity/Override.php` - Added reminder_sent field, fixed status values

#### **6. All Notification Flows Tested ✓**

1. Override registration - Email + SMS sent
2. Waitlist join - Confirmation email + SMS sent
3. Waitlist spot offered - Email + SMS sent
4. User registers after waitlist offer - Cache auto-refreshes
5. Group invitation sent - Email + SMS to invitee
6. Group invitation accepted (already registered) - Email + SMS to inviter
7. Group invitation accepted (magic link) - Direct to checkout, auto-join group, email + SMS to inviter
8. Group invitation declined - Email + SMS to inviter
9. Invitation reminder/nudge - Email + SMS sent
10. Waitlist join confirmation - Email + SMS sent
11. Override expiration reminder - Email + SMS 12 hours before expiration

---

## **Architecture Decisions**

### **Custom Registration Entity (Not Contrib)**

**Decision:** Built completely custom Registration entity instead of extending contrib Registration module.

**Rationale:**
- Contrib Registration designed for "host entity" pattern (events that people register for)
- Our use case: Direct entity references (player → season, player → tournament)
- Avoided fighting module assumptions about workflows
- Cleaner queries without extra joins
- Full control over complex field requirements (XOR constraints, status transitions)

**Trade-off:** More initial work, but much cleaner long-term maintenance

### **Captain as Entity Relationship (Not Role)**

**Decision:** Captain is a field on Team entity, not a Drupal role.

**Rationale:**
- Captains are per-team, not global
- Same user can be captain of multiple teams
- Same user can be captain of one team but not another
- Simpler to manage (no role assignment/removal needed)
- Custom access checker handles programmatic access control

**Implementation:** `TeamCaptainAccessCheck` verifies `current_user == team.captain`

### **Persistence: Registration.team vs Team.players**

**Decision:** Store team assignments in `Registration.team` field, not `Team.players`.

**Rationale:**
- Registration is the source of truth for player→team relationship
- Enables auto-save without "Save Changes" button
- Survives page refresh
- Each Registration has exactly one team (or NULL for workbench)
- No need to sync two fields

**Implementation:**
- `getRosterState()` reads from Registration.team
- `applyAssignments()` saves to Registration.team
- `clearRosters()` sets Registration.team to NULL
- `move()` endpoint saves individual moves

### **Role Hierarchy**

```
Anonymous
  └─ Authenticated (Player - default)
       └─ Board Member (4 permissions)
            └─ Slofriendly (6 permissions)
                 └─ Administrator (all permissions)

Captain (NOT a role - Team.captain field relationship)
```

---

## **Commands to Remember**

### **Daily Development:**
```bash
# Start DDEV
ddev start

# Clear cache (do often!)
ddev drush cr

# Fresh test data with users
ddev drush ccs-seed --test --users --force

# Enhance test users with gender/age/skill
ddev drush ccsoccer:enhance-test-data

# Check Mailhog for emails
open http://ccsoccer-d11.ddev.site:8026

# Git workflow (ALWAYS pull first - Andrew may have pushed!)
git pull
git status
git add .
git commit -m "message"
git push origin main
```

### **Roster Builder:**
```bash
# Open roster builder UI
open http://ccsoccer-d11.ddev.site/admin/ccsoccer/season/8/roster

# Generate rosters via Drush
ddev drush ccsoccer:suggest-rosters 8 --apply

# View current state
ddev drush ccsoccer:roster-state 8

# Clear all assignments
ddev drush ccsoccer:suggest-rosters 8 --clear --apply
```

### **Override Management:**
```bash
# Open override admin interface
open http://ccsoccer-d11.ddev.site/admin/ccsoccer/overrides

# Run cron manually (for testing expiration reminders)
ddev drush cron
```

### **Role & Permission Testing:**
```bash
# Create test users with roles
ddev drush ccsoccer:seed-users

# Log in as specific user (one-time link)
ddev drush uli --name=board_test
ddev drush uli --name=slofriendly_test
ddev drush uli --name=captain_test

# Check user roles
ddev drush user:information board_test

# Import role config (for collaborators)
ddev drush config:import --partial --source=modules/custom/ccsoccer/config/install -y
```

### **Database Queries:**
```bash
# Check team assignments (Registration.team)
ddev drush sqlq "SELECT r.id, r.player, r.team, t.name as team_name FROM ccsoccer_registration r LEFT JOIN team t ON r.team = t.id WHERE r.season = 8 AND r.team IS NOT NULL"

# Check players in workbench
ddev drush sqlq "SELECT r.id, r.player, u.name FROM ccsoccer_registration r JOIN users_field_data u ON r.player = u.uid WHERE r.season = 8 AND r.team IS NULL"

# Check team stats
ddev drush sqlq "SELECT t.id, t.name, COUNT(r.id) as players FROM team t LEFT JOIN ccsoccer_registration r ON t.id = r.team WHERE t.season = 8 GROUP BY t.id ORDER BY t.name"

# Check roles exist
ddev drush sqlq "SELECT id, label FROM user_role WHERE id IN ('board_member', 'slofriendly')"

# Check test users have correct roles
ddev drush sqlq "SELECT u.uid, u.name, r.roles_target_id FROM users_field_data u LEFT JOIN user__roles r ON u.uid = r.entity_id WHERE u.name IN ('board_test', 'slofriendly_test')"

# Check overrides
ddev drush sqlq "SELECT id, player, season, status, expiration_date, reminder_sent FROM ccsoccer_override ORDER BY created DESC LIMIT 10"
```

### **Debugging:**
```bash
# Watch Drupal logs
ddev drush watchdog:show --type=ccsoccer --count=20

# Check specific registration
ddev drush sqlq "SELECT * FROM ccsoccer_registration WHERE player = 23"

# Check permissions for user
ddev drush sqlq "SELECT roles_target_id FROM user__roles WHERE entity_id = 25"
```

---

## **Test Data Quick Reference**

### **Season 8 (TEST: Coed League):**
- 121 players registered
- 26 teams available
- Team size target: 8 players
- Type: Coed (requires women on each team)

### **Season 9 (TEST: Mens 35+ League):**
- Team size target: 14 players
- Type: Mens35+ (no gender requirement)

### **Test User Credentials:**

| Username | Password | Role | Can Access |
|----------|----------|------|------------|
| `admin` | (existing) | Administrator | Everything |
| `testuser0` | `password` | Player | Register, own data, manage groups |
| `player_test` | `password` | Player | Same as testuser0 |
| `board_test` | `password` | Board Member | View reports, send notifications, update game status |
| `slofriendly_test` | `password` | Slofriendly | Board Member perms + manage tournaments |
| `captain_test` | `password` | Player | Becomes captain when assigned to Team.captain field |

**Login Commands:**
```bash
ddev drush uli --name=board_test
ddev drush uli --name=slofriendly_test
ddev drush uli --name=captain_test
```

---

## **Known Issues / TODO**

### **Captain UI - Not Yet Built**
**Status:** Access control complete, routes exist, controller methods are stubs

**What's Missing:**
- Team roster display with member details
- Invitation form and handler
- Remove player confirmation and logic
- Team notification form functionality

**Files with TODO Comments:**
- `src/Controller/TournamentController.php` - All methods have detailed TODO
- `src/Form/TournamentTeamNotificationForm.php` - Stub form

### **Schedule Builder - Not Yet Started**
Route exists but form is stub:
- `/admin/ccsoccer/season/{season}/schedule`

### **Outstanding Work (Pre-February)**
- Schedule generation (Andrew's responsibility)
- Admin UI polish
- Additional admin reporting/analytics

### **Future Enhancements (Post-February)**
- Admin override creation directly from waitlist is 7-day default; may want customizable expiration
- Waitlist join admin notifications (optional)
- Email template customization interface

---

## **Collaboration Notes**

**Team:**
- Caleb - Lead developer
- Andrew (abmeade@hotmail.com) - Roster building and schedule generation

**Git Workflow:**
- **ALWAYS** `git pull` before `git push` (Andrew may have pushed changes)
- Commit working states frequently
- Clear commit messages

**After Pulling Changes:**
```bash
# Clear cache
ddev drush cr

# If entity fields changed, may need database update
ddev drush updb

# Import any new config
ddev drush config:import --partial --source=modules/custom/ccsoccer/config/install -y
```

**Entity Updates:**
- Drupal 10/11: Use update hooks in `.install` file
- No `drush entity:updates` command (doesn't exist anymore)
- Always clear cache after entity changes

---

## **Important Reminders**

- **Registration.team is source of truth** - not Team.players
- **Auto-save on drag/drop** - no Save button needed in Roster Builder
- **Groups drag as a unit** - can't separate grouped players in UI
- **Captain is NOT a role** - it's a Team.captain field relationship
- **Access checker verifies per-team** - same user can be captain of some teams but not others
- **Board Member can VIEW but not EDIT** - read-only access to admin areas
- **Slofriendly is additive** - has all Board Member permissions PLUS tournament management
- **Test users created with `--users` flag** - optional, safe to run multiple times
- **Roles must be imported** - collaborators need to run config:import after git pull
- **Always `ddev drush cr` after code changes**
- **Commit to git frequently** (working states)

---

**End of Handoff**

**Current State:** Roster Builder complete with drag-and-drop, roles & permissions implemented, notification system complete

**Next Priority:** Schedule Builder or Captain UI implementation

**Breaking Point:** Clean - all major systems functional, captain UI needs building
