feat(tasks): support subtasks with inline creation, checklist and title progress bar
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 54s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 54s
This commit is contained in:
1 parent
537b916533
commit
0ec1a8b7f7
7 files changed
+388
-58
No files matched your search
@@ -523,6 +523,22 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
setSlashSearch('');
|
||||
};
|
||||
|
||||
const getDirectChildTodos = (blocks: DocumentBlock[], parentIndex: number): DocumentBlock[] => {
|
||||
const parent = blocks[parentIndex];
|
||||
if (!parent) return [];
|
||||
const parentLevel = parent.level || 0;
|
||||
const childTodos: DocumentBlock[] = [];
|
||||
for (let i = parentIndex + 1; i < blocks.length; i++) {
|
||||
const cur = blocks[i];
|
||||
const curLevel = cur.level || 0;
|
||||
if (curLevel <= parentLevel) break;
|
||||
if (cur.type === 'todo') {
|
||||
childTodos.push(cur);
|
||||
}
|
||||
}
|
||||
return childTodos;
|
||||
};
|
||||
|
||||
// Drag & Drop Handlers on the Bullet Dot
|
||||
const handleDragStart = (e: React.DragEvent, blockId: string) => {
|
||||
e.dataTransfer.setData('text/plain', blockId);
|
||||
@@ -1467,26 +1483,68 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
<div className="flex-1 min-w-0 pt-0.5 relative">
|
||||
{/* TYPE: TODO */}
|
||||
{block.type === 'todo' && (
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!!block.checked}
|
||||
onChange={(e) =>
|
||||
updateBlock(block.id, { checked: e.target.checked })
|
||||
}
|
||||
className="w-4 h-4 checkbox-square cursor-pointer flex-shrink-0"
|
||||
/>
|
||||
<input
|
||||
ref={(el) => (blockInputRefs.current[block.id] = el)}
|
||||
type="text"
|
||||
value={block.content}
|
||||
onChange={(e) => handleContentChange(block, e.target.value)}
|
||||
onKeyDown={(e) => handleKeyDown(e, block, originalIndex)}
|
||||
placeholder="Aufgabe tippen… (/ für Befehle)"
|
||||
className={`w-full bg-transparent text-sm focus:outline-none placeholder:text-faint ${
|
||||
block.checked ? 'line-through text-faint' : 'text-main'
|
||||
}`}
|
||||
/>
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2 group/todo">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!!block.checked}
|
||||
onChange={(e) =>
|
||||
updateBlock(block.id, { checked: e.target.checked })
|
||||
}
|
||||
className="w-4 h-4 checkbox-square cursor-pointer flex-shrink-0"
|
||||
/>
|
||||
<input
|
||||
ref={(el) => (blockInputRefs.current[block.id] = el)}
|
||||
type="text"
|
||||
value={block.content}
|
||||
onChange={(e) => handleContentChange(block, e.target.value)}
|
||||
onKeyDown={(e) => handleKeyDown(e, block, originalIndex)}
|
||||
placeholder="Aufgabe tippen… (/ für Befehle)"
|
||||
className={`w-full bg-transparent text-sm focus:outline-none placeholder:text-faint ${
|
||||
block.checked ? 'line-through text-faint' : 'text-main'
|
||||
}`}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => insertNewBulletAfter(originalIndex, (block.level || 0) + 1, 'todo')}
|
||||
className="opacity-0 group-hover/todo:opacity-100 p-0.5 text-muted hover:text-accent rounded transition-opacity cursor-pointer flex-shrink-0"
|
||||
title="Unteraufgabe erstellen"
|
||||
>
|
||||
<Plus className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Progress bar for child todos if any exist */}
|
||||
{(() => {
|
||||
const childTodos = getDirectChildTodos(selectedDoc?.blocks || [], originalIndex);
|
||||
if (childTodos.length === 0) return null;
|
||||
const doneChild = childTodos.filter((c) => c.checked).length;
|
||||
const totalChild = childTodos.length;
|
||||
const pct = Math.round((doneChild / totalChild) * 100);
|
||||
return (
|
||||
<div
|
||||
className="space-y-0.5 ml-6 pt-0.5 select-none"
|
||||
title={`${doneChild} von ${totalChild} Unteraufgaben erledigt (${pct}%)`}
|
||||
>
|
||||
<div className="flex items-center justify-between text-[10px] text-muted">
|
||||
<span>
|
||||
{doneChild}/{totalChild} erledigt
|
||||
</span>
|
||||
<span className={pct === 100 ? 'text-status-ok font-bold' : ''}>
|
||||
{pct}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full h-1 bg-border rounded-full overflow-hidden">
|
||||
<div
|
||||
className={`h-full transition-all duration-300 ${
|
||||
pct === 100 ? 'bg-status-ok' : 'bg-accent'
|
||||
}`}
|
||||
style={{ width: `${pct}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in new issue
Block a user