# CC Soccer D11 - Session Handoff

**Status:** Waitlist management UI complete, notification system fully functional, roster builder operational

**Last Updated:** December 30, 2024 (Evening Session)

---

## **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 complete admin UI**
- ✅ 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 ✅ FIXED
  - Waitlist spot offered notifications ✅ FIXED
- ✅ 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` - **Admin dashboard with quick links** ✅ UPDATED
- `/admin/ccsoccer/season/{season}/roster` - Roster Builder UI
- `/admin/ccsoccer/waitlists` - **Waitlist overview (all seasons)** ✅ NEW
- `/admin/ccsoccer/season/{season}/waitlist` - **Season-specific waitlist** ✅ NEW
- `/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
- `/waitlist/join/{season}` - Public waitlist join ✅ FIXED
- `/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 4 - December 30, 2024 (Evening): Waitlist Management UI & Bug Fixes**

**Accomplishment:** Completed waitlist admin interface with season filtering, fixed critical notification bugs, improved admin navigation.

#### **1. Fixed Missing Notification Methods**

**Problem:** `WaitlistController` was calling `sendWaitlistJoined()` and other methods tried to call `sendWaitlistSpotOffered()`, but these methods didn't exist in `NotificationService`, causing fatal errors.

**Solution:** Added missing notification methods to `NotificationService`:

```php
/**
 * Send waitlist join confirmation notification.
 */
public function sendWaitlistJoined(UserInterface $user, string $season_name): array

/**
 * Send waitlist spot offered notification.
 */
public function sendWaitlistSpotOffered(UserInterface $user, $season, \DateTime $expiration): array
```

**Files Modified:**
- `src/Service/NotificationService.php` - Added two missing notification methods

#### **2. Fixed Waitlist Routing & Entity Conversion**

**Problem:** Routes for waitlist offer/cancel were passing string IDs instead of Waitlist entity objects, causing `Call to a member function isPending() on string` errors.

**Solution:** Added entity parameter conversion to routing configuration:

```yaml
ccsoccer.waitlist.offer:
  path: '/admin/ccsoccer/waitlist/{waitlist}/offer'
  options:
    parameters:
      waitlist:
        type: entity:ccsoccer_waitlist

ccsoccer.waitlist.cancel:
  path: '/admin/ccsoccer/waitlist/{waitlist}/cancel'
  options:
    parameters:
      waitlist:
        type: entity:ccsoccer_waitlist
```

**Files Modified:**
- `ccsoccer.routing.yml` - Added entity parameter conversion for waitlist routes

#### **3. Implemented Season-Specific Waitlist Filtering**

**Problem:** All waitlist pages showed all seasons' waitlists regardless of which season's URL was accessed.

**Solution:** Updated `WaitlistController::manage()` to accept optional season parameter:

```php
public function manage($season = NULL) {
  // If specific season provided, show only that season
  if ($season) {
    $seasons = [$season];
  }
  else {
    // Show all seasons
    $seasons = $season_storage->loadMultiple();
  }
  // ... rest of logic
}
```

**Routes:**
- `/admin/ccsoccer/waitlists` → Shows all seasons (no parameter)
- `/admin/ccsoccer/season/{season}/waitlist` → Shows only that season

**Files Modified:**
- `ccsoccer.routing.yml` - Added entity conversion for season parameter
- `src/Controller/WaitlistController.php` - Updated manage() method signature and logic

#### **4. Fixed Redirect After Waitlist Actions**

**Problem:** After offering a spot or canceling a waitlist entry, admins were redirected to the "all seasons" overview instead of back to the specific season they were managing.

**Solution:** Updated redirect logic to return to season-specific page:

```php
// In offerSpot()
return $this->redirect('ccsoccer.waitlist', ['season' => $season->id()]);

