# CC Soccer D11 - Session Handoff
**Date:** April 17, 2026
**Branch:** `main`

---

## Current State

### Code ✅ LOCAL + TEST IN SYNC
All of Caleb's and Andrew's work is merged to main and deployed to TEST.

### Migration ✅ COMPLETE — TEST NOW ON LIVE DATA
- Fresh D7 dump taken April 16, 2026 from production D7 DB
- Migration run locally: 1,727 users, 581 credits, 5,072 registrations, 42 seasons
- Exported as `d11-beta-2026-04-16-clean.sql.gz` (DEFINER-stripped)
- Imported to `n6ac4b5_d11prod` on InMotion
- TEST site (`test.ccsoccer.com`) now points at `n6ac4b5_d11prod` — **real user data, real emails**
- All board member roles assigned (including kaimark)

### Passkeys ✅ DEPLOYED TO TEST
- `drupal/wa` 2.0.0-rc7 installed locally and on test.ccsoccer.com
- Touch ID / passkey login working
- Password login and reset still work as fallback

### Server Aliases ✅ SET UP
Bash aliases saved to `~/.bashrc` on InMotion server:
- `ccsDeploy` — cd to site dir + git pull
- `ccsCr` — drush cr
- `ccsUpdb` — drush updb -y
- `ccsCim` — drush cim -y
- Full deploy: `ccsDeploy && ccsUpdb && ccsCim && ccsCr`

### Deploy Key ✅ SET UP
SSH deploy key configured on InMotion — `ccsDeploy` no longer prompts for GitHub credentials.

---

## Session Work — April 17, 2026

