71 Commits (master)
 

Author SHA1 Message Date
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
Guillermo Pages 95b32e5664 chore: bump deploy number to 8
continuous-integration/drone/push Build is passing Details
1 month 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
1 month ago