// In cancelWaitlist()
$season = $waitlist->getSeason();
return $this->redirect('ccsoccer.waitlist', ['season' => $season->id()]);
```

**Files Modified:**
- `src/Controller/WaitlistController.php` - Updated offerSpot() and cancelWaitlist() redirect logic

#### **5. Fixed ProductVariation Price Type Error**

**Problem:** When offering a waitlist spot (which triggers season update), Commerce ProductVariation price setting was failing with type error: "Argument #1 ($price) must be of type Price, PriceItem given"

**Solution:** Used `toPrice()` method to convert PriceItem field to Price object:

```php
// In ccsoccer_season_update() hook
if ($variation && !$season->get('price')->isEmpty()) {
  $variation->setPrice($season->get('price')->first()->toPrice());
  $variation->save();
}
```

**Files Modified:**
- `ccsoccer.module` - Fixed price conversion in season update hook (line 755)

#### **6. Added Waitlist to Admin Navigation**

**Problem:** No easy way to access waitlist management from admin dashboard.

**Solution:** Added waitlist links to both menu system and dashboard quick links:

**Menu Links (ccsoccer_links_menu.yml):**
```yaml
ccsoccer.waitlists:
  title: 'Waitlists'
  route_name: ccsoccer.waitlist.manage
  parent: ccsoccer.admin
  description: 'Manage waitlists for all seasons'
  weight: 5
```

**Dashboard Quick Links (AdminController.php):**
```php
[
  'title' => $this->t('Waitlists'),
  'url' => Url::fromRoute('ccsoccer.waitlist.manage'),
  'icon' => '⏳',
  'description' => $this->t('Manage waitlists for all seasons'),
]
```

**Files Modified:**
- `ccsoccer_links_menu.yml` - Added waitlists menu item
- `src/Controller/AdminController.php` - Added waitlists to quick links section

---

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

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

#### **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`

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

Algorithm for auto-generating balanced rosters with skill/age balancing, group preservation, and gender requirements.

**Files Created:**
- `src/Form/RosterBuilderForm.php`
- `src/Controller/RosterBuilderController.php`
- `src/Service/TeamBalancerService.php`
- `js/roster-builder.js`
- `css/roster-builder.css`

---

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

**Accomplishment:** Implemented complete role-based access control system.

**Created Roles:**
- Board Member role (view reports, update game status)
- Slofriendly role (Board Member + tournament management)
- Captain access control (custom access checker)

**Files Created:**
- `config/install/user.role.board_member.yml`
- `config/install/user.role.slofriendly.yml`
- `src/Access/TeamCaptainAccessCheck.php`

---

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

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

**Features Implemented:**
- Magic link invitation flow enhancement
- Player joined notifications
- Waitlist join confirmation notifications (methods added later in Session 4)
- Override expiration reminder system (12-hour cron)
- Override management CRUD interface

**Files Created:**
- `src/Controller/OverrideController.php`
- `src/Form/OverrideForm.php`

---

## **Architecture Decisions**

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

Built completely custom Registration entity instead of extending contrib module for direct entity references and full control over complex field requirements.

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

Captain is a field on Team entity, not a Drupal role, allowing per-team captain assignments with custom access checker.

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

Store team assignments in `Registration.team` field for auto-save capability and single source of truth.

### **Waitlist Management: Two Routes, One Controller**

Single `manage()` method handles both "all seasons" overview and season-specific filtering through optional parameter, reducing code duplication.

### **Role Hierarchy**

```
Anonymous
  └─ Authenticated (Player - default)
       └─ Board Member (manage waitlist, view reports, update game status)
            └─ Slofriendly (Board Member + tournament management)
                 └─ 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!)
git pull
git status
git add .
git commit -m "message"
git push origin main
```

### **Waitlist Management:**
```bash
# View all waitlists
open http://ccsoccer-d11.ddev.site/admin/ccsoccer/waitlists

# View specific season's waitlist (season ID 3)
open http://ccsoccer-d11.ddev.site/admin/ccsoccer/season/3/waitlist

# Check waitlist entries in database
ddev drush sqlq "SELECT w.id, u.name as player, s.name as season, w.status, w.created FROM ccsoccer_waitlist w JOIN users_field_data u ON w.player = u.uid JOIN season s ON w.season = s.id ORDER BY w.created DESC LIMIT 10"
```

### **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

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

### **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

# 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 roles exist
ddev drush sqlq "SELECT id, label FROM user_role WHERE id IN ('board_member', 'slofriendly')"

# 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, manage waitlists, 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=admin
```

---

## **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 (currently 7-day default)
- 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 manage waitlists** - has manage waitlist permission
- **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)
- **Waitlist actions redirect to season-specific page** - better UX than "all seasons" overview

---

**End of Handoff**

**Current State:** Waitlist management UI complete, all notification methods working, roster builder operational

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

**Breaking Point:** Clean - waitlist fully functional, notification system complete, ready for next feature