### Commerce Store ✅
Created CC Soccer store via admin UI (content entity, not config — doesn't survive DB import). Order type confirmed pointing to custom checkout flow. Payment gateways confirmed sandbox + manual — intentional for TEST.

### Jersey Notification Opt-in ✅
- Added `field_jersey_notifications` boolean to user entity via `ccsoccer_update_9062`
- Added "Jersey Alerts" checkbox column to `BoardContactPreferencesForm`
- `sendJerseyPurchase()` in `NotificationService` now queries only board members with `field_jersey_notifications = TRUE` instead of all board members
- Respects existing `field_board_email` / `field_board_phone` preferences
- Tested locally via Mailpit — only opted-in members received notification
- **Action needed on TEST:** Go to Board Contact Preferences and check Jersey Alerts for appropriate members

### Completion Pane — Non-registration Orders ✅
- `CompletionPane::buildPaneForm()` now has three branches: season reg, tournament reg, no-reg (jersey-only)
- Jersey-only shows "Order complete!" / "Your order has been confirmed." and redirects to My Account
- Previously showed "You're registered!" for all order types

### Beta Tester Role ✅
- `config/sync/user.role.beta_tester.yml` added — no permissions, weight 4
- `getAllowedContacts()` in `NotificationService` now includes `beta_tester` role alongside `board_member`
- Beta users get their own transactional emails on TEST without board notification access
- At launch: remove role from users — gating disappears automatically when `site_instance = production`
- Add beta user IPs manually to `.htaccess`

### Pre-launch Checklist Updated
- Added three-tier button methodology pass to checklist

---

## Session Work — April 25, 2026

### UPDATE_WORKFLOW.md ✅
New top-level doc covering `composer install` vs `composer update`, conflict resolution, and the producer/consumer split between developers. Reference for the team to avoid simultaneous lockfile bumps.

### Admin Tables — Mobile Scroll Wrapper ✅
- `js/admin-mobile.js` wraps every admin table in a focusable `<div class="ccsoccer-table-scroll-wrapper">` at runtime
- `css/admin-mobile.css` styles the wrapper (`overflow-x: auto`, iOS momentum scrolling, keyboard focus ring, print styles)
- Library `ccsoccer/admin-mobile` attached on admin routes via `ccsoccer_page_attachments()`
- Fixes mobile portrait AND desktop-with-sidebar-open in both Claro and the public theme (board members / tournament directors lack the use admin theme permission and see admin pages in the public theme)
- Wrapper has `tabindex=0`, `role=region`, `aria-label` for keyboard/screen-reader users

### Dashboard — Current Seasons Filter ✅
- `buildSeasonsOverview()` in `AdminController.php` now filters to `end_date >= today` and sorts by `start_date ASC`
- Past seasons hidden; empty-state message differentiates "no seasons created yet" vs "all existing seasons have ended"
- Affects `/admin/ccsoccer/dashboard` and `/admin/ccsoccer/board-dashboard` (shared rendering)

### Welcome Banner Legibility ✅
- "Board Member Tools" / "Tournament Director Tools" headings now render in white on the dark welcome banner
- `.dashboard-section.welcome-section` background defined explicitly with `--color-gray-800`, text with `--color-text-inverse` so the banner looks the same in Claro and the public theme

---

## ⚠️ ANDREW — ACTION REQUIRED: Update Your Local DB

Your local DB has the old 200-user sample with masked emails. You need to replace it with the new production-ready migration DB. **Do not re-run the migration** — just import the finished DB directly. This is much simpler than what Caleb went through.

### Step 1: Download the DB from the server
```bash
scp ccsoccer:/home/n6ac4b5/d11-beta-2026-04-16-clean.sql.gz ~/Sites/ccsoccer-d11/
```
(Assumes your SSH alias is `ccsoccer` — adjust if different)

### Step 2: Import it into DDEV
```bash
cd ~/Sites/ccsoccer-d11
ddev import-db --file=d11-beta-2026-04-16-clean.sql.gz
```
This replaces your local DB content cleanly — no site:install, no cim, no UUID drama.

### Step 3: Pull latest code
```bash
git pull
ddev drush cr
```

### Step 4: Update your local settings.local.php D7 connection
Your `settings.local.php` needs a `d7` database entry pointing at the fresh D7 dump so the migration command works if you ever need to run it. Import the D7 dump first:
```bash
ddev mysql -e "CREATE DATABASE IF NOT EXISTS n6ac4b5_ccsoccer;"
gunzip -c /path/to/d7-fresh-2026-04-16.sql.gz | ddev mysql n6ac4b5_ccsoccer
```
(The D7 dump is at `/home/n6ac4b5/d7-fresh-2026-04-16.sql.gz` on the server if you need it)

Your `settings.local.php` should already have:
```php
$databases['d7']['default'] = [
  'database' => 'n6ac4b5_ccsoccer',
  'username' => 'db',
  'password' => 'db',
  'host' => 'db',
  'driver' => 'mysql',
  'prefix' => '',
];
```
If it doesn't, add it.

### Step 5: Log in locally
Admin credentials: username `admin`, password `TJ4XxyYGCd`

---

## Andrew's Recent Features (all merged + on TEST)

### 1. Role-based Help Center
`/admin/ccsoccer/help` — documentation for board members, admins, and tournament directors. Role-gated by existing permissions. Own CSS (`help-center.css`), controller (`HelpCenterController.php`), routing, menu link, and library entry. All hardcoded content in PHP methods.

### 2. Personalized next-game banner
Shows authenticated players their next upcoming game in the site header (team, jersey color, time, field). Moved from module `hook_page_top()` to theme `preprocess_page` + Twig. Cache contexts set to `user` + `max-age = 0`.

### 3. Schedule grid current-week fix
Grid now scrolls to the current/next week on load. Fixed in both `ScheduleGridBuilder::calculateCurrentWeekIndex()` and `schedule-navigation.js`.

### 4. My Teams "registered but not assigned" fix
Players registered for a season with no team yet see "teams haven't been assigned yet, check back soon" instead of the false "not registered" message. Three-state logic in `ContentController::myTeamsPage()`.

### 5. Mobile swipe for schedule
Swipe left/right to navigate weeks on mobile. Feature branch `feature/mobile_swipe_schedule_view` merged to main.

### 6. Login form PHP warning fix
Added `'#parents' => []` to passkey help text element in `ccsoccer.module` — fixes PHP warnings on failed login attempts.

---

## Beta Testing Notes

### Password Reset Flow
- TEST now has real user data — all migrated users have randomized passwords
- Users (including board members) must use "Forgot password" to get in
- Password reset emails are gated by `site_instance = 'test'` — only board members' emails go through
- Caleb confirmed the flow works end-to-end (email went to spam — SPF/DKIM needed before launch)

### Notification Gating on TEST
When `site_instance = 'test'`, `NotificationService` dynamically builds an allowlist from all users with the `board_member` role. Only their emails/phones go through. Everyone else is silently blocked.

### What Players See
- My Account shows current season registration ✅
- No team or schedule shown (none assigned yet — correct) ✅
- Registration page shows available seasons ✅

---

## Pending Decision — Passkey RP ID
Passkeys are domain-bound. Passkeys registered on `test.ccsoccer.com` will NOT work on `ccsoccer.com` at launch unless the RP ID is set to `ccsoccer.com` now.

**Action needed:** Determine if `drupal/wa` supports a custom RP ID. If so, set it to `ccsoccer.com` before users start registering passkeys on TEST.

---

## Next Steps

### 1. Pre-launch checklist
- Set up SPF/DKIM for ccsoccer.com domain (password reset emails going to spam)
- Remove IP whitelist block from `.htaccess` on launch day
- Confirm www vs non-www canonical redirect
- Confirm HTTPS handling
- Enable CSS/JS aggregation
- reCAPTCHA setup
- Set up two alias sets in `~/.bashrc`: `ccsTest*` (pointing to `~/test_ccsoccer_site`) and `ccsProd*` (pointing to `~/public_html`) for Deploy, Cr, Updb, Cim
- Create live `settings.local.php` in `~/public_html/web/sites/default/` with live Authorize.net credentials and `site_instance = production`
- Three-tier button methodology pass (primary solid red = main CTAs, primary-soft light red = navigation actions, white/outlined = informational)

### 2. Post-launch manual actions (cannot be scripted)
- Assign `permanent_override`: Haley, Layne, Julie, Myk, Kyle
- Send password reset email to all migrated users (all have random passwords)
- Verify credit balances for known users against D7
- Check credits/registrations issued in D7 after April 16, 2026 dump date

### 3. Team handling refactor (discuss with Andrew first)
Eager team creation on season save → lazy creation when Roster Builder opens.
Do not implement without consulting Andrew — he built the roster builder.

### 4. Credits Admin page (discuss with Andrew)
`/admin/ccsoccer/credits` renders blank (default EntityListBuilder, no columns). Options:
- **Option A:** Remove menu item — Season/Player Credits pages cover all workflows
- **Option B:** Repurpose as credits dashboard/summary report
- **Option C:** Build `CreditsListBuilder.php` with filters + pagination
- **Option D:** Hybrid — dashboard for board members, filtered list for admins

### 5. Resolve Passkey RP ID decision (see above)

### 6. `slofriendly` role reference
During `cim` a warning appeared: `Config user.role.slofriendly not found`. There's a reference to this old role somewhere in module install hooks. Not breaking, but worth cleaning up before launch.

---

## DB Quick Reference

### TEST server DB (live migration data)
- DB: `n6ac4b5_d11prod`
- User: `n6ac4b5_ccsoccer_user`
- Password: `vGL3KWO(K8C;`
- Migration counts: 1,727 users, 581 credits, 5,072 registrations, 42 seasons

### TEST site app DB (old 200-user sample — no longer in use)
- DB: `n6ac4b5_ccsoccer_test`
- User: `n6ac4b5_ccsoccer_test`
- Password: `Soc8er#Test24!`

### D7 production DB (source of truth)
- DB: `n6ac4b5_ccsoccer`
- User: `n6ac4b5_ccsoccer_user`
- Password: `vGL3KWO(K8C;`
- Fresh dump: `/home/n6ac4b5/d7-fresh-2026-04-16.sql.gz` on server

### Local D11 DB
- Admin: `admin` / `TJ4XxyYGCd`
- D11 beta export: `d11-beta-2026-04-16-clean.sql.gz` in project root (gitignored)

---

## Migration Command Reference

**File:** `web/modules/custom/ccsoccer/src/Drush/Commands/MigrateCommands.php`

```bash
# Full migration (steps 1-4: seasons, users, credits, registrations)
ddev drush ccsoccer:migrate-d7

# Individual steps
ddev drush ccsoccer:migrate-d7 --step=seasons
ddev drush ccsoccer:migrate-d7 --step=users
ddev drush ccsoccer:migrate-d7 --step=credits
ddev drush ccsoccer:migrate-d7 --step=registrations

# Role assignments — run ONCE after migration is finalized (not in 'all')
ddev drush ccsoccer:migrate-d7 --step=roles --dry-run
ddev drush ccsoccer:migrate-d7 --step=roles
```

**Role assignments hardcoded in `assignRoles()`:**
| Username | Roles |
|---|---|
| David.farris | board_member |
| Csalvini | board_member |
| laynesmith | board_member |
| chrisraymer | board_member |
| SoccerHD4 | board_member, tournament_director |
| kaimark | board_member |
| cncross | board_member, tournament_director, administrator |
| abmeade@hotmail.com | board_member, tournament_director, administrator |

Note: `abmeade@hotmail.com` is his D11 username (email-as-username from D7), looked up by `name` field.

---

## Key Facts / Gotchas

### DB import fix for InMotion
DDEV exports views with `DEFINER=\`db\`@\`%\`` which InMotion rejects. Always use the clean version:
```bash
gunzip -c dump.sql.gz | sed 's/DEFINER=[^*]*\*/\*/' | gzip > dump-clean.sql.gz
```

### Fresh DB reset for local dev (if needed again)
If you ever need to wipe and re-run the migration locally:
```bash
ddev drush site:install --account-name=admin --account-pass=TJ4XxyYGCd -y
ddev drush config-set system.site uuid $(grep uuid config/sync/system.site.yml | awk '{print $2}') -y
ddev drush entity:delete shortcut -y
ddev drush entity:delete shortcut_set -y
ddev drush cim -y
ddev drush cr
# Then run migration
ddev drush ccsoccer:migrate-d7
ddev drush ccsoccer:migrate-d7 --step=roles
```
Note: `ddev import-db --file=dump.sql.gz` is much simpler if you have a finished DB export — skip the above entirely.

### Theme vs module rendering
Site-wide header content is rendered by the **theme** (`ccsoccer_theme_preprocess_page` + `page.html.twig`), NOT `hook_page_top()`. New header features go in the theme.

### Two-file CSS sync
Any change to base styles must be applied to both:
- `web/modules/custom/ccsoccer/css/ccsoccer-base.css` (admin/Claro pages)
- `web/themes/custom/ccsoccer_theme/css/base.css` (public pages)

### Email-as-username migration
Some D7 users had email addresses as usernames. These migrate as-is into the D11 `name` field. Their `mail` field gets real email from D7 `mail` column. Look them up by `name`, not `mail`.

### Notification gating on TEST
`NotificationService::getAllowedContacts()` queries all `board_member` role users dynamically. Board member emails/phones go through; all others are blocked. This means as of this session, board members can receive real emails on TEST (password resets confirmed working).

---

## Server Quick Reference
```bash
# SSH in
ssh ccsoccer

# Full deploy
ccsDeploy && ccsUpdb && ccsCim && ccsCr

# Individual commands
ccsDeploy   # git pull
ccsUpdb     # drush updb -y
ccsCim      # drush cim -y
ccsCr       # drush cr
```

## Test Server .htaccess (IP Whitelist)
Not in git — protected via `skip-worktree`. If ever lost:
```apache
Require ip 68.249.41.9 35.151.50.130 99.8.107.54 97.84.70.141
<IfModule mod_headers.c>
  Header set X-Robots-Tag "noindex, nofollow, noarchive"
</IfModule>
```
```bash
git update-index --skip-worktree web/.htaccess
```

## Git Workflow
- Always `git pull` before `git push`
- `main` is the primary branch
- `settings.local.php` is NOT in git
- `*.sql.gz` files are gitignored
