52 Commits (55358e4f09747d67d222387c75fcf2918b4857fd)
 

Author SHA1 Message Date
Guillermo Pages 55358e4f09 feat: use DRONE_COMMIT_SHA as build number
continuous-integration/drone/push Build is passing Details
3 weeks ago
Guillermo Pages 238320b015 fix: handle Flask API response wrapper format
continuous-integration/drone/push Build is passing Details
Fix TypeError: t.map is not a function on Plans and Members pages

Issue: Backend wraps responses in { status: 'success', data: {...} }
but frontend was expecting direct data.

Changes:
- Update handleApiResponse to extract data from Flask wrapper
- Special handling for policy endpoint (returns { policy: {...} })
- Handles both wrapped and unwrapped responses for compatibility

Fixes the 'Application error' on Plans and Members pages.
3 weeks ago
Guillermo Pages 86b1414ae0 refactor: simplify navigation for admin portal
continuous-integration/drone/push Build is passing Details
Remove player-facing menu items:
- Remove 'Booking' (player feature)
- Remove 'Player Lookup' (player feature)
- Remove 'Assessment' (player feature)

Update remaining navigation:
- Replace 'Dashboard' with direct link to '/admin/clubs'
- Rename to 'Venue Management' for clarity
- Simplified menu now shows only admin features
- Updated animation indices after removing items

Result: Clean, focused admin portal navigation.
3 weeks ago
Guillermo Pages 95759fd649 feat: add navigation for admin portal
continuous-integration/drone/push Build is passing Details
Add 'Venue Admin' menu item in hamburger navigation:
- Links to /admin/clubs for facility management
- Purple accent to distinguish from player features
- Positioned after assessment, before language selector

Add tabs to club detail page:
- Plans tab (purple accent)
- Members tab (purple accent)
- Settings tab (purple accent)
- All tabs link to new admin portal pages

Navigation is now complete for accessing all facility management features.
3 weeks ago
Guillermo Pages 5dd283525b feat: add facility admin management UI (plans, members, policy)
continuous-integration/drone/push Build is passing Details
Implements Phase B1-B4 of the admin portal frontend:

Phase B1 - Setup & Types:
- Add TypeScript type definitions for all admin APIs
- Add complete API client with all 15 endpoint integrations

Phase B2 - Membership Plans UI:
- Add plans listing page with create/edit/delete functionality
- Add PlanCard, PlanFormModal, and PlanListSkeleton components
- Support billing periods, pricing, and activation status

Phase B3 - Member Management UI:
- Add members listing page with search and filters
- Add member CRUD modals with role/plan assignment
- Add RoleBadge, StatusBadge, and MemberCard components
- Support filtering by role and status

Phase B4 - Policy Configuration UI:
- Add facility settings page for policy management
- Add AccessModelSelector for membership/payg/open models
- Add GuestPricingInput and BookingLimitsForm components
- Support configuring booking limits and access control

All components follow existing design patterns with purple/indigo
gradients, responsive layouts, and loading states.
3 weeks ago
Guillermo Pages ff50bdfa2c fix: move address fields from settings to top-level facility object
continuous-integration/drone/push Build is passing Details
The API now returns address at facility.address instead of facility.settings.address, and uses field names like address_line_1 and zip instead of line_1 and postal_code. Updated the ClubProfile type and form to match.
1 month ago
Guillermo Pages 94a1979201 fix: update facility detail types to match current API response shape
continuous-integration/drone/push Build is passing Details
The API now returns 'facility' with expanded address/settings fields instead of 'club', and slot types have been restructured to use flat properties instead of nested booking/meta objects.
1 month ago
Guillermo Pages 4d9aeb9045 fix: update admin endpoints from /admin/clubs to /admin/facilities (Build 363+)
continuous-integration/drone/push Build is passing Details
Complete migration of all admin API endpoints from clubs to facilities nomenclature:

**Admin Endpoint Changes:**
- GET /admin/clubs → GET /admin/facilities
- GET /admin/clubs/{id} → GET /admin/facilities/{id}
- All 30+ admin routes migrated from /clubs/ to /facilities/

**API Client Files Updated:**
- admin-clubs.ts: List and detail endpoints (2 endpoints)
- materialisation.ts: Materialization status and trigger (2 endpoints)
- slot-definitions.ts: All CRUD operations (6 endpoints)
- courts.ts: Court management and profile (7 endpoints)
- admin-api.ts: TypeScript documentation comments (7 updates)

**Total Changes:**
- 28 endpoint path replacements across 5 files
- All /admin/clubs references removed
- Comments and documentation updated

**Verification:**
- TypeScript compilation: ✓ No errors
- All admin endpoints now use /admin/facilities
- Fixes 404 errors on admin panel endpoints

BREAKING CHANGES: Requires backend Build 363+ with /admin/facilities endpoints
1 month ago
Guillermo Pages d0b6c1bb35 fix: update /facilities endpoint to use Build 362+ response structure
continuous-integration/drone/push Build is passing Details
Complete migration to Build 362+ facility discovery response format:

**Discovery Endpoint Changes:**
- Parse 'facilities' array instead of 'remotes' from /facilities response
- Update facility object structure to use facility_* fields exclusively
- Remove all origin_* field references from facility discovery

**Component Updates:**
- RemoteSportInfo: Update to parse facilities array and use facility_name, facility_slug, facility_logo_url
- useCourtSlots: Build remotesData with facilities array using new field structure
- Add backward compatibility fallbacks for transition period

**Response Structure Migration:**
- OLD: data.remotes[] with origin_id, origin_name, origin_slug fields
- NEW: data.facilities[] with facility_id, facility_name, facility_slug fields
- Frontend now receives pure facility/venue data, not provider/origin data

**Files Updated:**
- src/components/RemoteSportInfo.tsx (interface and parsing logic)
- src/hooks/useCourtSlots.ts (remotesData structure generation)

