Release v0.1.4: Auto-complete parents and done task strikethrough
New Features: 1. Auto-Complete Parent Tasks - When all child tasks are marked as "done", parent automatically becomes "done" - Works recursively up the task hierarchy - Implemented in backend crud.py with check_and_update_parent_status() - Prevents manual status management for completed branches 2. Strikethrough for Done Tasks - Time estimates crossed out when task status is "done" - Visual indicator that work is completed - Applied in both TreeView and KanbanView 3. Updated Version - Bumped to v0.1.4 in App.jsx header 4. Documentation - Added comprehensive CHANGELOG.md - Updated README.md with v0.1.4 features - Documented all versions from v0.1.0 to v0.1.4 - Added usage examples, architecture diagrams, troubleshooting Technical Changes: - backend/app/crud.py: Added check_and_update_parent_status() recursive function - frontend/src/components/TreeView.jsx: Added line-through styling for done tasks - frontend/src/components/KanbanView.jsx: Added line-through styling for done tasks - frontend/src/App.jsx: Version updated to v0.1.4 This release completes the intelligent time tracking and auto-completion features, making TESSERACT a fully-featured hierarchical task management system.
This commit is contained in:
157
CHANGELOG.md
Normal file
157
CHANGELOG.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to TESSERACT will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.1.4] - 2025-01-XX
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Strikethrough styling for time estimates when tasks are marked as "done"
|
||||||
|
- Auto-complete parent tasks when all child tasks are marked as "done"
|
||||||
|
- Works recursively up the task hierarchy
|
||||||
|
- Parents automatically transition to "done" status when all children complete
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Time estimates on completed tasks now display with strikethrough decoration
|
||||||
|
- Parent task status automatically updates based on children completion state
|
||||||
|
|
||||||
|
## [0.1.3] - 2025-01-XX
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Enhanced task creation forms with metadata fields
|
||||||
|
- Title field (required)
|
||||||
|
- Tags field (comma-separated input)
|
||||||
|
- Time estimate fields (hours and minutes)
|
||||||
|
- Flag color selector with 8 color options
|
||||||
|
- TaskForm component for consistent task creation across views
|
||||||
|
- Status change dropdown in TaskMenu (no longer requires Kanban view)
|
||||||
|
- Leaf-based time calculation system
|
||||||
|
- Parent tasks show sum of descendant leaf task estimates
|
||||||
|
- Prevents double-counting when both parents and children have estimates
|
||||||
|
- Excludes "done" tasks from time calculations
|
||||||
|
- Time format changed from decimal hours to hours + minutes (e.g., "1h 30m" instead of "1.5h")
|
||||||
|
- CHANGELOG.md and README.md documentation
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Task creation now includes all metadata fields upfront
|
||||||
|
- Time estimates display remaining work (excludes completed tasks)
|
||||||
|
- Time input uses separate hours/minutes fields instead of single minutes field
|
||||||
|
- Parent task estimates calculated from leaf descendants only
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Time calculation now accurately represents remaining work
|
||||||
|
- Time format more human-readable with hours and minutes
|
||||||
|
|
||||||
|
## [0.1.2] - 2025-01-XX
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Metadata fields for tasks:
|
||||||
|
- `estimated_minutes` (Integer) - Time estimate stored in minutes
|
||||||
|
- `tags` (JSON Array) - Categorization tags
|
||||||
|
- `flag_color` (String) - Priority flag with 7 color options
|
||||||
|
- TaskMenu component with three-dot dropdown
|
||||||
|
- Edit time estimates
|
||||||
|
- Edit tags (comma-separated)
|
||||||
|
- Set flag colors
|
||||||
|
- Edit task title
|
||||||
|
- Delete tasks
|
||||||
|
- SearchBar component in header
|
||||||
|
- Real-time search with 300ms debounce
|
||||||
|
- Optional project filtering
|
||||||
|
- Click results to navigate to project
|
||||||
|
- Displays metadata in results
|
||||||
|
- Time and tag display in TreeView and KanbanView
|
||||||
|
- Flag color indicators on tasks
|
||||||
|
- Backend search endpoint `/api/search` with project filtering
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- TreeView and KanbanView now display task metadata
|
||||||
|
- Enhanced visual design with metadata badges
|
||||||
|
|
||||||
|
## [0.1.1] - 2025-01-XX
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Tree view indentation now scales properly with nesting depth
|
||||||
|
- Changed from fixed `ml-6` to calculated `marginLeft: ${level * 1.5}rem`
|
||||||
|
- Each nesting level adds 1.5rem (24px) of indentation
|
||||||
|
- Kanban view subtask handling
|
||||||
|
- All tasks (including subtasks) now appear as individual draggable cards
|
||||||
|
- Subtasks show parent context: "↳ subtask of: [parent name]"
|
||||||
|
- Removed nested subtask list display
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Improved visual hierarchy in tree view
|
||||||
|
- Better subtask representation in Kanban board
|
||||||
|
|
||||||
|
## [0.1.0] - 2025-01-XX
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Initial MVP release
|
||||||
|
- Core Features:
|
||||||
|
- Arbitrary-depth nested task hierarchies
|
||||||
|
- Two view modes: Tree View and Kanban Board
|
||||||
|
- Self-hosted architecture with Docker deployment
|
||||||
|
- JSON import for LLM-generated task trees
|
||||||
|
- Technology Stack:
|
||||||
|
- Backend: Python FastAPI with SQLAlchemy ORM
|
||||||
|
- Database: SQLite with self-referencing Task model
|
||||||
|
- Frontend: React + Tailwind CSS
|
||||||
|
- Deployment: Docker with nginx reverse proxy
|
||||||
|
- Project Management:
|
||||||
|
- Create/read/update/delete projects
|
||||||
|
- Project-specific task trees
|
||||||
|
- Task Management:
|
||||||
|
- Create tasks with title, description, status
|
||||||
|
- Four status types: Backlog, In Progress, Blocked, Done
|
||||||
|
- Hierarchical task nesting (task → subtask → sub-subtask → ...)
|
||||||
|
- Add subtasks to any task
|
||||||
|
- Delete tasks (cascading to all subtasks)
|
||||||
|
- Tree View:
|
||||||
|
- Collapsible hierarchical display
|
||||||
|
- Expand/collapse subtasks
|
||||||
|
- Visual nesting indentation
|
||||||
|
- Inline editing
|
||||||
|
- Status display
|
||||||
|
- Kanban Board:
|
||||||
|
- Four columns: Backlog, In Progress, Blocked, Done
|
||||||
|
- Drag-and-drop to change status
|
||||||
|
- All tasks shown as cards (including subtasks)
|
||||||
|
- JSON Import:
|
||||||
|
- Bulk import task trees from JSON files
|
||||||
|
- Supports arbitrary nesting depth
|
||||||
|
- Example import file included
|
||||||
|
- UI/UX:
|
||||||
|
- Dark cyberpunk theme
|
||||||
|
- Orange (#ff6b35) accent color
|
||||||
|
- Responsive design
|
||||||
|
- Real-time updates
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
- Backend API endpoints:
|
||||||
|
- `/api/projects` - Project CRUD
|
||||||
|
- `/api/tasks` - Task CRUD
|
||||||
|
- `/api/projects/{id}/tree` - Hierarchical task tree
|
||||||
|
- `/api/projects/{id}/tasks` - Flat task list
|
||||||
|
- `/api/projects/{id}/import` - JSON import
|
||||||
|
- Database Schema:
|
||||||
|
- `projects` table with id, name, description
|
||||||
|
- `tasks` table with self-referencing `parent_task_id`
|
||||||
|
- Frontend Routing:
|
||||||
|
- `/` - Project list
|
||||||
|
- `/project/:id` - Project view with Tree/Kanban toggle
|
||||||
|
- Docker Setup:
|
||||||
|
- Multi-stage builds for optimization
|
||||||
|
- Nginx reverse proxy configuration
|
||||||
|
- Named volumes for database persistence
|
||||||
|
- Development and production configurations
|
||||||
|
|
||||||
|
## Project Information
|
||||||
|
|
||||||
|
**TESSERACT** - Task Decomposition Engine
|
||||||
|
A self-hosted web application for managing deeply nested todo trees with advanced time tracking and project planning capabilities.
|
||||||
|
|
||||||
|
**Repository**: https://github.com/serversdwn/tesseract
|
||||||
|
**License**: MIT
|
||||||
|
**Author**: serversdwn
|
||||||
@@ -92,6 +92,32 @@ def get_task_with_subtasks(db: Session, task_id: int) -> Optional[models.Task]:
|
|||||||
).filter(models.Task.id == task_id).first()
|
).filter(models.Task.id == task_id).first()
|
||||||
|
|
||||||
|
|
||||||
|
def check_and_update_parent_status(db: Session, parent_id: int):
|
||||||
|
"""Check if all children of a parent are done, and mark parent as done if so"""
|
||||||
|
# Get all children of this parent
|
||||||
|
children = db.query(models.Task).filter(
|
||||||
|
models.Task.parent_task_id == parent_id
|
||||||
|
).all()
|
||||||
|
|
||||||
|
# If no children, nothing to do
|
||||||
|
if not children:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if all children are done
|
||||||
|
all_done = all(child.status == models.TaskStatus.DONE for child in children)
|
||||||
|
|
||||||
|
if all_done:
|
||||||
|
# Mark parent as done
|
||||||
|
parent = get_task(db, parent_id)
|
||||||
|
if parent and parent.status != models.TaskStatus.DONE:
|
||||||
|
parent.status = models.TaskStatus.DONE
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
# Recursively check grandparent
|
||||||
|
if parent.parent_task_id:
|
||||||
|
check_and_update_parent_status(db, parent.parent_task_id)
|
||||||
|
|
||||||
|
|
||||||
def update_task(
|
def update_task(
|
||||||
db: Session, task_id: int, task: schemas.TaskUpdate
|
db: Session, task_id: int, task: schemas.TaskUpdate
|
||||||
) -> Optional[models.Task]:
|
) -> Optional[models.Task]:
|
||||||
@@ -100,11 +126,23 @@ def update_task(
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
update_data = task.model_dump(exclude_unset=True)
|
update_data = task.model_dump(exclude_unset=True)
|
||||||
|
status_changed = False
|
||||||
|
|
||||||
|
# Check if status is being updated
|
||||||
|
if "status" in update_data:
|
||||||
|
status_changed = True
|
||||||
|
old_status = db_task.status
|
||||||
|
|
||||||
for key, value in update_data.items():
|
for key, value in update_data.items():
|
||||||
setattr(db_task, key, value)
|
setattr(db_task, key, value)
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(db_task)
|
db.refresh(db_task)
|
||||||
|
|
||||||
|
# If status changed to 'done' and this task has a parent, check if parent should auto-complete
|
||||||
|
if status_changed and db_task.status == models.TaskStatus.DONE and db_task.parent_task_id:
|
||||||
|
check_and_update_parent_status(db, db_task.parent_task_id)
|
||||||
|
|
||||||
return db_task
|
return db_task
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ function App() {
|
|||||||
<h1 className="text-2xl font-bold text-cyber-orange">
|
<h1 className="text-2xl font-bold text-cyber-orange">
|
||||||
TESSERACT
|
TESSERACT
|
||||||
<span className="ml-3 text-sm text-gray-500">Task Decomposition Engine</span>
|
<span className="ml-3 text-sm text-gray-500">Task Decomposition Engine</span>
|
||||||
<span className="ml-2 text-xs text-gray-600">v0.1.3</span>
|
<span className="ml-2 text-xs text-gray-600">v0.1.4</span>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<SearchBar />
|
<SearchBar />
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ function TaskCard({ task, allTasks, onUpdate, onDragStart }) {
|
|||||||
<div className="flex items-center gap-2 mt-2">
|
<div className="flex items-center gap-2 mt-2">
|
||||||
{/* Time estimate */}
|
{/* Time estimate */}
|
||||||
{formatTimeWithTotal(task, allTasks) && (
|
{formatTimeWithTotal(task, allTasks) && (
|
||||||
<div className="flex items-center gap-1 text-xs text-gray-500">
|
<div className={`flex items-center gap-1 text-xs text-gray-500 ${task.status === 'done' ? 'line-through' : ''}`}>
|
||||||
<Clock size={11} />
|
<Clock size={11} />
|
||||||
<span>{formatTimeWithTotal(task, allTasks)}</span>
|
<span>{formatTimeWithTotal(task, allTasks)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ function TaskNode({ task, projectId, onUpdate, level = 0 }) {
|
|||||||
<div className="flex items-center gap-3 mt-1">
|
<div className="flex items-center gap-3 mt-1">
|
||||||
{/* Time estimate */}
|
{/* Time estimate */}
|
||||||
{formatTimeWithTotal(task) && (
|
{formatTimeWithTotal(task) && (
|
||||||
<div className="flex items-center gap-1 text-xs text-gray-500">
|
<div className={`flex items-center gap-1 text-xs text-gray-500 ${task.status === 'done' ? 'line-through' : ''}`}>
|
||||||
<Clock size={12} />
|
<Clock size={12} />
|
||||||
<span>{formatTimeWithTotal(task)}</span>
|
<span>{formatTimeWithTotal(task)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user