You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5.2 KiB
5.2 KiB
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
positionfield - 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 positionstransformSwapToLogical(): Converts visual swap to logical positionsupdatePlayersAfterDraggableSwap(): Updates player array after a swap
2. Update Booking Slot Page
Replace the CourtWithPlayers component with DraggableCourtWrapper:
// 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:
<DraggableCourtWrapper
players={draggableCourtPlayers}
onSwap={handleDraggableSwap}
courtColor={getCourtColor(orderedPlayers, booking.status || SlotStatus.available)}
isDraggable={!disableDrag}
className="h-64"
/>
3. Handle Empty Slots
For the "add player" functionality, we need to handle empty positions:
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
- Better Mobile Support: @dnd-kit provides superior touch handling
- Smoother Animations: Built-in drag overlay and transitions
- Accessibility: Better keyboard and screen reader support
- Smaller Code: Less custom drag logic to maintain
- 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
disableDragis true
Future Enhancements
- Add support for
onEmptySlotClickto handle "add player" functionality - Add visual indicators for
pendingJoinPositions - Consider adding animation for position changes
- Add haptic feedback on mobile devices