# DraggableCourt Integration Strategy for Booking Slot Page ## Overview This document outlines the strategy for integrating the new `DraggableCourt` component into the existing booking slot page, replacing the current `CourtWithPlayers` component. ## Position Mapping Challenge The key challenge is the difference in how positions are handled: ### Current System (CourtWithPlayers) - Uses **logical positions** (0-3) stored in `currentPosition` - Visual arrangement uses `displayToLogical = [0, 1, 3, 2]` - This means: - Logical position 0 → Visual position 0 (top-left) - Logical position 1 → Visual position 1 (bottom-left) - Logical position 2 → Visual position 3 (bottom-right) - Logical position 3 → Visual position 2 (top-right) ### New System (DraggableCourt) - Uses **direct visual positions** (0-3) in the `position` field - Visual arrangement is straightforward: [0, 1, 2, 3] - This means: - Position 0 → top-left - Position 1 → bottom-left - Position 2 → top-right - Position 3 → bottom-right ## Integration Steps ### 1. Add Transformation Utilities Created `src/utils/draggableCourtAdapter.ts` with: - `transformPlayersToDraggableCourt()`: Converts players from logical to visual positions - `transformSwapToLogical()`: Converts visual swap to logical positions - `updatePlayersAfterDraggableSwap()`: Updates player array after a swap ### 2. Update Booking Slot Page Replace the CourtWithPlayers component with DraggableCourtWrapper: ```tsx // In src/app/[locale]/booking/slot/[slot_id]/page.tsx import DraggableCourtWrapper from '@/src/components/DraggableCourtWrapper'; import { transformPlayersToDraggableCourt, transformSwapToLogical } from '@/src/utils/draggableCourtAdapter'; // Inside component: const draggableCourtPlayers = useMemo( () => transformPlayersToDraggableCourt(orderedPlayers), [orderedPlayers] ); const handleDraggableSwap = useCallback((fromDisplay: number, toDisplay: number) => { // Convert to logical positions for recording const [fromLogical, toLogical] = transformSwapToLogical(fromDisplay, toDisplay); // Record the swap in logical positions setRecordedSwaps(prev => [...prev, [fromLogical, toLogical]]); // Update players using existing logic setPlayersWithCurrentOrder(prev => { const fromPlayerIndex = prev.findIndex(p => p.currentPosition === fromLogical); const toPlayerIndex = prev.findIndex(p => p.currentPosition === toLogical); if (fromPlayerIndex === -1) return prev; const copy = [...prev]; if (toPlayerIndex !== -1) { // Swap players [copy[fromPlayerIndex], copy[toPlayerIndex]] = [ { ...copy[toPlayerIndex], currentPosition: fromLogical }, { ...copy[fromPlayerIndex], currentPosition: toLogical } ]; } else { // Move to empty position copy[fromPlayerIndex] = { ...copy[fromPlayerIndex], currentPosition: toLogical }; } return copy; }); }, [setRecordedSwaps, setPlayersWithCurrentOrder]); // Replace CourtWithPlayers with: ``` ### 3. Handle Empty Slots For the "add player" functionality, we need to handle empty positions: ```tsx const handleEmptyPositionClick = (displayPosition: number) => { const logicalPosition = displayToLogical[displayPosition]; handleEmptySlotClick(logicalPosition); }; ``` ### 4. Anonymous Players for Past Bookings The DraggableCourt already handles missing players by showing empty positions, so we don't need to create anonymous players like in CourtWithPlayers. ### 5. Props Mapping | CourtWithPlayers | DraggableCourtWrapper | |-----------------|----------------------| | `players` | `players` (transformed) | | `onOrderChange` + `onSwapRecorded` | `onSwap` | | `fixed` | `isDraggable` (inverted) | | `className` | `className` | | `slotStatus` | Used for `courtColor` | | `showAddButtons` | Not needed (always shows empty slots) | | `onEmptySlotClick` | Not directly supported yet | | `pendingJoinPositions` | Not supported yet | | `isPastBooking` | Not needed | ## Benefits of Migration 1. **Better Mobile Support**: @dnd-kit provides superior touch handling 2. **Smoother Animations**: Built-in drag overlay and transitions 3. **Accessibility**: Better keyboard and screen reader support 4. **Smaller Code**: Less custom drag logic to maintain 5. **Customizable Collision Detection**: Already implemented bottom-right collision point ## Testing Checklist - [ ] Visual positions match exactly between old and new components - [ ] Swaps are recorded with correct logical positions - [ ] Position updates persist correctly - [ ] Mobile drag and drop works smoothly - [ ] Keyboard navigation works (if enabled) - [ ] Empty positions display correctly - [ ] Court colors match based on player count - [ ] Disabled state works when `disableDrag` is true ## Future Enhancements 1. Add support for `onEmptySlotClick` to handle "add player" functionality 2. Add visual indicators for `pendingJoinPositions` 3. Consider adding animation for position changes 4. Add haptic feedback on mobile devices