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 eb3f74e..8072a83 100644 Binary files a/dashy.db and b/dashy.db differ diff --git a/screens.jsx b/screens.jsx index 9206ad0..0a1a65c 100644 --- a/screens.jsx +++ b/screens.jsx @@ -272,36 +272,56 @@ function OverviewScreen({ tasks, onOpen, onAddFor, density, onMoveTask, dbUsers ); } -function Column({ user, tasks, onOpen, onAdd, density, onDropTask, dragOver, onDragOver, onDragLeave, onDragStartCard, onDragEndCard, draggingId, dragOverTaskId, dropSide, onDragOverTask }) { +function Column({ user, title, icon, tasks, onOpen, onAdd, density, onDropTask, dragOver, onDragOver, onDragLeave, onDragStartCard, onDragEndCard, draggingId, dragOverTaskId, dropSide, onDragOverTask, colId, faded }) { + const columnId = colId || (user ? user.id : title); + return (
{ 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} + /> +
); }