41 Commits (256eccc99711d8b8013a4fe649ccda13b06f51ba)

Author SHA1 Message Date
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
2 months 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.
2 months 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
2 months 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.)
2 months 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.
2 months 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.
2 months 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.
3 months 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.
3 months 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
3 months 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
3 months 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
3 months 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
3 months 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
3 months 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.
3 months 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.
3 months 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.
3 months 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
3 months 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).
3 months 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.
3 months 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.
3 months 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
3 months 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.
3 months 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.
3 months 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
3 months 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
3 months 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)
3 months 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
3 months 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
3 months 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)"
3 months 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
3 months 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
3 months 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)
3 months 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.
3 months 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
3 months 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
3 months 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.
3 months 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
3 months 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.
3 months 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
3 months 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
3 months ago
Guillermo Pages c275bf1dcc feat(phase-0): integrate admin clubs API with staging backend
Phase 0 venue admin integration complete:
- Implemented GET /admin/clubs API integration
- Three-state response handling: 401 (auth), empty (no access), success (club list)
- RFC-7807 error handling with proper error display
- Club cards with navigation to detail pages
- Staging environment configuration (.env.staging)
- TypeScript interfaces aligned with VENUE_ADMIN_DESIGN.md

Integration points:
- API base URL: https://staging.api.playchoo.com
- Endpoints: /admin/clubs, /admin/clubs/{club_id}
- Auth: Cookie-based SwissOID (handled by API)
- Stub mode: Clubs 3 (Central Padel) and 7 (Riverside Tennis)

Files changed:
- src/app/[locale]/admin/clubs/page.tsx: API integration
- src/lib/api/admin-clubs.ts: API client with mock data
- src/types/admin-api.ts: TypeScript interfaces
- .env.staging: Staging configuration

Ready for:
- Live testing against staging stubs
- E2E tests with admin accounts (pending Access Avery seeding)

Refs: VENUE_ADMIN_DESIGN.md, VENUE_ADMIN_EXECUTION_PLAN.md
3 months ago