**Verification:**
- TypeScript compilation: ✓ No errors
- ESLint: ✓ No new issues

BREAKING CHANGES: Requires backend Build 362+ for facility-centric discovery responses
1 month ago
Guillermo Pages 6ebc57dd36 feat: migrate to API v2.0 with facility-based routing (Build 358+)
continuous-integration/drone/push Build is passing Details
Complete migration to match backend breaking changes from Build 355+ and Build 358+:

**Terminology Migration (Build 355+):**
- Rename club → facility (club_id → facility_id, club_name → facility_name)
- Rename remote → origin/provider (remote_server_id → origin_id, remote_type → provider)
- Update all field references: remote_name → origin_name, remote_logo_url → origin_logo_url

**Facility-Based Routing (Build 358+):**
- Change routing from provider-based to facility-based
- Update URL parameters: remote_slug → facility_slug in all booking/slot endpoints
- Update discovery endpoint: /remotes → /facilities
- Update slot responses: remote object → facility object with new field structure
- Update user settings: default_remote_sport.origin_slug → facility_slug

**Files Updated:**
- 32 files modified (TypeScript types, API clients, hooks, components)
- 150+ field name changes across the codebase
- All response interfaces updated to match new API contract

**Verification:**
- TypeScript compilation: ✓ No errors
- ESLint: ✓ No migration-related issues

BREAKING CHANGES: Requires backend Build 358+ for facility-based routing
1 month ago
Guillermo Pages 3b7f937505 feat(slot-definitions): add real-time job tracking to materialization status panel
continuous-integration/drone/push Build is passing Details
- Add MaterialisationJobInfo interface with job status, timing, and results
- Add last_job field to MaterialisationStatus (BUILD 353 support)
- Add calculateElapsedTime and formatElapsedTime helper functions
- Implement job status polling (every 2s while queued/running)
- Add elapsed time counter for running jobs with live updates
- Display job progress states:
  - Queued: 'Job queued, waiting to start...'
  - Running: 'Generating slots... (45s)' with policy and horizon info
  - Completed: ' Generated X slots (Y cancelled, Z errors)' with detailed metrics
  - Failed: ' Job failed' with error message
- Update rate limit display logic (only show when can_trigger=false AND no active job)
- Disable trigger button when job is active (queued/running)
- Resolves UX confusion where users saw rate limits while jobs were actually running
- Implements Backend Brooke BUILD 353 job tracking feature
1 month ago
Guillermo Pages 2881721b18 feat(slot-definitions): add materialization policy selector (SAFE/CLEANUP)
continuous-integration/drone/push Build is passing Details
- Add MaterialisationPolicyProfile type and MATERIALISATION_POLICIES constants
- Add policy selector UI with radio buttons for SAFE and CLEANUP modes
- SAFE mode (default): skips overlapping slots to protect existing schedule
- CLEANUP mode: removes conflicting slots to fix overlaps (keeps bookings safe)
- Update MaterialisationTriggerRequest to include policy_profile field
- Pass selected policy to triggerMaterialisation API call
- Fixes issue where partial overlaps blocked slot materialization
- Implements Backend Brooke's BUILD 352 policy preset feature
1 month ago
Guillermo Pages 52b3d38088 fix(materialisation): show correct status for idle state after successful run
continuous-integration/drone/push Build is passing Details
- 'Up to date' badge now shows when status is idle with last_success_at
- 'Not yet run' badge only shows when status is idle AND last_run_at is null
- Success details section now shows for both completed and idle-after-success states
- Fixes issue where successful materialisation showed 'Not yet run' after transitioning from 'completed' to 'idle' status
1 month ago
Guillermo Pages 8e64360fe2 fix(slot-definitions): make valid_from field required in generate modal
continuous-integration/drone/push Build is passing Details
- Set default value to today's date
- Add validation before form submission
- Update UI label to show required field with asterisk
- Fixes validation error: 'valid_from' required field
1 month ago
Guillermo Pages 2d66d42629 fix: add missing translations and fix type error
continuous-integration/drone/push Build is passing Details
Added missing translation keys in all language dictionaries:
- "Slot" (en: "Slot", fr: "Créneau", de: "Zeitfenster")
- "© 2024 Playchoo Manager. Venue administration portal." (translated to fr and de)

Fixed type error in ClubProfileTab.tsx:
- hasFieldValue() now explicitly returns boolean using Boolean() wrapper

This resolves all TypeScript compilation errors.
1 month ago
Guillermo Pages b239081211 fix: update response types to match actual API contract
continuous-integration/drone/push Build was killed Details
Backend Brooke confirmed the actual API response structure uses "definitions"
not "created_definitions". Also added missing preset and pattern fields to
GenerateSlotDefinitionsResponse.

Changes:
- GenerateSlotDefinitionsResponse: added preset and pattern fields
- GenerateSlotDefinitionsResponse: renamed created_definitions → definitions
- CloneSlotDefinitionResponse: renamed created_definitions → definitions

This matches the confirmed API contract from BUILD 348.
1 month ago
Guillermo Pages f450c83c4e revert: restore preset field name (Backend reverted breaking change)
continuous-integration/drone/push Build is passing Details
Backend Brooke reverted the breaking change from BUILD:347. The generate
endpoint continues to use "preset" field name, not "pattern_type".

This reverts commit 834e1a8 and restores the original API contract.

Changes:
- Reverted GenerateSlotDefinitionsRequest: pattern_type → preset
- Reverted GenerateSlotDefinitionsModal to send preset in request body

Backend BUILD:348 has restored the original "preset" field.
1 month ago
Guillermo Pages 834e1a8195 fix: rename preset to pattern_type in generate request (BREAKING CHANGE)
continuous-integration/drone/push Build is passing Details
Backend API changed field name from "preset" to "pattern_type" in the
generate slot definitions endpoint (BUILD:347).

