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.

150 lines
5.2 KiB
Markdown

# 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:
<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:
```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