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:
+123
-36
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user