Changes:
- Updated GenerateSlotDefinitionsRequest interface: preset → pattern_type
- Updated GenerateSlotDefinitionsModal to send pattern_type in request body

This fixes compatibility with the updated backend API contract.
1 month ago
Guillermo Pages 9c802ea2aa feat: migrate admin API clients to send locale/timezone headers
continuous-integration/drone/push Build is passing Details
Migrated all admin API clients to automatically send X-Locale and X-Timezone headers
with every request for proper internationalization and timezone-aware operations.

Changes:
- Migrated slot-definitions.ts, materialisation.ts, courts.ts, booking-admin.ts to use apiFetch utility
- Added getLocaleHeaders() helper to admin-clubs.ts for SSR compatibility
- Added getSlotDefinitionPresets() endpoint to fetch localized preset metadata
- Updated slot-definitions types to support API-based preset loading
- Refactored GenerateSlotDefinitionsModal to fetch presets from API with localized titles/descriptions

Benefits:
- All API requests now include user's locale (e.g., en-US, fr-CH) for i18n
- Timezone-aware operations use browser's Intl.DateTimeFormat timezone
- Preset titles and descriptions are localized per user language
- Consistent header management across all admin endpoints

Technical Details:
- apiFetch extracts locale from URL pathname via getPathnameLocale()
- apiFetch automatically adds credentials: 'include' for session auth
- admin-clubs.ts uses manual headers for SSR cookie forwarding compatibility
- GenerateSlotDefinitionsModal shows loading state while fetching presets
1 month ago
Guillermo Pages 1ebe61fa40 feat: add Generate and Clone UI for slot definitions
continuous-integration/drone/push Build is passing Details
Built complete bulk operations UI for slot definitions based on Backend Brooke's spec (BUILD:345).

New Components:
- GenerateSlotDefinitionsModal (380 lines)
  - Preset selector with 4 options (workday_standard, weekend_extended, all_week_uniform, hourly_daytime)
  - Court multi-select with Select All
  - Validity date pickers (optional)
  - Advanced pattern overrides (collapsible): custom days, time range, duration, interval, capacity
  - Real-time validation and error handling

- CloneSlotDefinitionModal (280 lines)
  - Source definition display with full details
  - Target courts multi-select
  - Target days multi-select (pre-selected source day)
  - Validity date overrides (optional)
  - Preview of estimated clone count

Integration:
- Added Generate button (blue, magic wand icon) to page header
- Added Clone button (blue copy icon) to each table row
- Wired up modals with success callbacks to reload data
- Success callbacks trigger table refresh

Features:
- All modals use professional slate theme
- Loading states with spinners
- Error display with user-friendly messages
- Form validation before submission
- Modal backdrop dismissal

Ready for testing against staging (BUILD:345).
1 month ago
Guillermo Pages 13ec348fb2 feat: add bulk operations API support (generate/clone)
continuous-integration/drone/push Build is passing Details
Added TypeScript types and API client functions for slot definition bulk operations.

Types:
- SlotDefinitionPreset enum with 4 presets
- PRESET_OPTIONS metadata for UI display
- GenerateSlotDefinitionsRequest/Response
- CloneSlotDefinitionRequest/Response
- PatternOverrides for preset customization
- SkippedSlot interface for overlap handling

API Functions:
- generateSlotDefinitions() - bulk create from presets
- cloneSlotDefinition() - copy to multiple courts/days

Based on Backend Brooke's spec (BUILD:345, staging).
Next: Build UI modals for generate and clone flows.
1 month ago
Guillermo Pages bd2cbb3a0d feat: implement booking admin test infrastructure for 8-scenario E2E grid
continuous-integration/drone/push Build is passing Details
Built complete booking admin API integration for idempotency validation testing requested by Backend Brooke.

Components:
- TypeScript types for booking admin operations (cancel/move/attendees)
- API client with full idempotency and ETag support
- Comprehensive test page with 8 scenario buttons
- Real-time result tracking with request/response capture

Features:
- X-Idempotency-Key header support (UUID v4)
- ETag-based optimistic concurrency (If-Match)
- Provider gating checks (local vs fairplay)
- User-friendly error message mapping (12 error codes)
- HAR capture instructions for integration log

Test Scenarios:
1. Cancel within grace (≤15 min)
2. Cancel beyond grace (>15 min)
3. Move dry_run success
4. Move dry_run failure
5. Move window exceeded (>14 days)
6. Attendee capacity limits
7. Capacity race (concurrent moves)
8. ETag guard (stale If-Match)

Ready for E2E execution against booking admin endpoints.
1 month ago
Guillermo Pages 6b971d723b refactor: remove all mock data flags and conditional logic
continuous-integration/drone/push Build is passing Details
Removed USE_MOCKS and USE_MOCK_DATA flags from all API client functions. All endpoints now call the real backend API directly without conditional mock data paths.

Changes:
- SlotDefinitionsComponent: removed mock flags from loadData() and loadDefinitions()
- materialisation.ts: removed mock conditionals from getMaterialisationStatus() and triggerMaterialisation()
- admin-clubs.ts: removed MaybeMock wrapper functions and USE_MOCK_DATA flag
- courts.ts: removed mock conditionals from all 7 CRUD functions

All API calls now go directly to https://api.playchoo.com backend.
1 month ago
Guillermo Pages 45e859694b fix(slot-definitions): show actual court names in dropdown
continuous-integration/drone/push Build is passing Details
Changed from hardcoded MOCK_COURTS to fetching real court data from club detail API.
Dropdown now shows actual court names (e.g., 'Padel 1', 'Tennis Int 1') instead of generic 'Court 1', 'Court 2'.

Changes:
- Fetch courts from getAdminClubDetail() on component mount
- Pass real courts array to SlotDefinitionForm
- Split loadData() (courts + definitions) and loadDefinitions() (refresh after CRUD)

