Task #463: @dnd-kit notes drag + reorder
- Replace HTML5+touch drag with @dnd-kit (PointerSensor distance:8, TouchSensor delay:200/tol:8) matching home.tsx pattern. - Add sort_order column to notes; ORDER BY asc(sortOrder), updatedAt desc. - New PATCH /notes/reorder endpoint: strict isPinned boolean validation, bucket+permission scoped, all writes in db.transaction for atomicity. - PATCH /notes/:id stamps sort_order = min-1 on folder/pin bucket change. - Client useReorderNotes hook with optimistic cache update. - handleDragEnd builds reorder payload from FULL bucket (owner notes or shared-folder bucket via ref), not the filtered/search subset, so hidden siblings retain stable order. - SharedFolderView publishes its data.notes via bucketRef when viewer has edit permission, enabling correct reorder in shared folders. - Layout fix at narrow viewport: rail stacks above notes (flex-col md:flex-row) so iPad portrait drag has proper bbox. - Playwright tests: notes-folders.spec.mjs both desktop pointer drag and touch long-press drag pass (32s). - OpenAPI codegen skipped: notes-api.ts is hand-written. - Out of scope (pre-existing failures): executive-meetings reorder/font, notes-share PATCH 403/404, groups-crud rollback.
This commit is contained in:
@@ -28,6 +28,14 @@ export const notesTable = pgTable("notes", {
|
||||
items: jsonb("items").$type<ChecklistItem[]>(),
|
||||
isPinned: boolean("is_pinned").notNull().default(false),
|
||||
isArchived: boolean("is_archived").notNull().default(false),
|
||||
// Per-bucket manual sort order. A "bucket" is the unique combination of
|
||||
// (userId, folderId, isPinned). Smaller numbers sort first; ties fall back
|
||||
// to updatedAt DESC so legacy notes (all default 0) keep their existing
|
||||
// recency-based order until the user manually reorders them. Updated by
|
||||
// POST /notes/reorder (drag-to-reorder) and on folder-change via PATCH
|
||||
// /notes/:id (the moved note is placed at the top of the destination
|
||||
// bucket so it lands where the user can see it immediately).
|
||||
sortOrder: integer("sort_order").notNull().default(0),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow().$onUpdate(() => new Date()),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user