Task #16: Per-user drag-and-drop reorder for Home apps
- New user_app_orders composite-PK table (user_id, app_id) -> sort_order - Added GET helper getVisibleAppsForUser that LEFT JOINs user order and sorts by COALESCE(user_sort, app.sort_order, name) - New PUT /api/me/app-order endpoint validates payload, filters to visible apps, dedupes, replaces row set in a transaction - Frontend: wrapped home apps grid in @dnd-kit DndContext + SortableContext with PointerSensor (distance:8) and TouchSensor (delay:250 / tolerance:5) so taps still navigate while a press-and-drag reorders - Optimistic local state with rollback on error; useEffect skips sync while a save mutation is in flight to avoid stomping on user changes - Bottom dock intentionally NOT sortable (per user choice) - DB schema pushed manually via SQL (drizzle-kit push prompted for rename ambiguity); regenerated api-zod / api-client-react - Verified end-to-end: PUT /api/me/app-order returns reordered list and subsequent GET /api/apps reflects the new per-user order
This commit is contained in:
@@ -98,7 +98,17 @@ export default function HomePage() {
|
||||
const [orderedApps, setOrderedApps] = useState<App[] | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (apps) setOrderedApps((prev) => prev ?? apps);
|
||||
if (!apps) return;
|
||||
setOrderedApps((prev) => {
|
||||
if (!prev) return apps;
|
||||
const byId = new Map(apps.map((a) => [a.id, a]));
|
||||
const kept = prev
|
||||
.map((a) => byId.get(a.id))
|
||||
.filter((a): a is App => a !== undefined);
|
||||
const keptIds = new Set(kept.map((a) => a.id));
|
||||
const added = apps.filter((a) => !keptIds.has(a.id));
|
||||
return [...kept, ...added];
|
||||
});
|
||||
}, [apps]);
|
||||
|
||||
const sensors = useSensors(
|
||||
|
||||
Reference in New Issue
Block a user