From cf3e4cfe411d2c1529dfcc9747e199eca7796109 Mon Sep 17 00:00:00 2001 From: NPS Agent Date: Tue, 12 May 2026 13:21:44 +0930 Subject: [PATCH] Allowed tasks to be moved freely up and down the order, made private windows display in vertical columns rather then horizontal and made both screens display tasks in the SAME order --- app.jsx | 9 ++- dashy.db | Bin 81920 -> 86016 bytes screens.jsx | 159 ++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 130 insertions(+), 38 deletions(-) diff --git a/app.jsx b/app.jsx index f9b2292..6067ec9 100644 --- a/app.jsx +++ b/app.jsx @@ -169,12 +169,16 @@ function App() { } }; - const moveTask = async (taskId, toUserId, position = null) => { + const moveTask = async (taskId, toUserId, position = null, status = null) => { try { const updates = { assignee_id: toUserId }; if (position !== null) updates.position = position; + if (status !== null) updates.status = status; await api.updateTask(taskId, updates); - await api.addAudit({ actor: meId, action: 'task_moved', summary: 'Moved task to ' + (merge(toUserId)||{}).name, target: taskId }); + + const u = (merge(toUserId)||{}).name; + const summary = status ? `Moved task to ${u} and set to ${status}` : `Moved task to ${u}`; + await api.addAudit({ actor: meId, action: 'task_moved', summary, target: taskId }); } catch(e) {} }; @@ -291,6 +295,7 @@ function App() { user={merge(tab)} tasks={filteredTasks} density={t.density} onOpen={(task) => setOpenTaskId(task.id)} onAddFor={(uid) => setAdding(uid)} + onMoveTask={moveTask} /> )} diff --git a/dashy.db b/dashy.db index eb3f74e67ff829ade336a53ff06e5789dd0ad21a..8072a83ea87b5c98f2a4779fd0b6ac5c6aa73597 100644 GIT binary patch delta 4220 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 delta 609 zcmW-eNlX)A0EMSRr~m%{ZCV7P(IT>_gp3un5Tg{80~o2>!HX8CTSS&B8Vb_ElZgne zb5K;I2M;c&;F!38%S}`?9zd;1 zEv~-6m2xVJnFNqrVouB$j_9O_IQAv7-GdW;9=IhjD}IV8@fF0OAI-ui=rR5)4&x}l z&Rz5cxh&rCaTWjp_=%375;6duvaLA870`=5;#N|Ra(D)LiL1mj7^ih8jvujyFp77e z7)pZ@P}v!Nkw(x-QN)%IjjW2mIed$B&>sGaPT);q1#g4BI92RsHgubx;15_a*^a-X zSKLS2`Fr}CW{Pt31B|g+u^1Mym9z^)SuzFe63&KGB#WeoW>QHY`9rs%HQ)^j0zbm= zHPE1&=Cf~j2own`tRp7bh)0ocC#|6UpiW$1Ep!9axCaLK9`478*ND@=jl0D)bOf&^ zH^DR?6!*y_oyR^1Csf%Cdk*dFhLLWcQ1t04n~}AD6lU9&OLnUXl&EO1eFmL$#A=dZ zQjbksfI4V|o3=8pq5Q<^j#bZUyz5-s)|x7FM_E&HnlmNYm-s=}`x{Q#O1-uL$sO}8 zFv^Z?w#ZxDM(1&Av7C*?+QBXdPhYgusefr)mK3TD>W*-xTzo(uKYCTp8`BHI-E!`f zZia(OMPsP8M(+tPw0jFZYN4v;ssELz>zgX&j2wNqDX1Je7z$PB>zZ@r62DR1TrJC` hUfpczl-0LJL!_fO$s>Eu0oAg%FE(Vk=k-_y**~38veW { e.preventDefault(); onDragOver && onDragOver(user.id); }} + onDragOver={(e) => { e.preventDefault(); onDragOver && onDragOver(columnId); }} onDragLeave={(e) => { if (e.relatedTarget && !e.currentTarget.contains(e.relatedTarget)) { - onDragLeave && onDragLeave(user.id); + onDragLeave && onDragLeave(columnId); } }} - onDrop={(e) => { e.preventDefault(); onDropTask && onDropTask(user.id); }} + onDrop={(e) => { e.preventDefault(); onDropTask && onDropTask(columnId); }} >
- -
-

{user.name}

- {user.role} -
+ {user ? ( + <> + +
+

{user.name}

+ {user.role} +
+ + ) : ( + <> + {icon && {icon}} +
+

{title}

+
+ + )}
{tasks.length} - + {onAdd && ( + + )}
-
+
{ + e.preventDefault(); + if (e.target === e.currentTarget) { + onDragOverTask(null, 'bottom'); + } + }}> {tasks.length === 0 && !dragOver && (
— inbox zero — @@ -341,6 +361,7 @@ function Column({ user, tasks, onOpen, onAdd, density, onDropTask, dragOver, onD onDragStart={onDragStartCard} onDragEnd={onDragEndCard} onDragOver={(e) => { + e.preventDefault(); const rect = e.currentTarget.getBoundingClientRect(); const mid = rect.top + rect.height / 2; onDragOverTask(t.id, e.clientY < mid ? 'top' : 'bottom'); @@ -354,11 +375,48 @@ function Column({ user, tasks, onOpen, onAdd, density, onDropTask, dragOver, onD ); } -function UserScreen({ user, tasks, onOpen, onAddFor, density }) { +function UserScreen({ user, tasks, onOpen, onAddFor, density, onMoveTask }) { const mine = tasks.filter(t => t.assignee === user.id); const open = mine.filter(t => t.status === 'open'); const flagged = mine.filter(t => t.status === 'unsuccessful' || t.status === 'billing'); const closed = mine.filter(t => t.status === 'closed'); + + const [draggingTask, setDraggingTask] = React.useState(null); + const [dragOverCol, setDragOverCol] = React.useState(null); + const [dragOverTaskId, setDragOverTaskId] = React.useState(null); + const [dropSide, setDropSide] = React.useState('bottom'); + + const onDrop = (status) => { + if (!draggingTask) return; + + const targetTasks = status === 'flagged' ? flagged : (status === 'open' ? open : closed); + const cleanTarget = targetTasks.filter(t => t.id !== draggingTask.id); + + let newPos = 0; + if (dragOverTaskId) { + const idx = cleanTarget.findIndex(t => t.id === dragOverTaskId); + if (dropSide === 'top') { + const prev = cleanTarget[idx - 1]; + newPos = prev ? (cleanTarget[idx].position + prev.position) / 2 : cleanTarget[idx].position / 2; + } else { + const next = cleanTarget[idx + 1]; + newPos = next ? (cleanTarget[idx].position + next.position) / 2 : cleanTarget[idx].position + 1000; + } + } else { + const last = cleanTarget[cleanTarget.length - 1]; + newPos = last ? last.position + 1000 : 1000; + } + + // Determine target status + let targetStatus = status; + if (status === 'flagged') targetStatus = 'unsuccessful'; // Default flagged status + if (status === 'closed') targetStatus = 'closed'; + if (status === 'open') targetStatus = 'open'; + + onMoveTask && onMoveTask(draggingTask.id, user.id, newPos, targetStatus); + setDraggingTask(null); setDragOverCol(null); setDragOverTaskId(null); + }; + return (
@@ -374,27 +432,56 @@ function UserScreen({ user, tasks, onOpen, onAddFor, density }) {
- {flagged.length > 0 && ( -
-
- {flagged.map(t => )} -
-
- )} - -
-
- {open.map(t => )} -
-
- - {closed.length > 0 && ( -
-
- {closed.map(t => )} -
-
- )} +
+ !draggingTask || t.id !== draggingTask.id)} + onOpen={onOpen} density={density} + dragOver={dragOverCol === 'flagged'} + onDragOver={setDragOverCol} + onDragLeave={() => { setDragOverCol(null); setDragOverTaskId(null); }} + onDragStartCard={(t) => { + setTimeout(() => setDraggingTask(t), 0); + }} + onDragEndCard={() => { setDraggingTask(null); setDragOverCol(null); setDragOverTaskId(null); }} + draggingId={draggingTask?.id} + dragOverTaskId={dragOverTaskId} dropSide={dropSide} + onDragOverTask={(tid, side) => { setDragOverTaskId(tid); setDropSide(side); }} + onDropTask={onDrop} + /> + !draggingTask || t.id !== draggingTask.id)} + onOpen={onOpen} density={density} + dragOver={dragOverCol === 'open'} + onDragOver={setDragOverCol} + onDragLeave={() => { setDragOverCol(null); setDragOverTaskId(null); }} + onDragStartCard={(t) => { + setTimeout(() => setDraggingTask(t), 0); + }} + onDragEndCard={() => { setDraggingTask(null); setDragOverCol(null); setDragOverTaskId(null); }} + draggingId={draggingTask?.id} + dragOverTaskId={dragOverTaskId} dropSide={dropSide} + onDragOverTask={(tid, side) => { setDragOverTaskId(tid); setDropSide(side); }} + onDropTask={onDrop} + /> + !draggingTask || t.id !== draggingTask.id)} + onOpen={onOpen} density={density} + dragOver={dragOverCol === 'closed'} + onDragOver={setDragOverCol} + onDragLeave={() => { setDragOverCol(null); setDragOverTaskId(null); }} + onDragStartCard={(t) => { + setTimeout(() => setDraggingTask(t), 0); + }} + onDragEndCard={() => { setDraggingTask(null); setDragOverCol(null); setDragOverTaskId(null); }} + draggingId={draggingTask?.id} + dragOverTaskId={dragOverTaskId} dropSide={dropSide} + onDragOverTask={(tid, side) => { setDragOverTaskId(tid); setDropSide(side); }} + onDropTask={onDrop} + /> +
); }