71 Commits (master)
 

Author SHA1 Message Date
Guillermo Pages ff96b5807c Fix registration approve/reject endpoints and display
continuous-integration/drone/push Build is passing Details
- Fix approve/reject API URLs (add /competitions prefix, send empty JSON body)
- Fix pending count in HUD (use pending_registrations field)
- Fix registration name display (show member names for individuals)
- Add pending_registrations to Competition type
1 week ago
Guillermo Pages c9f153c345 Add DELETE endpoint for competitions (draft/cancelled only)
continuous-integration/drone/push Build is passing Details
1 week ago
Guillermo Pages 447b2125a8 feat: add default stage configs when creating competitions from scratch
When creating a competition without a template, the modal now provides
sensible default configurations for each competition type:

- league: round_robin stage with standard points (3/1/0)
- tournament: single_elimination stage
- challenge: rating_delta_challenge stage with sum metric (leaderboard)
- hybrid: group stage + knockout stage

This ensures stages are created automatically instead of leaving
competitions with 'No stages configured yet'.
1 week ago
Guillermo Pages fbf4bdd55b fix: update remaining navigation links from /admin/clubs to /admin/facilities 1 week ago
Guillermo Pages 2899e1fd58 refactor: rename clubs to facilities in admin routes
- Rename /admin/clubs to /admin/facilities
- Rename [club_id] param to [facility_id]
- Update all component names: Club* -> Facility*
- Update CompetitionCard to use correct admin route with locale
- Keep API function names as-is (getAdminClubDetail) since they match backend
1 week ago
Guillermo Pages b91f2d47bc fix: add missing scope and config_snapshot fields to createCompetition
Backend requires scope and config_snapshot when creating competition
without a template. Frontend now:
- Defaults scope to 'facility' when facility_id is provided
- Renames config to config_snapshot for backend compatibility
- Provides empty config_snapshot when no template_id
1 week ago
Guillermo Pages 2a71371683 feat: complete competition manager UI with scheduling, results, and series
continuous-integration/drone/push Build is passing Details
- Add SchedulingTab with SlotPickerModal for linking matches to court slots
- Add ResultsTab with dispute management (list, resolve, reject disputes)
- Add Series management pages (list, create with RRULE configuration)
- Add SaveAsTemplateModal to save competition config as reusable template
- Add GroupAssignmentModal for assigning participants to groups
- Enable approve button for waitlisted registrations
- Add dispute types, API functions, and query/mutation hooks
- Add series types, API functions, and query/mutation hooks
1 week ago
Guillermo Pages 5e020aa4eb Update competition API routes to use /competitions prefix
continuous-integration/drone/push Build is passing Details
Harmonize all competition-related API endpoints to use plural
/competitions prefix for consistency with REST conventions.
1 week ago
Guillermo Pages e6a10f7290 feat: add competition management UI for facility managers
continuous-integration/drone/push Build is passing Details
Add comprehensive competition management features:

Types & API:
- competition.ts: Type definitions for competitions, templates, stages, etc.
- competition-admin.ts: API client for all competition endpoints
- useCompetitionQueries.ts: React Query hooks for data fetching
- useCompetitionMutations.ts: Mutation hooks for write operations

Components:
- CompetitionStatusBadge, CompetitionTypeBadge for visual indicators
- CompetitionCard, CompetitionList for displaying competitions
- Skeleton components for loading states

Pages:
- /competitions: List competitions with status/type filtering
- /competitions/templates: Manage reusable competition templates
- /competitions/[id]: Detail view with tabs for:
  - Overview: Competition details and configuration
  - Registrations: Approve/reject registration requests
  - Participants: Manage seeds and withdrawals
  - Fixtures: Generate and manage match fixtures
  - Standings: View standings tables and leaderboards

Navigation:
- Added Competitions tab to ClubTabNavigation
1 week ago
Guillermo Pages f39e3542ed refactor: add TanStack Query and centralized abstractions
continuous-integration/drone/push Build is passing Details
- Install @tanstack/react-query and devtools
- Add QueryProvider to app providers
- Create query hooks: useBookings, useClubs, useClubMembers, useClubPlans, useClubCredits, useClubPolicy
- Create mutation hooks: useMemberMutations, usePlanMutations
- Add ModalFormWrapper component for consistent form modals
- Centralize skeleton configuration (sizes, shapes) in skeletonConfig.ts
- Add error handling utilities (getApiErrorMessage, isApiError)
- Add size configuration utilities for consistent component sizing
- Update AvatarSkeleton, IconSkeleton, PlayerItemSkeleton to use centralized config
2 weeks ago
Guillermo Pages 256eccc997 refactor: add shared club detail layout with persistent tab navigation
continuous-integration/drone/push Build is passing Details
- Create ClubTabNavigation component with responsive horizontal scroll
- Create ClubDetailHeader component with shared layout (breadcrumb, header, tabs)
- Add layout.tsx for club_id route to wrap all sub-pages
- Extract Courts tab into separate page route
- Remove redundant AdminAuthGuard from individual pages (now in layout)
- Remove container wrappers from components (now in shared header)
- Remove "Back to club" links (breadcrumb now in header)
- Standardize colors: gray-* to slate-* for consistent dark text styling
3 weeks ago
Guillermo Pages 308d9d70bf feat: add slot instances management UI
continuous-integration/drone/push Build is passing Details
Add complete admin UI for managing individual slot instances:

Components:
- SlotInstancesComponent: day view with date navigation and filters
- SlotInstanceEditModal: edit times, capacity, convert to manual
- ManualSlotModal: create one-off manual slots

Features:
- Date picker with prev/next day navigation
- Filter by court and show/hide cancelled slots
- Group slots by court in table view
- Status badges (available, pending, booked, cancelled)
- Origin badges (template, manual, maintenance)
- Convert definition-based slots to manual (detach from materializer)
- Create manual slots for special events
- Edit slot times/capacity (protected when has bookings)
- Cancel and delete slots with confirmation

API Client:
- getSlotInstances, createSlotInstance, updateSlotInstance, deleteSlotInstance
- cancelSlotInstance, convertToManualSlot helpers

Types:
- SlotInstance, SlotInstancesResponse
- CreateSlotInstanceRequest, UpdateSlotInstanceRequest
- Helper functions for formatting and status colors

Also adds "Slot Instances" tab to ClubDetailTabs navigation.
3 weeks ago
Guillermo Pages 7055eb2f43 feat: add admin UI for credits, transfers, and plan entitlements
continuous-integration/drone/push Build is passing Details
- Add PlanEntitlementsEditor component for Pay-Per-Court settings
- Add EntitlementsConfigModal for editing plan entitlements
- Update PlanCard with entitlements badges and configure button
- Update MembershipPlansComponent to fetch/display entitlements
- Create credits management page with balance list and adjustment modal
- Create transfers management page with filtering and stats
- Add Credits and Transfers tabs to ClubDetailTabs navigation
- Add API client functions for credits and transfers endpoints
- Update facility-admin types with credit and transfer interfaces
3 weeks ago
Guillermo Pages 9f059bcbfe feat: add plan template picker for membership plan creation
continuous-integration/drone/push Build is passing Details
- Add PlanTemplate type and listPlanTemplates API function
- Create TemplateCard and TemplatePicker components
- Modify PlanFormModal to accept initialValues from template
- Update MembershipPlansComponent to show template picker first
- Add missing translations (Venue Management, Club Management, etc.)
3 weeks ago
Guillermo Pages 826c42442e fix: handle 204 No Content in DELETE responses
continuous-integration/drone/push Build is passing Details
DELETE endpoints (deletePlan, deleteEntitlement, deleteMember) return
204 with empty body. handleApiResponse was trying to parse JSON from
empty response, causing: 'Unexpected end of JSON input'

Fix: Check for status 204 before calling response.json()
Return { success: true, data: undefined } for 204 responses

This resolves console error while maintaining successful delete operations.
3 weeks ago
Guillermo Pages d29bf7884d fix: update billing period to match backend validation
continuous-integration/drone/push Build is passing Details
Backend only accepts: 'monthly', 'annual', or null
Frontend was offering: daily, weekly, monthly, quarterly, yearly, lifetime

Changes:
- Update BillingPeriod type to 'monthly' | 'annual' | null
- Update PlanFormModal dropdown to only show valid options
- Update PlanCard formatBillingPeriod to handle new values
- Display 'Annual (Yearly)' in UI but send 'annual' to API

Fixes validation error: 'Must be monthly, annual, or null'
3 weeks ago
Guillermo Pages 7c1c3a7568 feat: add admin context support and fix API type mismatches
continuous-integration/drone/push Build is passing Details
Add admin context API client:
- Create /admin/context types (ManagedFacility, AdminSettings)
- Add getAdminContext() API client function
- Separate admin context from player context

Fix API response type mismatches:
- UserSettings: API uses 'origin' terminology, map to internal 'remote'
- Fix transformApiSettings to use default_origin_sport/default_origin_member_id
- Update UserSettingsContext to read origin_members from API
- Fix getPolicy to handle direct object response (not wrapped)
- Fix listPlans/listMembers to extract nested arrays from API responses

These fixes ensure TypeScript types match actual API responses.
3 weeks ago
Guillermo Pages cc29ac453c fix: extract nested properties from list API responses
continuous-integration/drone/push Build is passing Details
Backend returns { plans: [...] } and { members: [...] } for list
endpoints, not direct arrays. Update listPlans() and listMembers()
to extract the nested property from the response.

Fixes:
- TypeError: t.map is not a function on Plans page
- TypeError: l is not iterable on Members page
3 weeks ago
Guillermo Pages af79b9a871 fix: add validation and diagnostics for API settings transform
continuous-integration/drone/push Build is passing Details
Add explicit checks for required TranslatedField wrapper structure
and throw descriptive errors with actual API response data when
fields are missing. This will help diagnose why the user context
API is failing despite returning 200 status.
3 weeks ago
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