# GoodPlay Web Migration Progress

Last updated: 2026-08-14

## Guardrails

- All implementation changes are restricted to `website/`.
- Flutter/Dart files and `api_php/` are read-only references.
- The existing GoodPlay API remains unchanged and continues serving the mobile app.
- Browser POST requests use CI4 session-backed CSRF protection.
- CI4 calls the existing API server-side using the same JSON contracts, `X-Token`, Basic authentication, and idempotency semantics as Flutter.

## Retained Scope

- Authentication: email login, registration, forgotten password, logout.
- Home: banners, listing sections, featured courts, sports.
- Courts: discovery, venue details, favourites, timeslots and court selection.
- Booking: quote, add-ons, applicable vouchers, confirmation, payment redirect/status.
- Account: booking history/details, invoices, rescheduling, profile/password.
- Loyalty: rewards and customer vouchers.
- Communication/content: inbox, notifications, privacy and terms.

## Excluded Scope

- Events and tournament registration.
- Activities, participants, chat, groups, brackets, matches and standings.
- OneSignal web push.
- Branch links.

## Completed

- [x] Audited the CI4 runtime (PHP 8.4; bundled CI4 runs successfully).
- [x] Established immutable source boundaries.
- [x] Mapped retained Flutter API endpoints and actions.
- [x] Added environment and progress-documentation conventions.
- [x] Added server-side API configuration and client.
- [x] Enabled session-backed CSRF and explicit routing.
- [x] Added responsive shared navigation, branding, fonts and reusable components.
- [x] Added email login, registration, password recovery and logout.
- [x] Added home banners/sections, sports and featured court rendering.
- [x] Added court discovery, venue detail pages and favourites.
- [x] Added timeslot selection, booking quotes, vouchers/add-ons, checkout and payment status.
- [x] Added booking history, booking detail/invoice and rescheduling flow.
- [x] Added rewards, voucher wallet and point redemption.
- [x] Added profile editing, password changes, inbox and notifications.
- [x] Added privacy and terms pages.
- [x] Corrected court name search to use the API's `search_term` contract.
- [x] Added by-time venue discovery with API timeslots, multi-time selection, and date/time hand-off into booking.
- [x] Corrected nested home listing sections and added full article/gallery pages with safe external and retained internal actions.
- [x] Matched the Flutter checkout handoff: bookings requiring payment redirect straight to the API-provided payment URL, without an intermediate booking-details page.
- [x] Expanded booking details with add-ons, discounts, tax, payment state, layout, invoices, and Google/Waze directions.
- [x] Expanded profile parity with member/referral information, activity statistics, support, and account-deletion access.
- [x] Added 20-item notification pagination and API-backed notification detail pages matching the Flutter notification content view.
- [x] Converted sport browsing, court search, and availability date changes to read-only GET navigation; retained session CSRF for state changes without per-submit token rotation.
- [x] Added canonical venue-name slugs for venue and booking URLs with backward-compatible redirects from numeric venue IDs.
- [x] Corrected the rewards catalogue to use `voucher_logo` instead of the API placeholder image, with clipped artwork, concise summaries, expandable terms, and readable voucher expiry dates.
- [x] Passed PHP syntax, route registration and unauthenticated HTTP smoke checks.

## In Progress

- [ ] Live authenticated API acceptance testing with a designated test customer.

## Pending

- [ ] Validate login with a real test customer.
- [ ] Validate a real zero-value/test booking and payment return flow.
- [ ] Confirm production Google OAuth requirements, if Google login is retained.
- [ ] Confirm whether DUPR linking is part of the web scope.
- [ ] Visual acceptance review against the running app on target phone and desktop sizes.

## Verification Results

