diff --git a/PROGRESS.md b/PROGRESS.md index c772762..b853887 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -52,6 +52,8 @@ 8. **Task Reopening:** Added a "Reopen task" button to restore accidentally closed tasks back to the queue. 9. **User Management (Settings):** Built backend API endpoints (`POST`, `PATCH`, `DELETE` for `/users`) and wired up the `WorkspaceTab` allowing Admins to manage the team from the UI. 10. **Task Editing:** Implemented inline editing for task descriptions using an active text box state with "Save/Cancel" actions. +11. **UI Cleanup:** Removed hardcoded, prototype placeholder notes from the `TaskDetail` modal to prepare for future dynamic notes integration. +12. **Permanent Deletion:** Added a "Delete task permanently" button to the `TaskDetail` sidebar with a confirmation dialog, backed by a new `DELETE /tasks/{id}` API endpoint. ### Phase 3: Advanced Features - **Real-time Notifications:** Explore WebSockets for task assignments. diff --git a/api.js b/api.js index b3a9744..355e77c 100644 --- a/api.js +++ b/api.js @@ -118,6 +118,14 @@ class ApiService { return data; } + async deleteTask(id) { + const data = await this.request(`/tasks/${id}`, { + method: 'DELETE', + }); + this.notify(); + return data; + } + async addAudit(auditData) { const data = await this.request('/audit', { method: 'POST', diff --git a/app.jsx b/app.jsx index 987bf82..26bff3a 100644 --- a/app.jsx +++ b/app.jsx @@ -166,6 +166,18 @@ function App() { } }; + const deleteTask = async (taskId) => { + try { + const t = tasks.find(x => x.id === taskId); + await api.deleteTask(taskId); + await api.addAudit({ actor: meId, action: 'task_deleted', summary: 'Deleted task' + (t ? ': ' + t.title : ''), target: taskId }); + setOpenTaskId(null); + } catch(e) { + console.error(e); + alert("Failed to delete task: " + e.message); + } + }; + const dismissHU = (id) => setHeadsUp(h => h.filter(x => x.id !== id)); const openTaskFromAnywhere = (id) => { setOpenTaskId(id); setShowLogs(false); }; @@ -223,7 +235,7 @@ function App() { setAdding(null)} onSubmit={addTask} defaultAssignee={adding} me={me} /> {mappedOpenTask && ( - setOpenTaskId(null)} onMove={moveTask} onPriority={setPriority} onComplete={() => completeTask(mappedOpenTask.id)} onReopen={() => reopenTask(mappedOpenTask.id)} onEditDesc={(newDesc) => editTaskDesc(mappedOpenTask.id, newDesc)} /> + setOpenTaskId(null)} onMove={moveTask} onPriority={setPriority} onComplete={() => completeTask(mappedOpenTask.id)} onReopen={() => reopenTask(mappedOpenTask.id)} onEditDesc={(newDesc) => editTaskDesc(mappedOpenTask.id, newDesc)} onDeleteTask={() => deleteTask(mappedOpenTask.id)} /> )} {showLogs && ( setShowLogs(false)} wide> diff --git a/backend/main.py b/backend/main.py index c4bd706..f176752 100644 --- a/backend/main.py +++ b/backend/main.py @@ -118,15 +118,24 @@ def update_task(task_id: str, task_update: schemas.TaskUpdate, db: Session = Dep db_task = db.query(models.Task).filter(models.Task.id == task_id).first() if not db_task: raise HTTPException(status_code=404, detail="Task not found") - + update_data = task_update.dict(exclude_unset=True) for key, value in update_data.items(): setattr(db_task, key, value) - + db.commit() db.refresh(db_task) return db_task +@app.delete("/tasks/{task_id}") +def delete_task(task_id: str, db: Session = Depends(get_db)): + db_task = db.query(models.Task).filter(models.Task.id == task_id).first() + if not db_task: + raise HTTPException(status_code=404, detail="Task not found") + + db.delete(db_task) + db.commit() + return {"message": "Task deleted"} @app.get("/audit", response_model=List[schemas.AuditLog]) def read_audit(db: Session = Depends(get_db)): return db.query(models.AuditLog).order_by(models.AuditLog.at.desc()).all() diff --git a/dashy.db b/dashy.db index 5efd1e3..3ddbe1a 100644 Binary files a/dashy.db and b/dashy.db differ diff --git a/screens.jsx b/screens.jsx index 50c1409..c90b559 100644 --- a/screens.jsx +++ b/screens.jsx @@ -407,7 +407,7 @@ function Modal({ children, onClose, title, eyebrow, wide = false }) { ); } -function TaskDetail({ task, allAudit = [], onClose, onMove, onPriority, onComplete, onReopen, onEditDesc }) { +function TaskDetail({ task, allAudit = [], onClose, onMove, onPriority, onComplete, onReopen, onEditDesc, onDeleteTask }) { const [editingDesc, setEditingDesc] = React.useState(false); const [descValue, setDescValue] = React.useState(task ? task.description || '' : ''); @@ -557,6 +557,16 @@ function TaskDetail({ task, allAudit = [], onClose, onMove, onPriority, onComple
Thu, May 7 ยท 4:00 pm
+ +
+ +