Artifacts: src/app/[locale]/admin/clubs/[club_id]/slot-definitions/SlotDefinitionsComponent.tsx:36-38,298
1 month ago
Guillermo Pages 20971e53c0 fix(api): update policies type and materialisation endpoint URL
continuous-integration/drone/push Build is passing Details
Per Backend Brooke messages:
- Made BookingDetail.policies required (commit 75c8edf deployed)
- Fixed materialisation endpoint: /materialisation-trigger → /slot-materialize
- CORS headers fix deploying on backend (X-Idempotency-Key, If-Match, If-None-Match)

Artifacts: src/types/bookings.ts:69-73, src/lib/api/materialisation.ts:91
1 month ago
Guillermo Pages 9186b35649 fix(types): add missing useEffect import and fix type mismatches
continuous-integration/drone/push Build is passing Details
Fixes from tsc type checking:
- Re-added useEffect import to ClubCourtsTab (used in CourtFormModal)
- Updated mock courts data to include created_at/updated_at timestamps
- Fixed CourtError dependencies type to match CourtDependencies structure
  (slot_instances_future/booked instead of upcoming_bookings)

All court-related TypeScript errors resolved. Build succeeds.
1 month ago
Guillermo Pages 86fa2a92e6 fix(courts): use club detail courts data instead of separate fetch
continuous-integration/drone/push Build is passing Details
Root cause: GET /admin/clubs/{id}/courts endpoint doesn't include
sport_variation nested structure, but GET /admin/clubs/{id} does.

Changes:
- Updated AdminClubDetail.courts to use Court[] type (includes sport_variation)
- ClubDetailTabs now passes courts + onUpdate callback to ClubCourtsTab
- ClubCourtsTab accepts courts as props instead of fetching separately
- Removed redundant getCourts() API call
- Removed debug logging

This fixes the court edit dropdown issue where sport variation wasn't
displaying because the data wasn't available from the courts endpoint.
1 month ago
Guillermo Pages e21581dcd0 debug: add console logging for court sport variation initialization
continuous-integration/drone/push Build is passing Details
Added debug logging to investigate sport variation dropdown issue:
- Log court data object on mount
- Log extracted sport_variation_id value
- Added visual disabled state (gray background) for locked dropdown
- Extracted initialSportVariationId to const for better debugging

This will help identify if:
1. Court data has sport_variation nested structure
2. sport_variation_id is being extracted correctly
3. Dropdown value is being set properly

Related: BUILD #21 sport variation display issue
1 month ago
Guillermo Pages 6a80607430 feat(bookings): add dry_run slot picker with live validation
continuous-integration/drone/push Build is passing Details
Implemented complete slot picker flow with real-time validation:

**Components Created:**

1. **SlotPickerTile** (src/components/bookings/SlotPickerTile.tsx)
   - Individual slot tile with dry_run validation
   - 4 validation states: idle, validating, valid, invalid
   - Real-time API calls to validateMoveBooking() on selection
   - Visual feedback: checkmark (✓) for valid, X for invalid, spinner during validation
   - Displays validation errors inline with bullet points
   - Shows success message "✓ Valid - ready to confirm"
   - Integrates CapacityDisplay component
   - Color-coded borders (green=valid, red=invalid, gray=idle)
   - Disabled state for full/cancelled slots
   - onValidationResult callback for parent coordination

2. **SlotPickerModal** (src/components/bookings/SlotPickerModal.tsx)
   - Full-screen modal for slot selection
   - Groups slots by date (weekday, month, day, year)
   - Sorts dates chronologically
   - Filters out current slot
   - 2-column grid layout for slots
   - Info banner with policy hints (14-day window, product-type, club match)
   - Confirm button disabled until valid slot selected
   - Loading states with spinner
   - Empty state with helpful message
   - Tracks validation state from child tiles

3. **CancelBookingModal** (src/components/bookings/CancelBookingModal.tsx)
   - Confirmation modal for booking cancellation
   - Optional reason textarea (500 char limit)
   - Displays grace period policy (reads from booking.policies or defaults to 15 min)
   - Shows booking details (court, time, attendees count)
   - Warning banner: "This action cannot be undone"
   - Policy info banner with grace period details
   - Two-button layout: "Keep Booking" / "Yes, Cancel Booking"
   - Loading state with spinner

**Features:**
- Live dry_run validation on slot selection (no manual trigger needed)
- Real-time feedback with checkmarks/errors
- Graceful error handling (network failures, validation errors)
- Parameterized error messages from getBookingErrorMessage()
- SSR-safe with proper disabled states
- Professional slate theme matching existing UI
- Responsive grid layouts

**Integration:**
- SlotPickerTile calls validateMoveBooking() API automatically
- SlotPickerModal coordinates validation state
- CancelBookingModal reads policy fields from BookingDetail
- All components ready for integration into BookingDrawer

Related: Phase 3, Booking Admin API v1.1, BUILD #23
1 month ago
Guillermo Pages 13097541f1 feat(bookings): add provider-aware UI components for booking management
continuous-integration/drone/push Build is passing Details
Implemented complete UI foundation for booking drawer with provider gating:

**Components Created:**

1. **ProviderBanner** (src/components/bookings/ProviderBanner.tsx)
   - Displays informational banner for FairPlay clubs
   - "This club is managed by {provider}" messaging
   - Only shows for non-local providers
   - Blue theme with Info icon

2. **CapacityDisplay** (src/components/bookings/CapacityDisplay.tsx)
   - Shows "X / Y booked · Z remaining" format
   - Three variants: default, compact, inline
   - Color-coded availability (red=full, orange=low, gray=available)
   - Responsive text sizing

