From fe9b76ab93c2256c1136bf425f080dc0ba928ad1 Mon Sep 17 00:00:00 2001 From: xarmian Date: Tue, 5 May 2026 09:47:55 -0400 Subject: [PATCH] feat(views): gate drag/archive in ListView/BoardView on canEditCollection (TASK-1106) (#420) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The collection page's ListView and BoardView allowed all roles to drag items, drag-status-change, reorder groups/columns, and archive groups. Server enforces edit per-item and per-collection on these mutations (handlers_items.go, handlers_role_board.go) — UI now matches. Changes: - ListView + BoardView accept a `canEdit?: boolean` prop (default true to preserve behavior in existing callers). - ListView: dndzone for groups + intra-group items receives `dragDisabled: !canEdit`. Group drag handle and archive-group button hidden when !canEdit. - BoardView: column-cards dndzone receives `dragDisabled: isMobile || !canEdit`. Column-header drag (column reorder) gated via `draggable={canEdit}` and conditional drag handlers. Column-drag-handle indicator and archive-column button hidden when !canEdit. - Collection page passes `canEdit={canEditThisCollection}` to both views. Scope note: per-item drag gating (e.g. a guest with ItemGrant.edit on one item dragging just that one card) is not implemented — svelte-dnd-action only supports zone-level dragDisabled. Achieving per-item would require switching to dragHandleZone+dragHandle and shipping an explicit handle UI for everyone, which is a larger UX change. Server already enforces per-item edit on the resulting mutations, so no security gap. Documented as a follow-up if needed. TableView: excluded from drag/archive scope — no drag handlers to gate. Status-cell editing is already gated via FieldEditor's readonly prop from TASK-1105. Parent: PLAN-1100. --- .../components/collections/BoardView.svelte | 23 +++++++++----- .../components/collections/ListView.svelte | 30 +++++++++++++++---- .../[workspace]/[collection]/+page.svelte | 2 ++ 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/web/src/lib/components/collections/BoardView.svelte b/web/src/lib/components/collections/BoardView.svelte index e73cf0a1..d4fe0538 100644 --- a/web/src/lib/components/collections/BoardView.svelte +++ b/web/src/lib/components/collections/BoardView.svelte @@ -20,9 +20,16 @@ oncreate?: () => void; itemProgress?: Record; progressLabel?: string; + /** + * canEdit gates drag-to-reorder, drag-to-status-change, column + * reordering, and the archive-column button. See ListView.svelte + * for the rationale (zone-level gate; per-item is a follow-up). + * Default true preserves behavior in callers that don't pass it. + */ + canEdit?: boolean; } - let { items, collection, wsSlug = '', groupField = 'status', focusedItemId = null, onStatusChange, onReorder, onArchiveColumn, onGroupReorder, oncreate, itemProgress, progressLabel = 'tasks' }: Props = $props(); + let { items, collection, wsSlug = '', groupField = 'status', focusedItemId = null, onStatusChange, onReorder, onArchiveColumn, onGroupReorder, oncreate, itemProgress, progressLabel = 'tasks', canEdit = true }: Props = $props(); let confirmArchiveColumn = $state(null); let isMobile = $state(false); @@ -221,17 +228,19 @@