feat: Phase 4 — Footfall Dashboard (slot-based bookings + trainer attendance)#6
feat: Phase 4 — Footfall Dashboard (slot-based bookings + trainer attendance)#6
Conversation
Made-with: Cursor
Made-with: Cursor
…rd widgets Made-with: Cursor
There was a problem hiding this comment.
Pull request overview
This pull request implements Phase 4 of the footfall dashboard, transitioning from raw footfall events to slot-based amenity bookings. The core change replaces the FootfallEvent model with a new AmenityBooking model, where gym bookings serve as the authoritative footfall records. The PR includes new dashboard components, API endpoints for footfall and trainer attendance data, and 687 seeded amenity bookings across 7 days with realistic peak-hour distribution.
Changes:
- Replaced
FootfallEventmodel withAmenityBooking(centerId, memberName, memberFlat, slotDate, slotHour, status) for slot-based footfall tracking - Seeded 687 amenity bookings across 7 days with peak-hour distribution (5am-10am and 6pm-9pm) for 2 active centers
- Added new dashboard components:
FootfallCard(today's count with hourly bar chart),BookingFeed(recent bookings), andTrainerAttendanceCard(trainer attendance summary) - Created new API routes:
GET /api/footfallandGET /api/attendance/trainerfor data retrieval - Implemented new RWA resident dashboard at
/rwawith footfall, bookings, and trainer attendance data
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| prisma/schema.prisma | Replaced FootfallEvent model with AmenityBooking, updated Center relations |
| prisma/seed.ts | Updated seeding logic to generate realistic 687 amenity bookings with peak-hour distribution across two centers |
| prisma/migrations/20260409103000_replace_footfall_with_amenity_bookings/migration.sql | Database migration to drop FootfallEvent table and create AmenityBooking table |
| components/dashboard/FootfallCard.tsx | New component displaying today's booking count with delta vs yesterday and hourly bar chart |
| components/dashboard/BookingFeed.tsx | New component showing recent 12 bookings with member names and flat numbers |
| components/dashboard/TrainerAttendanceCard.tsx | New component displaying trainer attendance summary (present/absent/late) with check-in times |
| app/rwa/page.tsx | New RWA resident dashboard page combining footfall, bookings, and trainer attendance cards |
| app/rwa-admin/page.tsx | Updated stats calculation to use amenity bookings instead of footfall events |
| app/api/footfall/route.ts | New API endpoint for footfall data with hourly breakdown and recent bookings |
| app/api/attendance/trainer/route.ts | New API endpoint for trainer attendance with summary and per-trainer details |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const hourLabel = (h: number) => { | ||
| const suffix = h >= 12 ? "pm" : "am" | ||
| const display = h > 12 ? h - 12 : h | ||
| return `${display}${suffix}` | ||
| } |
There was a problem hiding this comment.
The hourLabel function is missing the h === 0 ? 12 : h check when calculating the display hour. This will produce incorrect labels like "0am" instead of "12am" for midnight hour bookings (when h is 0). While this particular bug won't affect the current feature since bookings are only for hours 5-21, the inconsistency with other implementations of hourLabel in FootfallCard.tsx and BookingFeed.tsx makes it a bug that should be fixed for consistency and future-proofing.
| const hourLabel = (h: number) => { | ||
| const suffix = h >= 12 ? "pm" : "am" | ||
| const display = h > 12 ? h - 12 : h === 0 ? 12 : h | ||
| const nextH = h + 1 | ||
| const nextDisplay = nextH > 12 ? nextH - 12 : nextH === 0 ? 12 : nextH | ||
| const nextSuffix = nextH >= 12 ? "pm" : "am" | ||
| return `${display}${suffix}–${nextDisplay}${nextSuffix}` | ||
| } |
There was a problem hiding this comment.
The hourLabel function in BookingFeed has a logic error for the suffix calculation when the next hour crosses the am/pm boundary at midnight (hour 24). When h=23, nextH becomes 24, and nextSuffix becomes "pm" (since 24 >= 12), but hour 24 represents midnight (hour 0) and should be "am". The correct logic should be nextSuffix = nextH > 24 || nextH < 12 ? "am" : "pm" or handle the 24-hour case explicitly. While this won't affect current bookings (limited to 5-21), it's a latent bug that could cause incorrect labels if booking hours are extended in the future.
Integrates FootfallCard, TrainerAttendanceCard, and BookingFeed into the proper /rwa-admin page (with sidebar) replacing the placeholder. Scopes stat queries to the active center. Removes the orphaned /rwa standalone page that had no layout or navigation. Made-with: Cursor
Summary
FootfallEvent— a gym slot booking (e.g. 7am–8am) IS the footfall record. No scanner device required at the gym.TrainerAttendance.checkIn/checkOutalready tracked this correctly.What's new
Data
AmenityBookingmodel:centerId,memberName,memberFlat,slotDate,slotHour(int),statusFootfallEventmodel removed (booking = footfall, no raw events needed)API routes
GET /api/footfall?centerId=&date=→ total bookings, by-hour array (5am–9pm), recent 10 bookingsGET /api/attendance/trainer?centerId=&date=→ present/absent/late summary + per-trainer check-in times and hours workedRWA Admin Dashboard (
/rwa)Test plan
/rwa— footfall card shows today's booking count with hourly chartGET /api/footfall?centerId=[id]returns{ totalBookings, byHour, recentBookings }GET /api/attendance/trainer?centerId=[id]returns{ summary, trainers }Made with Cursor