3. **NotifyPlayersToggle** (src/components/bookings/NotifyPlayersToggle.tsx)
   - Toggle for controlling player notifications
   - Defaults to ON (true)
   - Persists preference to localStorage (key: booking_admin_notify_players)
   - SSR-safe with hydration protection
   - Bell/BellOff icon feedback
   - Exports getStoredNotifyPreference() helper

4. **BookingDrawer** (src/components/bookings/BookingDrawer.tsx)
   - Main booking management UI (360+ lines)
   - Provider-aware: disables actions for FairPlay clubs
   - Displays: time, location, capacity, attendees, booked_by
   - Status badges (confirmed/cancelled/no_show)
   - Attendee type badges (app_user/guest/remote_member)
   - Action buttons: Move, Edit Attendees, Cancel (hidden for FairPlay)
   - ETag support for optimistic concurrency
   - Loading and error states
   - Integrates all sub-components

**Features:**
- Read-only mode for FairPlay bookings (provider.manages_slot_storage === false)
- Capacity display with color-coded availability
- Notification toggle with localStorage persistence
- Professional slate theme matching existing UI
- Fully typed with BookingDetail interface
- Error handling with user-friendly messages

**TODO Placeholders:**
- Move booking flow (button wired, implementation pending)
- Edit attendees flow (button wired, implementation pending)
- Cancel booking flow (button wired, implementation pending)

Related: Phase 3, Booking Admin API v1.1, BUILD #22
1 month ago
Guillermo Pages 71367143c6 feat(bookings): add TypeScript types and API client for Booking Admin v1.1
continuous-integration/drone/push Build is passing Details
Implemented foundation for Phase 3 Booking Admin integration:

**TypeScript Types (src/types/bookings.ts):**
- BookingDetail with nested slot, attendees, provider info
- Request types: CancelBookingRequest, MoveBookingRequest, UpdateAttendeesRequest
- Response types for all operations (cancel, move, attendees)
- MoveDryRunResponse for slot picker validation
- ProviderInfo for FairPlay gating
- 18 booking error codes with user-friendly message mapping
- getBookingErrorMessage() helper with parameterized messages

**API Client (src/lib/api/bookings.ts):**
- getBooking() - GET /admin/bookings/{id} with ETag extraction
- cancelBooking() - PATCH /cancel with ETag + idempotency
- validateMoveBooking() - PATCH /move?dry_run=true for picker
- moveBooking() - PATCH /move with ETag + idempotency
- updateBookingAttendees() - PATCH /attendees with ETag + idempotency

**Features:**
- ETag optimistic concurrency (If-Match header)
- X-Idempotency-Key for retry safety (auto-generated UUID)
- Comprehensive error handling with RFC-7807 format
- Parameterized error messages (grace_minutes, limit_days)
- Network error fallbacks

**Production Config (per Backend Brooke):**
- Cancel grace: 15 min after slot start
- Move window: 14-day limit
- Product-type enforcement (singles ≠ doubles)
- Provider gating (FairPlay = read-only)

Related: Phase 3, Backend Brooke commit 514a3b6, v1.1 contract
1 month ago
Guillermo Pages 1b7497b6d2 fix(admin): duration validation + sport variation display
continuous-integration/drone/push Build is passing Details
Fixed two issues reported by user:

1. Slot definition duration validation error:
   - Changed step from "15" to "1" on duration input
   - HTML5 validation was failing because with min="1" and step="15",
     valid values were 1, 16, 31, 46, 61, 76, 91... (not 90!)
   - Now accepts any integer duration >= 1 minute
   - Error "nearest 76 or 91" resolved

2. Sport variation not showing in court edit form:
   - Updated Court type to include nested sport_variation structure
   - Backend now returns full sport_variation object with sport details
   - Updated CourtFormModal to extract sport_variation_id from nested
     structure: court.sport_variation.sport_variation_id
   - Added sport variation display in court list (e.g., "Padel - Indoor")
   - Maintains backward compatibility with deprecated flat field

Changes:
- SlotDefinitionForm: step="1" instead of step="15"
- Court interface: added optional sport_variation nested object
- ClubCourtsTab: reads sport_variation_id from nested structure
- ClubCourtsTab: displays "Sport - Variation" in court list

Related: Backend Brooke nested sport_variation response structure
1 month ago
Guillermo Pages b7c02c9418 fix(admin): remove blur auto-close + parse settings from GET response
continuous-integration/drone/push Build is passing Details
Fixed two critical issues:

1. Profile form blur bug - ACTUAL FIX:
   - Removed ALL blur auto-close handlers from EditableField component
   - Removed blur auto-close from name and timezone inline inputs
   - Users can now type freely without inputs closing prematurely
   - Form uses Save/Cancel buttons for submission, not blur events

2. Settings parsing from GET response:
   - Backend GET /admin/clubs/{id} returns nested structure with club object
   - Updated getClubProfile to extract club from response.club
   - Settings now properly loaded and displayed from GET response
   - PATCH response already worked (returns club directly)

Root cause of blur bug: blur handlers were closing inputs when field had
content, but this prevented multi-character input. Removed blur logic
entirely since form has explicit Save/Cancel buttons.

Impact: Profile form now fully functional for editing all fields including
settings (address + contact). Settings persist and display correctly.

Related: BUILD #20, Backend Brooke BUILD:297 (settings support)
1 month ago
Guillermo Pages 107f5c6799 fix(admin): resolve click-to-edit blur closure bug in profile form
continuous-integration/drone/push Build is passing Details
Fixed critical bug where EditableField component would close after typing
first character. Root cause was stale closure variable in onBlur handler.

Changes:
- Replace inline onBlur with handleBlur() function that evaluates current
  value at blur time instead of using stale hasValue closure variable
- Fixes issue where typing first char would set hasValue=true in closure,
  causing immediate blur to close input

Impact: Users can now fill in profile fields without being kicked out

