Add drag & drop reordering of trips on the dashboard
- trip_members.sort_order (per-user, migrated) drives GET /api/trips order - PATCH /api/trips/:id/order updates only the caller's membership row - Trip cards get a drag handle reusing the generalized row-reorder helper; clicking the card still navigates
This commit is contained in:
@@ -23,6 +23,7 @@ CREATE TABLE IF NOT EXISTS trip_members (
|
||||
trip_id INTEGER NOT NULL REFERENCES trips(id),
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
role TEXT NOT NULL DEFAULT 'editor',
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (trip_id, user_id)
|
||||
);
|
||||
|
||||
@@ -72,6 +73,7 @@ const MIGRATIONS = [
|
||||
{ table: 'entries', column: 'rental', ddl: 'ALTER TABLE entries ADD COLUMN rental TEXT' },
|
||||
{ table: 'entries', column: 'end_date', ddl: 'ALTER TABLE entries ADD COLUMN end_date TEXT' },
|
||||
{ table: 'entries', column: 'transport_mode', ddl: 'ALTER TABLE entries ADD COLUMN transport_mode TEXT' },
|
||||
{ table: 'trip_members', column: 'sort_order', ddl: 'ALTER TABLE trip_members ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0' },
|
||||
];
|
||||
|
||||
function applyMigrations(db) {
|
||||
|
||||
@@ -116,13 +116,16 @@ export default function tripsRoutes(db) {
|
||||
const router = express.Router();
|
||||
|
||||
const listForUser = db.prepare(`
|
||||
SELECT t.id, t.name, t.start_date, t.end_date, t.owner_id, t.currency, tm.role,
|
||||
SELECT t.id, t.name, t.start_date, t.end_date, t.owner_id, t.currency, tm.role, tm.sort_order,
|
||||
(SELECT COUNT(*) FROM trip_members WHERE trip_id = t.id) AS member_count,
|
||||
(SELECT COUNT(*) FROM entries WHERE trip_id = t.id) AS entry_count
|
||||
FROM trips t
|
||||
JOIN trip_members tm ON tm.trip_id = t.id AND tm.user_id = ?
|
||||
ORDER BY t.created_at DESC, t.id DESC
|
||||
ORDER BY tm.sort_order, t.created_at DESC, t.id DESC
|
||||
`);
|
||||
const updateMemberOrder = db.prepare(
|
||||
'UPDATE trip_members SET sort_order = ? WHERE trip_id = ? AND user_id = ?'
|
||||
);
|
||||
const insertTrip = db.prepare(
|
||||
'INSERT INTO trips (name, start_date, end_date, owner_id, currency, join_code) VALUES (?, ?, ?, ?, ?, ?)'
|
||||
);
|
||||
@@ -233,6 +236,18 @@ export default function tripsRoutes(db) {
|
||||
res.status(200).json({ trip: tripJson(getTrip.get(ctx.tripId)) });
|
||||
});
|
||||
|
||||
// PATCH /api/trips/:id/order { sort_order } — caller's own dashboard order.
|
||||
router.patch('/:id/order', (req, res) => {
|
||||
const ctx = requireMember(req, res);
|
||||
if (!ctx) return;
|
||||
const { sort_order } = req.body || {};
|
||||
if (!Number.isInteger(sort_order)) {
|
||||
return res.status(400).json({ error: 'sort_order must be an integer' });
|
||||
}
|
||||
updateMemberOrder.run(sort_order, ctx.tripId, req.session.userId);
|
||||
res.status(200).json({ sort_order });
|
||||
});
|
||||
|
||||
// DELETE /api/trips/:id (owner only)
|
||||
router.delete('/:id', (req, res) => {
|
||||
const ctx = requireMember(req, res);
|
||||
|
||||
Reference in New Issue
Block a user