- `php spark list`: passed.
- `php spark routes`: passed; all routes explicit and CSRF-protected.
- PHP syntax check across all changed PHP files: passed on 2026-08-14.
- JavaScript syntax check: passed on 2026-08-14.
- Local HTTP smoke test: `/health`, `/login`, `/`, `/courts`, `/rewards`, `/privacy` all returned HTTP 200.
- Auth guard: passed; unauthenticated `/profile` returns HTTP 302 to `/login`.
- CSRF enforcement: passed; a POST without a CSRF token is rejected and redirected without exposing a framework stack trace.
- CSRF multi-form behavior: passed; the same valid session token can submit separate forms, while search/date browsing no longer consumes a token.
- Slug routing: numeric venue `9` permanently redirects to its generated venue-name slug; direct venue and booking slug URLs return HTTP 200 and retain date query parameters.
- Desktop and narrow/mobile Chromium visual smoke checks: passed after correcting the auth-card viewport constraint.
- Checkout pricing: changing a voucher recalculates the API total immediately; rental quantity changes do the same after a short debounce. Contact values are preserved, the booking is not created by a price-only refresh, and the existing server-side second-confirmation check remains as a no-JavaScript/race-condition fallback.
- Payment review: corrected per-court pricing to use the API's `court_price`, added the court/add-on/voucher/discount/SST breakdown, and normalized timeslot ranges.
- Venue details: corrected facilities to use the API's `facilities_name` and `facilities_logo` fields; restored venue layout, policy, map/navigation links, court count, and readable rich-text hours/pricing from the Flutter screen.
- Local PHP TLS: configured an explicit trusted CA bundle after diagnosing cURL error 60; certificate verification remains enabled.
- Live public API rendering now passes after configuring PHP's missing CA bundle: home returned 5 sports and 2 featured venues; sport 1 returned 5 venues; venue 9 returned 19 courts and 304 timeslot choices; rewards, privacy and terms returned live non-empty content.
- 2026-08-14 local request smoke test: public routes and CSRF-protected court-search POST returned HTTP 200; authenticated routes remained guarded. A fresh live-data recheck could not be completed because `app.goodplay.my:443` refused connections from the execution environment, so the authenticated and payment acceptance items remain open.

## API Contract Inventory

| Area | Method | Existing API endpoint/action |
|---|---|---|
| Login | POST | `customer` / `email_login` |
| Register | POST | `customer` / `register_email` |
| Forgot password | POST | `customer` / `forget_password` |
| Profile | GET | `customer/action/profile` |
| Edit profile | POST | `customer` / `edit_profile` |
| Change password | POST | `customer` / `change_password` |
| Home banners | GET | `banner/action/appbanner` |
| Home sections | GET | `banner/action/listing` |
| Featured courts | GET | `banner/action/featured_court` |
| Sports | GET | `complex/action/sports` |
| Court listing | POST | `complex` / `allv2` |
| Court discovery times | GET | `complex/action/timeslotv2` |
| Favourites | GET | `complex/action/favorite` |
| Toggle favourite | POST | `venue` / `add_to_favorites`, `remove_from_favorites` |
| Venue timeslots | POST | `venue_v5` / `timeslotv2`, `timeslotv2_labels` |
| Venue courts | POST | `venue_v5` / `courts` |
| Booking calculation | POST | `booking_v5` / `calculate_price` |
| Booking add-ons | POST | `booking_v5` / `addon_products` |
| Create booking | POST | `booking_v5` / `create_booking` |
| Payment status | POST | `booking_v5` / `check_payment` |
| Booking history | GET | `booking_v5/action/all` |
| Booking details | GET | `booking_v5/action/details/booking_id/{id}` |
| Applicable vouchers | GET | `customer/action/get_voucher/filter/applicable/venue_id/{id}` |
| Rewards | GET | `voucher/action/get_purchasable_voucher` |
| Buy reward | POST | `voucher` / `purchase_voucher` |
| Customer vouchers | GET | `customer/action/get_voucher/filter/available` |
| Inbox | GET | `customer/action/get_inbox` |
| Notifications | GET | `notification/action/all` |
| Site content | GET | `home/action/about_us` |

## Continuation Notes

Start by reading this file and `docs/ARCHITECTURE.md`, then run `php spark routes` and `php spark serve`. Do not infer permission to edit anything outside `website/`.

For the remaining acceptance pass, use a designated test customer and a safe zero-value/test venue. Verify login, favourite changes, reward purchase, booking creation, payment confirmation, booking detail, and rescheduling. Do not create a chargeable production booking merely to exercise the flow.