Related: BUILD #19
1 month ago
Guillermo Pages 36c5fe2183 feat(admin): add sport variation selection to court creation + fix profile trim crash
continuous-integration/drone/push Build is passing Details
Court Sport Variation (BUILD:296-297 requirement):
- Added Sport and SportVariation types to courts.ts
- Created getSports() API client for GET /admin/sports
- Updated Court and CourtRequest types with sport_variation_id field
- Added sport variation dropdown to court creation modal
- Auto-selects first variation for new courts
- Sport type locked after creation (disabled in edit mode)
- Mock data updated with sport_variation_id values

Profile Bug Fix:
- Fixed click-to-edit crash when field values are empty/undefined
- Added null checks before calling .trim() in hasFieldValue() and EditableField
- Error: "Cannot read properties of undefined (reading 'trim')"
- Now safely handles empty string values

UI Details:
- Sport dropdown shows "Padel - Indoor", "Padel - Outdoor", etc.
- Loading spinner while fetching sports
- Validation error if no sport variation selected
- Form includes both name and sport_variation_id in POST request

Backend Brooke message: Court creation now requires sport_variation_id parameter
1 month ago
Guillermo Pages 47dbb96c47 feat(admin): implement click-to-edit pattern for club profile fields
continuous-integration/drone/push Build is passing Details
UX Improvements:
- Fields with existing data display as read-only text (not inputs)
- Pencil icon appears on hover (right side of field)
- Click text or icon to convert to editable input
- Auto-blur closes edit mode when field has value
- Prevents accidental edits

Implementation:
- Created reusable EditableField component with pencil icon
- Applied to all optional fields: address (5 fields), contact (3 fields)
- Name and timezone use inline click-to-edit (no separate component)
- Edit state tracked per-field with Set<string>
- Form prefills with existing profile data from API

Visual Design:
- Hover: Border changes to slate-300, background to slate-50
- Pencil icon: slate-400, opacity 0→100 on hover
- Maintains existing validation error styling (red borders)
- Consistent with professional slate theme

User Request: "should not be an input directly (put a pen on the side as well as making the text clickable) and on click convert to input (prevents accidental edits)"
1 month ago
Guillermo Pages a61e64ded0 feat(admin): align UI with Backend Brooke Phase 2 contracts, flip mocks to real endpoints
continuous-integration/drone/push Build is passing Details
Phase 2 Integration Updates:
- Profile UI: Read/write address and contact fields from settings.address/settings.contact JSONB structure
- Court dependencies: Updated modal to show 3 counts (slot_definitions, slot_instances_future, slot_instances_booked)
- Error codes: Fixed duplicate court check to use court_name_duplicate (matches backend)
- Mock flags: Flipped all 9 USE_MOCKS flags to false (courts.ts x7, materialisation.ts x2)

Profile Tab (ClubProfileTab.tsx):
- Read address fields from profile.settings?.address?.line_1 etc.
- Read contact fields from profile.settings?.contact?.phone etc.
- Write to settings structure preserving existing settings
- Added Info icon tooltips: "Stored in settings until native fields ship"
- Removed Integration section (provider/remote_club_id not in API)

Court Dependencies Modal (ClubCourtsTab.tsx):
- Display slot_instances_future count (was upcoming_bookings)
- Display slot_instances_booked count (new field)
- Updated deletion instructions for 3-count structure

Types (courts.ts):
- Created ClubProfileSettings interface for JSONB structure
- Updated ClubProfile to use settings instead of flat fields
- Updated CourtDependencies with court_id + 3 dependency counts
- Updated ClubProfileUpdateRequest to support settings

API Clients:
- courts.ts: Updated mock data to match API contracts (settings structure, 3 counts, court_name_duplicate)
- materialisation.ts: Flipped USE_MOCKS to false for real backend integration

Integration Ready:
- All contract mismatches resolved
- Build succeeds
- Ready for Phase 1 smoke tests against BUILD:290-291

Related: Backend Brooke BUILD:290 (courts CRUD) + BUILD:291 (club profile PATCH)
Contract references: docs/owners/payloads/court-api-contract.md, club-profile-api-contract.md
1 month ago
Guillermo Pages cb255cf1f3 fix(api): remove fallback URLs, throw if env var not defined
continuous-integration/drone/push Build is failing Details
Per user feedback and Chief Cole direction (Chief_Cole-20251107123442):
- Remove fallback URLs from all API clients
- Throw error if NEXT_PUBLIC_PYTHON_API_URL not defined
- Standardize on NEXT_PUBLIC_PYTHON_API_URL env var across all clients
- Production URL: https://api.playchoo.com (staging/prod share hostname)

Updated files:
- src/lib/api/courts.ts
- src/lib/api/materialisation.ts
- src/lib/api/slot-definitions.ts

All clients now fail fast with clear error if env var missing.
Build tested and passed.
1 month ago
Guillermo Pages 9a9c3a3b95 feat(admin): add court management UI with Profile/Courts tabs (Phase 2)
continuous-integration/drone/push Build is passing Details
Phase 2 implementation per Chief Cole approval (Chief_Cole-20251107103829)

Club Detail Page Restructure:
- Replaced single-view club detail with tabbed interface
- Three tabs: Profile, Courts, Slot Definitions (links to existing page)
- Tab navigation with active state highlighting
- Breadcrumb navigation maintained

Profile Tab (ClubProfileTab):
- Full club metadata editing form with sections:
  - Basic Information: name (required), timezone (required, IANA dropdown)
  - Location: address line 1/2, city, postal code, country (all optional)
  - Contact: phone, email (validated), website (validated) (all optional)
  - Integration: provider, remote club ID (read-only for Phase 2)
- Client-side validation: required fields, email format, URL format
- Server-side validation: maps backend errors to fields
- Success/error messaging with auto-hide success toast
- Cancel button resets form to original values
- Professional slate theme form styling

