# Booking Page Documentation ## ๐Ÿ“š Overview This folder contains comprehensive documentation for the WeChat Mini-Program booking system in the mp-pilates project (Uni-app + Vue 3). ### ๐Ÿ“„ Documentation Files 1. **BOOKING_PAGE_ANALYSIS.md** โญ START HERE - Complete file-by-file breakdown of all components - Data flow diagrams - API contract documentation - Color scheme and styling details - Potential issues and problems 2. **COMPONENT_HIERARCHY.md** - Visual component tree structure - State management flow (Pinia stores) - API sequence diagrams - State machine for slot cards - Data transformations 3. **QUICK_REFERENCE.md** - Code snippets for quick lookup - Debugging tips and console commands - Common issues and solutions - Debugging checklist - API examples --- ## ๐ŸŽฏ Quick Navigation ### I want to understand... **...the overall flow** โ†’ Read: BOOKING_PAGE_ANALYSIS.md โ†’ "Complete Data Flow Diagram" section **...how the UI is structured** โ†’ Read: COMPONENT_HIERARCHY.md โ†’ "Component Tree" + "UI Layout Breakdown" **...where specific code is** โ†’ Read: QUICK_REFERENCE.md โ†’ "Finding Specific Things" **...how to debug an issue** โ†’ Read: QUICK_REFERENCE.md โ†’ "Common Issues & Solutions" **...the API contracts** โ†’ Read: QUICK_REFERENCE.md โ†’ "API Contract Summary" **...the store state** โ†’ Read: COMPONENT_HIERARCHY.md โ†’ "State Management Flow" --- ## ๐Ÿ—๏ธ Project Structure ``` packages/app/src/ โ”œโ”€โ”€ pages/ โ”‚ โ””โ”€โ”€ booking/ โ”‚ โ””โ”€โ”€ index.vue # Main booking page (311 lines) โ”œโ”€โ”€ components/ โ”‚ โ”œโ”€โ”€ DateSelector.vue # Date picker (50 lines) โ”‚ โ”œโ”€โ”€ TimePeriodFilter.vue # Time period filter (50 lines) โ”‚ โ”œโ”€โ”€ SlotCard.vue # Individual slot card (230 lines) โ”‚ โ””โ”€โ”€ BookingConfirmPopup.vue # Booking confirmation modal (430 lines) โ”œโ”€โ”€ stores/ โ”‚ โ”œโ”€โ”€ booking.ts # Booking state (72 lines) โ”‚ โ””โ”€โ”€ user.ts # User/membership state (110 lines) โ””โ”€โ”€ utils/ โ”œโ”€โ”€ request.ts # API request utilities (80 lines) โ””โ”€โ”€ format.ts # Date formatting utilities (50 lines) packages/shared/src/ โ”œโ”€โ”€ types/ โ”‚ โ”œโ”€โ”€ time-slot.ts # TimeSlot types โ”‚ โ”œโ”€โ”€ api.ts # API response types โ”‚ โ””โ”€โ”€ booking.ts # Booking types โ”œโ”€โ”€ constants.ts # TIME_PERIODS, etc โ””โ”€โ”€ enums.ts # Enums (TimeSlotStatus, etc) ``` --- ## ๐Ÿ”„ Data Flow at a Glance ``` Page Load โ†“ [Check login + load memberships] โ†“ Store: fetchSlots(today) โ†“ API: GET /time-slot/available?date=TODAY โ†“ State: bookingStore.slots = [TimeSlotWithBookingStatus[], ...] โ†“ Computed: filteredSlots (optionally filtered by period) โ†“ Render: SlotCard components โ†“ User interaction: - Tap date โ†’ loadSlots(newDate) - Filter period โ†’ filteredSlots re-computed - Book slot โ†’ onBookTap() โ†’ popup - Confirm โ†’ createBooking() โ†’ refresh slots - Cancel โ†’ cancelBooking() โ†’ refresh slots ``` --- ## ๐ŸŽญ Key Components ### 1. pages/booking/index.vue **Role:** Main page that orchestrates everything **State:** selectedDate, selectedPeriod, showConfirmPopup, pendingSlot **Stores:** bookingStore, userStore **Key computed:** scrollHeight, filteredSlots ### 2. components/SlotCard.vue **Role:** Displays individual time slot **Props:** slot (TimeSlotWithBookingStatus) **Emits:** book, cancel **States:** 4 button states based on status + isBookedByMe ### 3. components/DateSelector.vue **Role:** Horizontal date picker **Props:** modelValue (YYYY-MM-DD) **Data:** dateRange (7 days from today) **Display:** Shows weekday, day number, month ### 4. components/TimePeriodFilter.vue **Role:** Horizontal tab filter **Props:** modelValue (MORNING|AFTERNOON|EVENING|null) **Constants:** TIME_PERIODS from shared ### 5. components/BookingConfirmPopup.vue **Role:** Modal for confirming booking **Props:** visible, slot, memberships **State:** selectedMembershipId (auto-selected on show) **Logic:** Auto-select first membership when popup opens ### 6. stores/booking.ts **Actions:** - fetchSlots(date) โ†’ GET /time-slot/available?date= - createBooking(dto) โ†’ POST /booking - cancelBooking(bookingId) โ†’ PUT /booking/:id/cancel - fetchMyBookings(status?) โ†’ GET /booking/my - fetchUpcomingBookings() โ†’ GET /booking/my/upcoming ### 7. stores/user.ts **Computed:** - loggedIn: !!token.value - hasValidMembership: activeMemberships.length > 0 - activeMemberships: memberships filtered by ACTIVE status --- ## ๐Ÿ“Š State Types ### TimeSlotWithBookingStatus ```typescript { id: string // UUID date: "2026-04-05" // YYYY-MM-DD startTime: "09:00" // HH:MM endTime: "10:00" // HH:MM capacity: 1 // Max slots bookedCount: 0 // Currently booked status: "OPEN" | "FULL" | "CLOSED" source: "MANUAL" | "TEMPLATE" templateId: null isBookedByMe: boolean // User has booked this myBookingId: string | null // Booking ID (for cancel) } ``` ### MembershipWithCardType ```typescript { id: string cardType: { name: string, ... } status: "ACTIVE" | "EXPIRED" | "USED_UP" remainingTimes: number | null expireDate: "2026-12-31" } ``` --- ## ๐ŸŽจ Visual States ### Slot Card Button States | Condition | Button | Color | Action | |-----------|--------|-------|--------| | OPEN, not booked | "ๅฏ้ข„็บฆ" | Tan (#c9a87c) | Show popup | | OPEN, booked by me | "ๅทฒ้ข„็บฆ" + "ๅ–ๆถˆ" link | Tan + Red | Show cancel confirm | | FULL | "ๅทฒ็บฆๆปก" | Gray (#f0f0f0) | Disabled | | CLOSED | "ๅทฒๅ…ณ้—ญ" | Gray (#f0f0f0) | Disabled | ### Capacity Badge Colors | Condition | Background | Text | Meaning | |-----------|------------|------|---------| | <80% booked | #f0faf3 | #4caf50 | Green - Plenty of spots | | โ‰ฅ80% booked | #fff8ed | #f59e0b | Orange - Almost full | | FULL | #fef0f0 | #ef4444 | Red - No spots | | CLOSED | #f5f5f5 | #999 | Gray - Unavailable | --- ## ๐Ÿ” Authentication - Token stored in localStorage - Automatically included in request headers - 401 response โ†’ Clear token + show "please login" toast - onBookTap checks loggedIn โ†’ shows login modal if needed - onBookTap checks hasValidMembership โ†’ shows purchase modal if needed --- ## ๐Ÿ“ก API Endpoints ### GET /time-slot/available?date=YYYY-MM-DD ``` Query: date (required, YYYY-MM-DD format) Returns: TimeSlotWithBookingStatus[] Auth: Bearer token required ``` ### POST /booking ``` Body: { timeSlotId, membershipId } Returns: BookingWithDetails Auth: Bearer token required ``` ### PUT /booking/:bookingId/cancel ``` Path: bookingId Returns: BookingWithDetails (with status: CANCELLED) Auth: Bearer token required ``` ### GET /membership/my ``` Returns: MembershipWithCardType[] Auth: Bearer token required ``` --- ## โš ๏ธ Known Issues ### 1. GET Request Body Issue - File: `utils/request.ts`, `get()` function - Problem: Data passed as body instead of query params - Impact: Might not work on all platforms ### 2. Error Handling - File: `stores/booking.ts`, `fetchSlots()` - Problem: Network error โ†’ empty array instead of error message - Impact: Users can't tell if error or truly no slots ### 3. Loading State - File: `pages/booking/index.vue` - Problem: Skeleton only appears on initial load - Impact: Date changes appear instant (confusing on slow network) ### 4. Date Math - File: `utils/format.ts`, `getDateRange()` - Problem: Uses ms arithmetic (86400000ms per day) - Impact: Doesn't account for DST transitions --- ## ๐Ÿงช Testing Checklist ### Happy Path - [ ] Load page โ†’ today's slots display - [ ] Tap date โ†’ slots change for that date - [ ] Filter by period โ†’ slots filtered correctly - [ ] Tap "ๅฏ้ข„็บฆ" โ†’ popup shows - [ ] Confirm booking โ†’ slot shows "ๅทฒ้ข„็บฆ" - [ ] Tap "ๅ–ๆถˆ" โ†’ booking cancelled, slot resets - [ ] Pull to refresh โ†’ slots reload ### Edge Cases - [ ] No slots for date โ†’ empty state appears - [ ] Not logged in โ†’ login modal on book tap - [ ] No valid membership โ†’ purchase modal on book tap - [ ] Network error โ†’ ??? (currently shows empty) - [ ] Slot becomes FULL โ†’ button updates to disabled - [ ] Multiple memberships โ†’ can select different card --- ## ๐Ÿ“ File Sizes | File | Lines | Purpose | |------|-------|---------| | pages/booking/index.vue | 311 | Main page orchestration | | components/BookingConfirmPopup.vue | 430 | Booking modal | | components/SlotCard.vue | 230 | Slot display | | stores/booking.ts | 72 | Booking state | | utils/request.ts | 80 | API client | | components/DateSelector.vue | 50 | Date picker | | components/TimePeriodFilter.vue | 50 | Period filter | | utils/format.ts | 50 | Date utilities | --- ## ๐ŸŽ“ Learning Path **Level 1: Overview** 1. Read this file 2. Look at BOOKING_PAGE_ANALYSIS.md โ†’ "Complete Data Flow Diagram" **Level 2: Components** 1. Read COMPONENT_HIERARCHY.md โ†’ "Component Tree" 2. Read BOOKING_PAGE_ANALYSIS.md โ†’ "File-by-File Analysis" **Level 3: Implementation** 1. Read QUICK_REFERENCE.md โ†’ "Where Slots Come From" 2. Read actual source files in order: - stores/booking.ts - pages/booking/index.vue - components/SlotCard.vue - components/BookingConfirmPopup.vue **Level 4: Debugging** 1. Read QUICK_REFERENCE.md โ†’ "Debugging Tips" 2. Read QUICK_REFERENCE.md โ†’ "Common Issues & Solutions" **Level 5: Deep Dive** 1. Read COMPONENT_HIERARCHY.md โ†’ "State Management Flow" 2. Read COMPONENT_HIERARCHY.md โ†’ "API Calls Sequence" 3. Study utils/request.ts for request handling --- ## ๐Ÿ”— Related Documentation - Backend: `/packages/server/src/time-slot/` - Shared types: `/packages/shared/src/types/` - Auth: `/packages/app/src/utils/auth.ts` - User store: `/packages/app/src/stores/user.ts` --- ## ๐Ÿ“ž Quick Answers **Q: Why doesn't the page load?** A: Check 1) Is API returning data? 2) Is token valid? 3) Check console for errors **Q: Why doesn't filtering work?** A: Check 1) Is selectedPeriod.value being set? 2) Is slot.startTime correct format? **Q: Why doesn't the booking button work?** A: Check 1) Is slot.status === OPEN? 2) Is isBookedByMe === false? 3) Is user logged in? **Q: How do I add error handling?** A: See QUICK_REFERENCE.md โ†’ "Issue 1: Slots not loading" โ†’ Solution **Q: How do I test the booking flow?** A: See "Testing Checklist" section above --- ## ๐Ÿš€ Common Tasks ### Add loading indicator during date change โ†’ Use bookingStore.loadingSlots in template ### Show error message for API failures โ†’ Add error state to bookingStore, show in template ### Change colors/styling โ†’ Edit style blocks in .vue files (see color scheme in BOOKING_PAGE_ANALYSIS.md) ### Modify time period ranges โ†’ Edit TIME_PERIODS in packages/shared/src/constants.ts ### Change initial date or time range โ†’ Edit pages/booking/index.vue onMounted() or DATE_SELECTOR_DAYS constant ### Add/remove date selector days โ†’ Edit DATE_SELECTOR_DAYS in packages/shared/src/constants.ts --- Generated: 2026-04-05 Last Updated: BOOKING_PAGE_ANALYSIS.md