continuous-integration/drone/push Build is passingDetails
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
continuous-integration/drone/push Build is passingDetails
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)
continuous-integration/drone/push Build is passingDetails
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
continuous-integration/drone/push Build is passingDetails
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
continuous-integration/drone/push Build is passingDetails
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)"
continuous-integration/drone/push Build is failingDetails
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.
continuous-integration/drone/push Build is passingDetails
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
continuous-integration/drone/push Build is passingDetails
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.
continuous-integration/drone/push Build is passingDetails
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
continuous-integration/drone/push Build is passingDetails
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
continuous-integration/drone/push Build is passingDetails
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.
continuous-integration/drone/push Build is passingDetails
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
continuous-integration/drone/push Build is passingDetails
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.
continuous-integration/drone/push Build is failingDetails
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
continuous-integration/drone/push Build is passingDetails
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
continuous-integration/drone/push Build is passingDetails
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
continuous-integration/drone/push Build is passingDetails
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.