Courts Tab (ClubCourtsTab):
- Court list view with empty state
- Add/edit/delete actions per court
- Add Court modal: single field (name), duplicate name validation
- Edit Court modal: pre-populated, same validation
- Delete with cascade blocking:
  - Dependency check before delete (slot definitions, upcoming bookings)
  - Blocking modal shows dependency counts with resolution steps
  - Confirmation modal for clean delete (no dependencies)
- In-memory mock data management (3 courts default)
- Court 1 has mock dependencies (12 slot defs, 45 bookings) for testing

Modal Components:
- CourtFormModal: Add/edit with field-level validation
- DeleteConfirmationModal: Confirm before deletion
- DependenciesBlockingModal: Shows why delete is blocked + resolution steps

All components use professional slate theme
All modals use backdrop blur + center positioning
All forms have loading states + error handling
Build tested and passed (npm run build successful)

Total new lines: ~1400 across 3 components
Ready for mock-driven testing while Backend Brooke builds APIs
1 month ago
Guillermo Pages 251b849500 feat(admin): add materialisation trigger/status UI + court management infrastructure
continuous-integration/drone/push Build is passing Details
Phase 1 continuation + Phase 2 preparation per Chief Cole request (Chief_Cole-20251107102650)

Materialisation UI (Phase 1 continuation):
- Created MaterialisationStatusPanel component with 5 states (never run, success, running, failed, rate limited)
- Polling strategy: 3-second intervals while job running
- Rate-limit countdown timer with live updates
- Idempotency key generation (UUID v4) for manual triggers
- Mock data for development with state cycling
- Integrated into slot definitions page between header and table
- TypeScript types: MaterialisationStatus, MaterialisationTriggerRequest/Response, helpers
- API client with mock implementation (USE_MOCKS flag)
- Professional slate theme consistent with existing UI

Court Management Infrastructure (Phase 2 preparation):
- Created TypeScript types for courts and club profile
- Court types: Court, CourtRequest, CourtDependencies
- Profile types: ClubProfile, ClubProfileUpdateRequest
- Common timezones list (40+ IANA zones)
- Validation helpers (email, URL)
- API client with full CRUD mocks:
  - getClubProfile, updateClubProfile
  - getCourts, createCourt, updateCourt, deleteCourt
  - getCourtDependencies (cascade blocking)
- Mock data: 3 courts, full profile, dependency simulation
- In-memory mock state management

Implementation ready for:
- Next: Club detail tab navigation (Profile/Courts/Slot Definitions)
- Next: Profile tab with edit form
- Next: Courts tab with add/edit/delete modals

Build tested and passed (npm run build successful)
1 month ago
Guillermo Pages 478d43b44a feat(slot-definitions): add create/edit form with validation
continuous-integration/drone/push Build is passing Details
Implement full CRUD form modal for slot definitions with:
- All fields: court, day, time, duration, capacity, date range, description
- Field-level validation with inline error messages
- API contract alignment (HH:MM:SS, 0=Monday format, optional valid_to)
- Overlap detection (409 handling)
- No end date checkbox
- Edit mode support
- Professional slate theme
- Loading states
- Delete functionality integrated

Form validates:
- Required fields
- Duration must be positive
- Capacity must be positive
- End date must be after start date
- Slot cannot extend past midnight
- Backend validation errors shown inline

Fully functional create/edit/delete flow ready for backend integration.
1 month ago
Guillermo Pages a8ad9fed51 feat(slot-definitions): implement Phase 1 slot definition table UI
continuous-integration/drone/push Build is passing Details
Add TypeScript types, API client, and table view for slot definitions.
Uses mock data until backend endpoints are ready.

Components:
- SlotDefinitionsComponent: table with create/edit/delete actions
- TypeScript types matching API contract specs
- API client with CRUD operations (POST/GET/PATCH/DELETE)
- Mock data for local development

Features:
- Professional slate theme throughout
- Empty state with CTA
- Table showing: day, time range, duration, capacity, valid period, description
- Edit/delete action buttons (TODOs for modals)
- Loading and error states with proper UX
- Breadcrumb navigation back to club

Contract alignment:
- Day format: 0=Monday, 6=Sunday
- Time format: HH:MM:SS with display helpers
- Date range: valid_from required, valid_to optional
- RFC-7807 error handling ready

Next: Create/edit form modal with validation
1 month ago
Guillermo Pages b3778b22af feat(design): extend professional slate theme throughout manager portal
continuous-integration/drone/push Build is passing Details
Extended the professional slate gray color scheme from the landing page
to all admin components and navigation. Replaced all purple/pink/indigo
gradients with clean slate-900/600/200 colors for a timeless,
enterprise-focused aesthetic.

Changes:
- Updated AdminClubsList.tsx with slate theme
- Updated AdminClubDetail.tsx with slate theme
- Updated AdminAuthGuard.tsx loading states with slate colors
- Updated Navigation.tsx header, menu items, and footer with slate theme
- Updated layout.tsx body and footer with slate colors
- Added ~30 new translation keys to dictionaries/en.json

Design system:
- Primary: slate-900 (almost black)
- Text: slate-900 (headings), slate-600 (body), slate-500 (muted)
- Borders: slate-200 (2px), slate-300 on hover
- Backgrounds: white, slate-50, slate-100
- Professional rounded corners: rounded-2xl, rounded-xl, rounded-lg
- Clean typography: tracking-tight, font-bold/medium/light
1 month ago
Guillermo Pages af1f119906 fix(landing): professional redesign with proper translations and timeless aesthetic
continuous-integration/drone/push Build is passing Details
Fixed translation issues and replaced colorful gradients with sophisticated,
timeless design suitable for enterprise venue management platform.

Changes:
1. Translations Fixed:
   - Removed all untranslated strings causing blank text
   - Only using existing translation keys (e.g., t('Login'))
   - All text now hardcoded in English (proper i18n to be added later)

