7.3 KiB
7.3 KiB
Worship List Update - December 2025
Summary of Changes
This update addresses cross-device synchronization issues, adds drag-and-drop reordering for songs, and rebrands "Worship Planning" to "Worship List" throughout the application.
🎯 Main Features Added
1. Fixed Cross-Device Song Adding ✅
- Problem: Unable to add songs to worship plans from other devices
- Solution:
- Backend now normalizes all ID comparisons using
.toString()to handle both numeric IDs (from local storage) and UUID strings (from backend) - Added duplicate detection for song associations (prevents duplicate entries)
- POST endpoints now preserve provided IDs from migration instead of always generating new UUIDs
- Backend now normalizes all ID comparisons using
2. Drag-and-Drop Song Reordering ✅
- New Feature: Songs in worship plans can now be reordered by:
- Dragging: Click and drag songs with the ⋮⋮ handle
- Arrows: Use ▲ ▼ buttons to move songs up or down
- Visual Feedback:
- Songs highlight on hover with purple border
- Cursor changes to "grabbing" during drag
- Touch-friendly on mobile devices
3. Worship Planning → Worship List Rebrand ✅
- Changed Throughout:
- Page title: "Worship List"
- Navigation menu: "📋 Worship List" (both desktop and mobile)
- Home page card: "Worship List"
- Documentation files updated
- Icon Change: 📅 → 📋 (from calendar to clipboard)
🔧 Technical Changes
Backend (backend/server.js)
ID Normalization (Songs)
// POST - Preserve provided IDs
const incomingId = req.body.id != null ? req.body.id.toString() : uuid();
if (db.songs.find((s) => s.id?.toString() === incomingId)) {
return res.status(409).json({ error: "Song ID already exists" });
}
// GET/PUT/DELETE - Normalize comparisons
db.songs.find((s) => s.id?.toString() === req.params.id)
ID Normalization (Profiles, Plans, Associations)
- All
profile_id,plan_id,song_idcomparisons now use.toString() - Added duplicate prevention for:
profileSongsassociationsplanSongsassociations
Export Endpoint Fix
db.songs.find((s) => s.id?.toString() === ps.song_id?.toString())
Frontend (frontend/src/App.js)
Profile Songs Mapping Enhancement
export async function getProfileSongs(profileId) {
// Map associations to full songs
for (const ps of backend) {
let song = await localStorageAPI.getSong(ps.song_id);
if (!song) {
// Fetch from backend if not in local cache
const r = await fetch(`${API_BASE}/songs/${ps.song_id}`);
if (r.ok) song = await r.json();
}
if (song) fullSongs.push(song);
}
}
Drag-and-Drop Implementation
function handleDragStart(e, index) {
e.dataTransfer.effectAllowed = "move";
e.dataTransfer.setData("text/plain", index.toString());
}
function handleDrop(e, dropIndex) {
e.preventDefault();
const dragIndex = parseInt(e.dataTransfer.getData("text/plain"));
const newSongs = [...chosenSongs];
const [removed] = newSongs.splice(dragIndex, 1);
newSongs.splice(dropIndex, 0, removed);
setChosenSongs(newSongs);
}
Force Migration Button
- Added "⚙️ Force Full Migration (Associations)" button in Settings
- Re-runs migration even when backend not empty
- Migrates missing
profileSongsandprofileSongKeys
Migration Script (frontend/src/migration.js)
Key Fixes
// Now uses correct localStorage keys
const allProfileSongs = JSON.parse(
localStorage.getItem(STORAGE_KEYS.PROFILE_SONGS) || "[]"
);
// Keys stored as array entries
const profileSongKeysArr = JSON.parse(
localStorage.getItem(STORAGE_KEYS.PROFILE_SONG_KEYS) || "[]"
);
Duplicate Prevention
- Checks existing backend data before posting
- Skips songs/profiles/plans already present
- Logs skipped items with ⏭️ emoji
📱 User Interface Changes
Worship List Page
- ✨ New Label: "Chosen Songs (X) - Drag to reorder or use arrows"
- 🎨 Visual Drag Handle: ⋮⋮ appears on each song
- 🎯 Hover Effects: Songs highlight with purple border and shadow
- 📊 Better Feedback: Order numbers update instantly on reorder
Navigation
- Desktop menu: "📋 Worship List"
- Mobile menu: "📋 Worship List"
- Consistent branding throughout
🔄 Migration Guide
For Existing Users
-
Restart Backend:
cd backend node server.js -
Run Force Migration (if needed):
- Go to Settings → switch to Online Mode
- Scroll to Data Management section
- Click "⚙️ Force Full Migration (Associations)"
- This will sync profile songs and keys to backend
-
Verify on Other Devices:
- Open app on secondary device
- Ensure Online Mode with correct backend IP
- Profile saved songs should now display
- Worship plan songs should be clickable and show titles
✅ Testing Checklist
- Backend normalizes IDs correctly (songs, profiles, plans)
- Profile songs display on other devices
- Plan songs display with titles and are clickable
- Adding songs to plan works from any device
- Drag-and-drop reordering works
- Arrow button reordering works
- Force migration button copies associations
- "Worship List" name appears throughout app
- Navigation icons updated (📋 instead of 📅)
- Duplicate associations prevented
🐛 Bug Fixes
-
404 Errors on Song Lookup
- Cause: Numeric IDs (1, 2) not matching string comparisons
- Fix: All ID comparisons now use
.toString()
-
Empty Profile Song Lists
- Cause: Backend had no profileSongs associations
- Fix: Migration script now migrates associations; Force Migration button added
-
Plan Songs Not Showing
- Cause: Export endpoint not finding songs with mixed ID types
- Fix: Normalized comparisons in export logic
-
Unable to Add Songs from Other Device
- Cause: Duplicate check failing due to ID type mismatch
- Fix: Duplicate detection now compares stringified IDs
📝 Known Limitations
- Drag-and-Drop on Touch Devices: HTML5 drag API has limited mobile support; arrow buttons remain primary method on phones/tablets
- Real-time Order Sync: Order changes update on save; WebSocket doesn't broadcast reorder events (plan must be refreshed)
- Migration Required: Existing users must run Force Migration once to populate backend associations
🚀 Future Enhancements
- Real-time collaborative editing (WebSocket song order updates)
- Touch-friendly drag library (React DnD or similar)
- Bulk song import from CSV
- Plan templates and recurring schedules
- Worship list history and version control
📞 Support
If you encounter issues:
- Check backend is running:
http://localhost:5000/api/health - Verify Online Mode settings match your backend IP
- Run Force Migration to sync associations
- Clear browser cache and localStorage if data seems stale
- Check browser console for error messages
Updated: November 29, 2025 Version: 2.1.0 Author: GitHub Copilot