16 KiB
Phase 1: Slot Definition Management - Wireframes & Form States
Owner: Frontend Faye Created: 2025-11-05 Status: Draft for review
Overview
Phase 1 enables venue admins to create, edit, and manage recurring slot templates (slot definitions). These templates are materialized into actual slot instances by Cron Carter's materialisation job.
User Story: As a venue admin, I want to create recurring time slots for my courts so that customers can book them automatically without manual intervention.
Page Structure
Route: /[locale]/admin/clubs/[club_id]/slot-definitions
Navigation from Club Detail:
- Club detail page → "Manage Schedules" button → Slot Definitions page
Layout:
┌─────────────────────────────────────────────────────────┐
│ [Back to Club] Central Padel Geneva │
├─────────────────────────────────────────────────────────┤
│ │
│ Slot Definitions │
│ Recurring time slots for your courts │
│ │
│ [+ Create New Definition] [Filter ▼] │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Court 1 - Mondays 18:00-19:00 │ │
│ │ Capacity: 4 players | Valid from: 2025-11-01 │ │
│ │ Status: ● Active │ │
│ │ [Edit] [Delete]│ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Court 2 - Tuesdays 19:00-20:00 │ │
│ │ Capacity: 4 players | Valid from: 2025-11-01 │ │
│ │ Status: ● Active │ │
│ │ [Edit] [Delete]│ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
Create/Edit Form Modal
Form Structure
┌────────────────────────────────────────────────────┐
│ Create Slot Definition [×] │
├────────────────────────────────────────────────────┤
│ │
│ Court * │
│ [Select court ▼] │
│ │
│ Day of Week * │
│ [Monday ▼] │
│ │
│ Start Time * │
│ [18] : [00] (24-hour format) │
│ │
│ Duration * │
│ [60] minutes │
│ │
│ Capacity * │
│ [4] players │
│ │
│ Valid From * │
│ [2025-11-15] 📅 │
│ │
│ Valid To (optional) │
│ [No end date] 📅 │
│ │
│ ───────────────────────────────────────────── │
│ │
│ [Cancel] [Create Definition] │
│ │
└────────────────────────────────────────────────────┘
Form States & Validation
Field Definitions
Court (Required)
- Type: Dropdown select
- Options: List of active courts for the club
- Validation: Must select a court
- Error: "Please select a court"
Day of Week (Required)
- Type: Dropdown select
- Options: Monday (1), Tuesday (2), ..., Sunday (0)
- Validation: Must select a day
- Error: "Please select a day of week"
Start Time (Required)
- Type: Time input (HH:MM)
- Format: 24-hour
- Validation:
- Must be valid time (00:00 - 23:59)
- Should align with typical business hours warning (e.g., warning if before 06:00 or after 23:00)
- Error: "Please enter a valid start time"
- Warning: "⚠️ This time is outside typical business hours (06:00-23:00)"
Duration (Required)
- Type: Number input
- Options: Preset buttons: [30 min] [60 min] [90 min] [120 min] or custom
- Validation:
- Must be > 0
- Must be <= 480 minutes (8 hours)
- Recommended: 30, 60, 90, 120 minutes
- Error: "Duration must be between 1 and 480 minutes"
- Warning: "⚠️ Non-standard duration. Most bookings are 60 or 90 minutes."
Capacity (Required)
- Type: Number input
- Default: 4 (for padel/tennis doubles)
- Validation: Must be between 1 and 8
- Error: "Capacity must be between 1 and 8 players"
Valid From (Required)
- Type: Date picker
- Default: Tomorrow's date
- Validation:
- Must be today or future date
- Cannot be before today
- Error: "Valid from date cannot be in the past"
Valid To (Optional)
- Type: Date picker or "No end date" checkbox
- Validation:
- If set, must be after "Valid From"
- Warning if > 1 year in future
- Error: "Valid to must be after valid from date"
- Warning: "⚠️ Definition valid for more than 1 year. Consider reviewing periodically."
Form Validation States
1. Empty / Initial State
- All fields empty or default values
- Submit button disabled
- No error messages shown
2. Filling Out (In Progress)
- Fields being completed
- Real-time validation on blur
- Submit button disabled until all required fields valid
- Show field-level errors on blur
3. Valid State
- All required fields filled and validated
- No errors shown
- Submit button enabled and highlighted
- Preview of materialization impact shown
4. Error State
- One or more validation errors
- Error messages shown under relevant fields
- Submit button disabled
- Focus on first error field
5. Conflict State (Backend Error)
- Form submitted but backend returns conflict
- Error banner: "⚠️ A definition already exists for Court 1 on Mondays at 18:00"
- Suggestion: "Edit the existing definition or choose a different time"
- Form fields remain editable
6. Submitting State
- Submit button shows loading spinner
- Form fields disabled
- Cannot close modal
- Loading text: "Creating definition..."
7. Success State
- Brief success message: "✓ Slot definition created successfully"
- Auto-close modal after 1 second
- Refresh slot definitions list
- Optional: Show materialisation job status
Materialisation Status Indicator
After creating/editing a definition, show materialisation status:
┌────────────────────────────────────────────────────┐
│ ✓ Definition created successfully │
│ │
│ Materialisation Status: │
│ ⏳ Slots are being generated... (Job #12345) │
│ │
│ Expected completion: 2 minutes │
│ [View Job Details] │
└────────────────────────────────────────────────────┘
States:
- Queued: ⏳ Materialisation job queued (position #3)
- Running: ⏳ Generating slots... (45% complete)
- Complete: ✓ 24 new slots created for next 7 days
- Error: ⚠️ Materialisation failed (contact support)
Link to Cron Carter: This requires coordination with Cron Carter for job status API.
Delete Confirmation Modal
┌────────────────────────────────────────────────────┐
│ Delete Slot Definition? [×] │
├────────────────────────────────────────────────────┤
│ │
│ Are you sure you want to delete: │
│ │
│ 📍 Court 1 - Mondays 18:00-19:00 │
│ Valid from: 2025-11-01 │
│ │
│ ⚠️ This will not delete existing bookings, │
│ but future slots will not be generated. │
│ │
│ [Cancel] [Delete Definition] │
│ │
└────────────────────────────────────────────────────┘
Filter & Sort Options
Filter Dropdown
- All Courts
- By Court (Court 1, Court 2, etc.)
- Active Only
- Inactive Only
- By Day of Week
Sort Options
- Court (A-Z)
- Day of Week (Mon-Sun)
- Start Time (Early-Late)
- Created Date (Newest-Oldest)
Empty States
No Definitions Yet
┌────────────────────────────────────────────────────┐
│ │
│ 📅 │
│ │
│ No slot definitions yet │
│ │
│ Create recurring time slots so customers can │
│ book your courts automatically. │
│ │
│ [+ Create First Definition] │
│ │
└────────────────────────────────────────────────────┘
No Results (After Filter)
┌────────────────────────────────────────────────────┐
│ │
│ 🔍 │
│ │
│ No definitions match your filters │
│ │
│ Try adjusting your filter criteria. │
│ │
│ [Clear Filters] │
│ │
└────────────────────────────────────────────────────┘
Mobile Responsive Considerations
Card Stacking (< 768px)
- Definition cards stack vertically
- Edit/Delete buttons move to card footer
- Form modal takes full screen
- Time picker uses native mobile inputs
Touch Targets
- Minimum 44px × 44px for all interactive elements
- Larger tap areas for primary actions
- Swipe to delete on mobile (optional)
Accessibility
- All form fields have proper
<label>associations - Error messages use
aria-describedby - Form validation uses
aria-invalid - Focus management: First field on modal open, first error on validation fail
- Keyboard navigation: Tab order follows visual order
- Screen reader announcements for success/error states
API Integration Points
Based on VENUE_ADMIN_DESIGN.md:
GET /admin/clubs/{club_id}/slot-definitions
- Load existing definitions for display
- Filter by
court_idoractive_ondate
POST /admin/clubs/{club_id}/slot-definitions
Request Body:
{
"court_id": 12,
"dow": 1,
"starts_at": "18:00:00",
"duration_minutes": 60,
"capacity": 4,
"valid_from": "2025-11-15",
"valid_to": null
}
PATCH /admin/clubs/{club_id}/slot-definitions/{id}
Request Body: Same as POST (partial updates allowed)
Headers: If-Match: "<etag>" for optimistic concurrency
DELETE /admin/clubs/{club_id}/slot-definitions/{id}
Headers: If-Match: "<etag>"
Technical Implementation Notes
Component Structure
/admin/clubs/[club_id]/slot-definitions/
├── page.tsx # Main list page
├── components/
│ ├── DefinitionCard.tsx # Individual definition card
│ ├── DefinitionForm.tsx # Create/edit modal form
│ ├── DeleteConfirm.tsx # Delete confirmation modal
│ ├── FilterBar.tsx # Filter/sort controls
│ └── MaterialisationStatus.tsx # Job status indicator
State Management
- Use React useState for form state
- Consider useReducer for complex form validation
- Optimistic updates for delete (rollback on error)
- Polling or WebSocket for materialisation status updates
Form Libraries (Recommendation)
- React Hook Form: For form state management
- Zod: For schema validation matching API contracts
- Or plain React state for simplicity (Phase 1 is small)
Open Questions for Cron Carter
- Materialisation Job Status API: What endpoint do we call to check job status?
- Real-time Updates: WebSocket, polling, or manual refresh?
- Job Failure Handling: What UI should we show if materialisation fails?
- Manual Trigger: Should admins be able to manually trigger materialisation?
Next Steps
- Review with Chief Cole: Confirm this matches vision
- Share with Cron Carter: Align on materialisation status display
- Implementation: After Phase 0 integration complete
- Design Review: Share with Ops Omar for pilot feedback
Status: Ready for review and implementation planning.