2. Professional Design System:
   - Removed purple/pink gradients
   - Clean slate gray color palette (slate-50 to slate-900)
   - Minimal use of color - black/white/gray focused
   - Subtle gradients only where needed (slate-50 to white)

3. Timeless Layout:
   - Clean typography with proper hierarchy
   - Generous white space
   - Elegant borders and shadows
   - Professional card-based layout
   - Trust indicators with stats (Real-time, 24/7, Secure, Multi-venue)

4. Enterprise Aesthetic:
   - Sophisticated, not playful
   - Focus on efficiency and professionalism
   - Clear value propositions
   - Business-focused copy

Result: Landing page conveys class and professionalism appropriate for
venue management platform used by serious business operators.
1 month ago
Guillermo Pages 8e0bbd58d9 feat(landing+auth): add marketing landing page and admin auth guards
continuous-integration/drone/push Build is passing Details
Per Chief Cole's guidance (Chief_Cole-20251107080515), enhanced manager
portal with showcase landing and auth protection.

Changes:
1. Marketing Landing Page:
   - Hero section with gradient backgrounds and value props
   - Feature tiles showcasing club management, scheduling, booking insights
   - CTA buttons linking to login
   - Marketing copy focused on venue admin benefits
   - Responsive design matching consumer app style

2. Auth Guards for /admin Routes:
   - Created AdminAuthGuard component using useSwissOIDAuth hook
   - Redirects unauthenticated users to login immediately
   - Shows loading state during auth check
   - Preserves locale in redirect flow
   - Wrapped all /admin/clubs pages with guard

3. Protected Routes:
   - /admin/clubs - guarded
   - /admin/clubs/[club_id] - guarded
   - / - public landing (no guard needed)

Result: Unauthenticated users see marketing showcase at root, and are
redirected to login if they try to access /admin routes directly.

Refs: docs/owners/Frontend_Faye-needs-to-read-from-Chief_Cole-20251107080515.md
1 month ago
Guillermo Pages 4df827bce6 fix(admin): use useTranslation hook in client components
continuous-integration/drone/push Build is passing Details
Build #6 failed because client components were trying to receive
translations as props from server components, which breaks Next.js
serialization boundaries.

Fix: Use useTranslation() hook directly in client components to access
translations context, matching the pattern used in consumer app.

Changes:
- AdminClubsList: Remove locale/t props, use useTranslation() hook
- AdminClubDetail: Remove locale/t props, use useTranslation() hook
- Simplified page.tsx wrappers to not pass translations

This matches the consumer app pattern and allows proper SSR/client
component boundaries.
1 month ago
Guillermo Pages 9f47dab8d8 refactor(admin): convert to client-side rendering and add missing routes
continuous-integration/drone/push Build is failing Details
Responding to Chief Cole's feedback after validation of BUILD:283:
- Manager portal should match consumer app pattern (client-side fetching)
- Fix 404 errors for /dashboard and /admin/clubs/[id]
- Ensure locale-prefixed routing works smoothly

Changes:
1. Client-side rendering for admin views:
   - Converted /admin/clubs list to use 'use client' pattern
   - Created AdminClubsList.tsx component with useEffect data fetching
   - Removed SSR cookie forwarding (no longer needed for client-side)

2. Added club detail page:
   - Created /admin/clubs/[club_id] route with AdminClubDetail.tsx
   - Client-side fetching with proper loading/error states
   - Support for 401/403/404 error handling with user-friendly messages
   - Breadcrumb navigation back to clubs list

3. Added dashboard route:
   - Created /dashboard that redirects to /admin/clubs
   - Resolves post-login 404 error reported in testing

4. Locale handling:
   - Middleware automatically adds locale prefix to all routes
   - Internal links include locale parameter to avoid extra redirects
   - /dashboard → /en-US/dashboard → /en-US/admin/clubs

Result: Manager portal now follows consumer app patterns with full
client-side data fetching, proper error handling, and complete routing.

Refs: docs/owners/Frontend_Faye-needs-to-read-from-Chief_Cole-20251107073302.md
1 month ago
Guillermo Pages 51a928c1f4 fix(admin): forward cookies in SSR for admin/clubs endpoints
continuous-integration/drone/push Build is passing Details
Root cause: Next.js server-side rendering was making API calls without
forwarding browser cookies, causing AUTHORIZATION_ERROR during page load.

Changes:
- Updated getAdminClubs() to accept optional cookieHeader parameter
- Updated getAdminClubDetail() to accept optional cookieHeader parameter
- Modified /admin/clubs page to extract cookies via next/headers and
  forward them to API calls during SSR
- Use credentials: 'omit' when manually setting Cookie header to avoid
  fetch() conflicts

Result: Admin endpoints now authenticate properly during SSR, allowing
page to render club list correctly instead of showing error state.

Resolves: SSR authorization error reported in BUILD:283 regression test
1 month ago
Guillermo Pages ca2a1a8f88 fix(middleware): handle invalid locale values in Accept-Language header
continuous-integration/drone/push Build is passing Details
Added error handling to prevent middleware crashes when browsers send
invalid locale codes. Filters out malformed locale values before passing
to Intl.getCanonicalLocales, with fallback to default locale if all
values are invalid.

Fixes: RangeError: Incorrect locale information provided
1 month ago
Guillermo Pages 26a1975799 fix(auth): update manager app to use dedicated auth backend
continuous-integration/drone/push Build is passing Details
Updated SwissOID configuration to use manager-specific auth backend:
- NEXT_PUBLIC_AUTH_BACKEND_URL: https://manager-auth.api.playchoo.com
- NEXT_PUBLIC_SWISSOID_TARGET_SERVICE_HANDLE: playchoo-manager

This separates manager authentication from consumer app authentication
as required by the architecture. Staging uses staging.manager-auth.api.playchoo.com.
1 month ago