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

This commit is contained in:
NPS Agent
2026-05-12 13:21:44 +09:30
parent b0fd767c80
commit cf3e4cfe41
3 changed files with 130 additions and 38 deletions
+7 -2
View File
@@ -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}
/>
)}
</main>
BIN
View File
Binary file not shown.
+123 -36
View File
@@ -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 (
<section
className={"column" + (dragOver && tasks.length === 0 ? " column--over" : "")}
data-comment-anchor={"col-" + user.id}
onDragOver={(e) => { 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); }}
>
<header className="column__head">
<div className="column__head-l">
<Avatar user={user} size={28} />
<div className="column__head-meta">
<h2 className="column__name">{user.name}</h2>
<span className="column__role">{user.role}</span>
</div>
{user ? (
<>
<Avatar user={user} size={28} />
<div className="column__head-meta">
<h2 className="column__name">{user.name}</h2>
<span className="column__role">{user.role}</span>
</div>
</>
) : (
<>
{icon && <span className="column__icon">{icon}</span>}
<div className="column__head-meta">
<h2 className="column__name">{title}</h2>
</div>
</>
)}
</div>
<div className="column__head-r">
<span className="column__count mono">{tasks.length}</span>
<button className="add-btn" onClick={onAdd} aria-label={"Add task for " + user.name}>
<Icon.Plus />
</button>
{onAdd && (
<button className="add-btn" onClick={onAdd} aria-label={"Add task"}>
<Icon.Plus />
</button>
)}
</div>
</header>
<div className="column__list" style={{ flex: 1, position: 'relative' }}>
<div className="column__list" style={{ flex: 1, position: 'relative', opacity: faded ? 0.45 : 1 }}
onDragOver={(e) => {
e.preventDefault();
if (e.target === e.currentTarget) {
onDragOverTask(null, 'bottom');
}
}}>
{tasks.length === 0 && !dragOver && (
<div className="column__empty">
<span className="mono"> inbox zero </span>
@@ -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 (
<div className="user-view">
<div className="user-view__hero">
@@ -374,27 +432,56 @@ function UserScreen({ user, tasks, onOpen, onAddFor, density }) {
</div>
</div>
{flagged.length > 0 && (
<Section title="Needs review" sub="Auto-flagged by the system">
<div className="user-view__grid">
{flagged.map(t => <TaskCard key={t.id} task={t} onOpen={onOpen} density={density} />)}
</div>
</Section>
)}
<Section title="Open" sub={open.length + ' tasks'}>
<div className="user-view__grid">
{open.map(t => <TaskCard key={t.id} task={t} onOpen={onOpen} density={density} />)}
</div>
</Section>
{closed.length > 0 && (
<Section title="Completed" sub={closed.length + ' tasks'}>
<div className="user-view__grid" style={{ opacity: 0.6 }}>
{closed.map(t => <TaskCard key={t.id} task={t} onOpen={onOpen} density={density} />)}
</div>
</Section>
)}
<div className="board board--user">
<Column
title="Needs review" icon="⚠" colId="flagged"
tasks={flagged.filter(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}
/>
<Column
title="Open" icon="○" colId="open"
tasks={open.filter(t => !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}
/>
<Column
title="Completed" icon="✓" colId="closed" faded={true}
tasks={closed.filter(t => !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}
/>
</div>
</div>
);
}