11 KiB
Drag-Drop API Endpoints PRD
Overview
This document outlines the API endpoints required to support drag-and-drop functionality for party grid management in the Hensei application. The frontend has implemented drag-drop interactions, but requires backend endpoints to persist position changes and item swaps.
Problem Statement
Current State
- The API currently only supports add/remove operations for grid items
- Position changes require removing and re-adding items
- Swapping items requires multiple API calls (remove both, then add both)
- This approach is error-prone and creates race conditions
- No atomic operations for complex moves
User Impact
- Drag-drop appears to work visually but doesn't persist
- Risk of data loss if operations partially fail
- Poor performance with multiple network requests
- Inconsistent state between UI and database
Proposed Solution
Implement dedicated API endpoints that match the drag-drop operations:
- Update Position - Move an item to an empty slot
- Swap Items - Exchange positions of two items
- Batch Update - Handle complex multi-item operations atomically
API Specifications
1. Update Position Endpoints
Move a grid item to a new empty position within the same or different container.
Weapons
PUT /api/v1/parties/:party_id/grid_weapons/:id/position
Request Body:
{
"position": 5,
"container": "main" | "extra"
}
Response (200 OK):
{
"party": { /* updated party object */ },
"grid_weapon": { /* updated grid weapon */ }
}
Characters
PUT /api/v1/parties/:party_id/grid_characters/:id/position
Request Body:
{
"position": 2,
"container": "main" | "extra"
}
Special Rules:
- Characters must maintain sequential filling (no gaps)
- Server should auto-compact positions after move
Response (200 OK):
{
"party": { /* updated party object */ },
"grid_character": { /* updated grid character */ },
"reordered": true // Indicates if sequential filling was applied
}
Summons
PUT /api/v1/parties/:party_id/grid_summons/:id/position
Request Body:
{
"position": 1,
"container": "main" | "subaura"
}
Response (200 OK):
{
"party": { /* updated party object */ },
"grid_summon": { /* updated grid summon */ }
}
2. Swap Endpoints
Exchange positions between two grid items of the same type.
Weapons
POST /api/v1/parties/:party_id/grid_weapons/swap
Request Body:
{
"source_id": "uuid-1",
"target_id": "uuid-2"
}
Response (200 OK):
{
"party": { /* updated party object */ },
"swapped": {
"source": { /* updated source weapon */ },
"target": { /* updated target weapon */ }
}
}
Characters
POST /api/v1/parties/:party_id/grid_characters/swap
Request Body:
{
"source_id": "uuid-1",
"target_id": "uuid-2"
}
Response (200 OK):
{
"party": { /* updated party object */ },
"swapped": {
"source": { /* updated source character */ },
"target": { /* updated target character */ }
}
}
Summons
POST /api/v1/parties/:party_id/grid_summons/swap
Request Body:
{
"source_id": "uuid-1",
"target_id": "uuid-2"
}
Response (200 OK):
{
"party": { /* updated party object */ },
"swapped": {
"source": { /* updated source summon */ },
"target": { /* updated target summon */ }
}
}
3. Batch Grid Update Endpoint
Handle complex multi-item operations atomically.
POST /api/v1/parties/:party_id/grid_update
Request Body:
{
"operations": [
{
"type": "move",
"entity": "character",
"id": "uuid-1",
"position": 2,
"container": "main"
},
{
"type": "swap",
"entity": "weapon",
"source_id": "uuid-2",
"target_id": "uuid-3"
},
{
"type": "remove",
"entity": "summon",
"id": "uuid-4"
}
],
"options": {
"maintain_character_sequence": true,
"validate_before_execute": true
}
}
Response (200 OK):
{
"party": { /* fully updated party object */ },
"operations_applied": 3,
"changes": [
{ "entity": "character", "id": "uuid-1", "action": "moved", "from": 0, "to": 2 },
{ "entity": "weapon", "id": "uuid-2", "action": "swapped", "with": "uuid-3" },
{ "entity": "summon", "id": "uuid-4", "action": "removed" }
]
}
Business Rules
Position Constraints
Characters (0-4, 5-6 for extra)
- Main slots (0-4): Must be sequential, no gaps allowed
- Extra slots (5-6): Can have gaps
- Auto-compact: When moving/removing, shift remaining characters to maintain sequence
Weapons (-1 mainhand, 0-8 grid, 9+ extra)
- Mainhand (-1): Not draggable, cannot be target
- Grid slots (0-8): Can have gaps
- Extra slots (9+): Can have gaps
Summons (-1 main, 0-3 sub, 4-5 subaura, 6 friend)
- Main (-1): Not draggable, cannot be target
- Sub slots (0-3): Can have gaps
- Subaura (4-5): Can have gaps
- Friend (6): Not draggable, cannot be target
Validation Rules
-
Type Matching
- Can only swap/move items of the same type
- Cannot mix characters, weapons, and summons
-
Position Validation
- Target position must be valid for the entity type
- Cannot move to restricted positions (mainhand, main summon, friend)
-
Container Rules
- Items can move between containers (main ↔ extra)
- Container must be valid for the entity type
-
Conflict Resolution
- For weapons: Check Ultima/Opus conflicts
- For summons: Check duplicate restrictions
- Apply same rules as create operations
Error Handling
Common Error Responses
400 Bad Request
{
"error": "Invalid position",
"details": {
"position": 10,
"max_position": 8,
"entity": "weapon"
}
}
403 Forbidden
{
"error": "Cannot modify restricted slot",
"details": {
"slot": "mainhand",
"reason": "Mainhand weapon cannot be moved via drag-drop"
}
}
409 Conflict
{
"error": "Operation would create invalid state",
"details": {
"reason": "Cannot have two Ultima weapons in grid",
"conflicting_items": ["uuid-1", "uuid-2"]
}
}
422 Unprocessable Entity
{
"error": "Validation failed",
"details": {
"source_id": ["not found"],
"target_id": ["belongs to different party"]
}
}
Rollback Strategy
For batch operations:
- Validate all operations before executing any
- Use database transaction for atomicity
- If any operation fails, rollback entire batch
- Return detailed error showing which operation failed
Implementation Notes
Backend (Rails API)
-
Controller Actions
- Add
update_positionaction to grid controllers - Add
swapaction to grid controllers - Add
grid_updateaction to parties controller
- Add
-
Model Methods
GridWeapon#update_position(new_position, container = nil)GridCharacter#update_position(new_position, container = nil)GridSummon#update_position(new_position, container = nil)Party#swap_items(source_item, target_item)Party#apply_grid_operations(operations)
-
Validations
- Add position range validators
- Add container validators
- Ensure conflict rules are checked
-
Authorization
- Require edit permission (user ownership or edit key)
- Use existing
authorize_party_edit!pattern
Frontend Integration
-
API Client Updates
// Add to apiClient async updateWeaponPosition(partyId, weaponId, position, container) async updateCharacterPosition(partyId, characterId, position, container) async updateSummonPosition(partyId, summonId, position, container) async swapWeapons(partyId, sourceId, targetId) async swapCharacters(partyId, sourceId, targetId) async swapSummons(partyId, sourceId, targetId) async batchGridUpdate(partyId, operations) -
Drag Handler Updates
// In Party.svelte async function handleMove(source, target) { const result = await apiClient.updateWeaponPosition( party.id, source.itemId, target.position, target.container ) party = result.party } async function handleSwap(source, target) { const result = await apiClient.swapWeapons( party.id, source.itemId, target.itemId ) party = result.party }
Migration Strategy
Phase 1: Backend Implementation
- Implement new endpoints in Rails API
- Add comprehensive tests
- Deploy to staging
Phase 2: Frontend Integration
- Add new methods to API client
- Update drag handlers to use new endpoints
- Keep fallback to old method temporarily
Phase 3: Validation
- Test all drag-drop scenarios
- Verify data integrity
- Monitor for errors
Phase 4: Cleanup
- Remove old implementation
- Remove fallback code
- Update documentation
Success Metrics
-
Performance
- Single API call for position updates (vs 2-4 calls)
- Response time < 200ms for position updates
- Response time < 300ms for swaps
-
Reliability
- Zero data inconsistencies
- Atomic operations (no partial updates)
- Proper rollback on failures
-
User Experience
- Immediate visual feedback
- Smooth animations
- No lost changes
-
Developer Experience
- Clean, intuitive API
- Comprehensive error messages
- Easy to debug issues
Security Considerations
-
Authorization
- Verify party ownership or edit key
- Validate all IDs belong to specified party
- Rate limiting on batch operations
-
Input Validation
- Sanitize all position values
- Validate container strings
- Check array bounds
-
Audit Trail
- Log all position changes
- Track user/edit key for each operation
- Monitor for suspicious patterns
Future Enhancements
-
Undo/Redo Support
- Track operation history
- Implement reverse operations
- Client-side undo stack
-
Optimistic Updates
- Apply changes immediately in UI
- Rollback on server rejection
- Queue operations for offline support
-
Bulk Operations
- "Auto-arrange" endpoint
- "Clear grid" endpoint
- "Copy from template" endpoint
-
WebSocket Support
- Real-time updates for shared parties
- Conflict resolution for simultaneous edits
- Live collaboration features
Conclusion
These API endpoints will provide a robust foundation for drag-drop functionality, ensuring data consistency, good performance, and excellent user experience. The atomic nature of these operations will eliminate current issues with partial updates and race conditions.