diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/devdocs/v1/IMPLEMENTATION_PLAN.md b/devdocs/v1/IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..e96c913 --- /dev/null +++ b/devdocs/v1/IMPLEMENTATION_PLAN.md @@ -0,0 +1,575 @@ +# GoalsBreakDown - Implementation Plan + +## 1. Project Structure + +``` +GoalsBreakDown/ +├── app.py # Flask application entry point +├── config.py # Configuration constants +├── database.py # TinyDB initialization and operations +├── auth.py # Authentication helpers +├── requirements.txt # Python dependencies +├── data/ # TinyDB data directory +│ └── db.json # TinyDB database file +├── static/ +│ ├── css/ +│ │ ├── style.css # Global styles +│ │ ├── goals.css # Goal page styles +│ │ └── tasks.css # Task page styles +│ └── js/ +│ ├── api.js # API client utilities +│ ├── auth.js # Authentication logic +│ ├── goals.js # Goal page logic +│ ├── tasks.js # Task page logic +│ └── sortable.min.js # Drag-and-drop library +└── templates/ + ├── base.html # Base template with navigation + ├── login.html # Login page + ├── register.html # Registration page + ├── goals.html # Goal CRUD page + └── tasks.html # Task CRUD page with scroll view +``` + +## 2. Dependencies & Environment Management + +### 2.1 Environment Setup (uv) +```bash +# Initialize project with uv +uv init --no-readme + +# Add dependencies +uv add flask tinydb bcrypt + +# Run the application +uv run python app.py +``` + +### 2.2 requirements.txt (managed by uv) +``` +flask==3.0.0 +tinydb==4.8.0 +bcrypt==4.1.2 +``` + +### 2.3 pyproject.toml (auto-generated by uv) +```toml +[project] +name = "goalsbreakdown" +version = "0.1.0" +requires-python = ">=3.10" +dependencies = [ + "flask>=3.0.0", + "tinydb>=4.8.0", + "bcrypt>=4.1.2", +] +``` + +## 3. Database Schema (TinyDB) + +### 3.1 Collections (TinyDB Tables) + +**users** table: +```json +{ + "user_id": 1, + "username": "admin", + "password_hash": "$2b$12$...", + "role": "admin", + "max_goals": 10 +} +``` + +**goals** table: +```json +{ + "goal_id": 1, + "user_id": 1, + "title": "Learn Python", + "activated": true +} +``` + +**tasks** table: +```json +{ + "task_id": 1, + "goal_id": 1, + "title": "Complete basics", + "desc": "Learn variables, loops, functions", + "status": "doing", + "start_time": "2026-05-08T10:00:00", + "finished_time": null, + "order": 1.0 +} +``` + +### 3.2 Database Initialization + +```python +# database.py responsibilities: +# - Initialize TinyDB with data/db.json +# - Create tables: users, goals, tasks +# - Seed admin user on first run (username: admin, password: admin123) +# - Provide CRUD helper functions for each table +``` + +## 4. API Endpoints + +### 4.1 Authentication + +| Method | Endpoint | Description | Auth Required | +|--------|----------|-------------|---------------| +| POST | `/api/auth/register` | Register new user | No | +| POST | `/api/auth/login` | Login user | No | +| POST | `/api/auth/logout` | Logout user | Yes | +| GET | `/api/auth/me` | Get current user info | Yes | + +**POST /api/auth/register** +- Request: `{ "username": str, "password": str }` +- Response: `{ "success": bool, "message": str, "user_id": int }` +- Validation: username unique, password min 6 chars +- Default: role="user", max_goals=5 + +**POST /api/auth/login** +- Request: `{ "username": str, "password": str }` +- Response: `{ "success": bool, "message": str, "user": { user object } }` + +**GET /api/auth/me** +- Response: `{ "user_id": int, "username": str, "role": str, "max_goals": int }` + +### 4.2 Goals + +| Method | Endpoint | Description | Auth Required | +|--------|----------|-------------|---------------| +| GET | `/api/goals` | List all goals for current user | Yes | +| POST | `/api/goals` | Create new goal | Yes | +| PUT | `/api/goals/` | Update goal | Yes | +| DELETE | `/api/goals/` | Delete goal | Yes | +| PATCH | `/api/goals//toggle` | Toggle activated status | Yes | + +**GET /api/goals** +- Response: `[ { goal objects } ]` +- Note: Returns all goals (activated and deactivated) + +**POST /api/goals** +- Request: `{ "title": str }` +- Response: `{ "success": bool, "goal_id": int, "message": str }` +- Validation: Check user's max_goals limit before creation + +**PUT /api/goals/** +- Request: `{ "title": str, "activated": bool }` +- Response: `{ "success": bool, "message": str }` +- Validation: Goal must belong to current user + +**DELETE /api/goals/** +- Response: `{ "success": bool, "message": str }` +- Cascade: Delete all tasks associated with this goal + +**PATCH /api/goals//toggle** +- Response: `{ "success": bool, "activated": bool }` + +### 4.3 Tasks + +| Method | Endpoint | Description | Auth Required | +|--------|----------|-------------|---------------| +| GET | `/api/tasks` | List all tasks (optionally filtered by goal) | Yes | +| POST | `/api/tasks` | Create new task | Yes | +| PUT | `/api/tasks/` | Update task | Yes | +| DELETE | `/api/tasks/` | Delete task | Yes | +| PATCH | `/api/tasks//status` | Update task status | Yes | +| PATCH | `/api/tasks//order` | Update task order | Yes | + +**GET /api/tasks** +- Query params: `?goal_id=` (optional filter) +- Response: `[ { task objects } ]` +- Sorting: Unfinished by `order` ASC, finished by `finished_time` DESC + +**POST /api/tasks** +- Request: `{ "goal_id": int, "title": str, "desc": str (optional) }` +- Response: `{ "success": bool, "task_id": int, "message": str }` +- Validation: goal_id must belong to current user and be activated + +**PUT /api/tasks/** +- Request: `{ "title": str, "desc": str }` +- Response: `{ "success": bool, "message": str }` + +**DELETE /api/tasks/** +- Response: `{ "success": bool, "message": str }` + +**PATCH /api/tasks//status** +- Request: `{ "status": "todo" | "doing" | "pending" | "done" }` +- Response: `{ "success": bool, "message": str }` +- Business logic: + - If status="doing": Check no other "doing" task exists for this goal + - If status="done": Set `finished_time` to current timestamp + - If status changed from "done" to other: Set `finished_time` to null + +**PATCH /api/tasks//order** +- Request: `{ "order": float }` +- Response: `{ "success": bool, "message": str }` +- Note: Used for drag-and-drop reordering + +### 4.4 Admin + +| Method | Endpoint | Description | Auth Required | +|--------|----------|-------------|---------------| +| GET | `/api/admin/users` | List all users | Yes (admin) | +| PUT | `/api/admin/users/` | Update user settings | Yes (admin) | + +**GET /api/admin/users** +- Response: `[ { user_id, username, role, max_goals } ]` + +**PUT /api/admin/users/** +- Request: `{ "max_goals": int, "role": str (optional) }` +- Response: `{ "success": bool, "message": str }` + +## 5. Frontend Components + +### 5.1 Page: Login (`/login`) + +**Components:** +- Username input field +- Password input field +- Login button +- Link to registration page + +**Flow:** +1. User enters credentials +2. POST to `/api/auth/login` +3. On success: redirect to `/goals` +4. On failure: show error message + +### 5.2 Page: Register (`/register`) + +**Components:** +- Username input field +- Password input field +- Confirm password input field +- Register button +- Link to login page + +**Flow:** +1. User enters credentials +2. POST to `/api/auth/register` +3. On success: auto-login and redirect to `/goals` +4. On failure: show error message + +### 5.3 Page: Goals (`/goals`) + +**Layout:** +``` ++------------------------------------------+ +| Navigation: [Goals] [Tasks] [Logout] | ++------------------------------------------+ +| [Create Goal Button] | ++------------------------------------------+ +| Goal List: | +| +--------------------------------------+ | +| | Goal Title | [Activate/Deactivate] | +| | | [Edit] [Delete] | | +| +--------------------------------------+ | +| | Goal Title | [Activate/Deactivate] | +| | | [Edit] [Delete] | | +| +--------------------------------------+ | ++------------------------------------------+ +``` + +**Components:** +- Goal list (table or card layout) +- Create goal modal/form +- Edit goal modal/form +- Activate/Deactivate toggle button +- Delete confirmation dialog + +**Interactions:** +- Click "Create Goal" → opens modal → submit → refresh list +- Click "Edit" → opens modal with pre-filled data → submit → refresh +- Click toggle → PATCH request → update UI +- Click "Delete" → confirmation → DELETE request → refresh + +### 5.4 Page: Tasks (`/tasks`) + +**Layout:** +``` ++--------------------------------------------------+ +| Navigation: [Goals] [Tasks] [Logout] | ++--------------------------------------------------+ +| Goal Selector: [Dropdown of activated goals] | ++--------------------------------------------------+ +| Task Scroll View (Time Picker Style): | +| | +| +---------------------------+ | +| | Task N-2 (todo) | [faded] | +| +---------------------------+ | +| | Task N-1 (todo) | [dimmed] | +| +---------------------------+ | +| | >>> Task N (doing) <<< | [HIGHLIGHTED] | +| +---------------------------+ | +| | Task N+1 (todo) | [normal] | +| +---------------------------+ | +| | Task N+2 (todo) | [faded] | +| +---------------------------+ | +| | +| [Create Task Button] | ++--------------------------------------------------+ +| Side Panel (when task selected): | +| +----------------------------------------------+ | +| | Task Title: [____________] | | +| | Description: [____________] | | +| | Status: [Dropdown: todo/doing/pending/done] | | +| | [Save] [Delete] | | +| +----------------------------------------------+ | ++--------------------------------------------------+ +``` + +**Components:** +- Goal selector dropdown (filters tasks by selected goal) +- Scroll view container with centered focus area +- Task items (draggable for unfinished tasks) +- Create task modal/form +- Side panel for task editing +- Status change buttons + +**Scroll View Implementation:** +- Container with `overflow-y: auto` +- Center item highlighted with CSS +- Scroll to center the "doing" task on load +- Use SortableJS for drag-and-drop reordering +- Only unfinished tasks are draggable +- Finished tasks shown in separate section below (ordered by finished_time) + +**Drag-and-Drop Flow:** +1. User drags task item +2. SortableJS handles reordering +3. On drop: calculate new `order` value +4. PATCH `/api/tasks//order` +5. Update local state + +**Side Panel Flow:** +1. User clicks task item +2. Side panel slides in from right +3. Panel shows editable fields +4. User modifies and clicks "Save" +5. PUT request to update task +6. Panel closes or stays open for further edits + +## 6. Authentication & Session Management + +### 6.1 Session Storage +- Use Flask's built-in session management +- Store `user_id` and `username` in session +- Session cookie is HTTP-only and secure + +### 6.2 Password Handling +- Hash passwords with bcrypt before storage +- Verify passwords with bcrypt.checkpw() +- Never store plain text passwords + +### 6.3 Middleware +- `@login_required` decorator for protected routes +- `@admin_required` decorator for admin routes +- Check user ownership on goal/task operations + +## 7. Business Logic Implementation + +### 7.1 Goal Limit Enforcement +```python +def can_create_goal(user_id): + user = get_user(user_id) + goal_count = count_goals_by_user(user_id) + return goal_count < user.max_goals +``` + +### 7.2 Focus Rule Enforcement +```python +def set_task_to_doing(task_id, goal_id): + # Check if another task is already "doing" + doing_task = find_task(goal_id, status="doing") + if doing_task and doing_task.task_id != task_id: + # Optionally auto-change old doing to todo + update_task_status(doing_task.task_id, "todo") + + update_task_status(task_id, "doing") +``` + +### 7.3 Task Ordering +```python +# Unfinished tasks: ordered by 'order' field (ascending) +# Finished tasks: ordered by 'finished_time' (descending) + +def get_tasks_for_goal(goal_id): + unfinished = query(status != "done").order_by("order") + finished = query(status == "done").order_by("finished_time", reverse=True) + return unfinished + finished +``` + +### 7.4 Drag-and-Drop Order Calculation +```python +def reorder_task(task_id, new_position, siblings): + # Use real numbers to avoid reordering all tasks + prev_order = siblings[new_position - 1].order if new_position > 0 else 0 + next_order = siblings[new_position + 1].order if new_position < len(siblings) - 1 else prev_order + 2 + new_order = (prev_order + next_order) / 2 + update_task_order(task_id, new_order) +``` + +## 8. Development Phases + +### Phase 1: Foundation (Setup & Auth) +**Setup:** +```bash +uv init --no-readme +uv add flask tinydb bcrypt +``` + +**Files to create:** +- `pyproject.toml` (auto-generated by uv) +- `uv.lock` (auto-generated by uv) +- `config.py` +- `database.py` +- `auth.py` +- `app.py` (basic setup) +- `templates/login.html` +- `templates/register.html` +- `static/css/style.css` +- `static/js/api.js` +- `static/js/auth.js` + +**Deliverables:** +- Flask app running on localhost:5000 (`uv run python app.py`) +- User registration with validation +- User login/logout with session management +- Password hashing with bcrypt +- Admin user seeded on first run + +### Phase 2: Goal CRUD +**Files to create:** +- `templates/goals.html` +- `static/css/goals.css` +- `static/js/goals.js` + +**API endpoints to implement:** +- GET/POST/PUT/DELETE `/api/goals` +- PATCH `/api/goals//toggle` + +**Deliverables:** +- Goal list page with all CRUD operations +- Activate/deactivate toggle +- Goal limit enforcement +- Delete confirmation dialog + +### Phase 3: Task CRUD (Basic) +**Files to create:** +- `templates/tasks.html` +- `static/css/tasks.css` +- `static/js/tasks.js` + +**API endpoints to implement:** +- GET/POST/PUT/DELETE `/api/tasks` +- PATCH `/api/tasks//status` + +**Deliverables:** +- Task list filtered by goal +- Task creation with goal selection +- Task editing in side panel +- Status change with focus rule enforcement +- Finished time tracking + +### Phase 4: Task Ordering & Drag-Drop +**Files to modify:** +- `static/js/tasks.js` (add drag-drop logic) +- `static/css/tasks.css` (add scroll view styles) + +**API endpoints to implement:** +- PATCH `/api/tasks//order` + +**Deliverables:** +- Scroll view with centered focus +- Highlighted "doing" task +- Drag-and-drop reordering with SortableJS +- Order calculation with real numbers +- Finished tasks section (sorted by finished_time) + +### Phase 5: Admin Features +**Files to create:** +- Admin section in goals.html or separate page +- `static/js/admin.js` (if needed) + +**API endpoints to implement:** +- GET `/api/admin/users` +- PUT `/api/admin/users/` + +**Deliverables:** +- Admin can view all users +- Admin can modify user's max_goals +- Admin can change user role + +### Phase 6: Polish & Testing +**Tasks:** +- Error handling and user feedback +- Loading states and animations +- Responsive design for mobile +- Input validation on frontend +- Edge case handling (empty states, etc.) +- Manual testing of all flows + +## 9. Configuration (config.py) + +```python +# Database +DB_PATH = "data/db.json" + +# Default admin credentials +DEFAULT_ADMIN_USERNAME = "admin" +DEFAULT_ADMIN_PASSWORD = "admin123" + +# Default user settings +DEFAULT_MAX_GOALS = 5 + +# Flask +SECRET_KEY = "your-secret-key-here" # Change in production +DEBUG = True +HOST = "0.0.0.0" +PORT = 5000 +``` + +## 10. Error Handling + +### API Error Responses +```json +{ + "success": false, + "message": "Error description", + "error_code": "VALIDATION_ERROR | NOT_FOUND | UNAUTHORIZED | FORBIDDEN" +} +``` + +### HTTP Status Codes +- 200: Success +- 201: Created +- 400: Bad Request (validation error) +- 401: Unauthorized (not logged in) +- 403: Forbidden (insufficient permissions) +- 404: Not Found +- 500: Internal Server Error + +## 11. Security Considerations + +- Password hashing with bcrypt (cost factor 12) +- Session-based authentication with HTTP-only cookies +- CSRF protection (Flask-WTF or manual token) +- Input sanitization (prevent XSS) +- SQL injection not applicable (TinyDB), but validate all inputs +- Rate limiting on auth endpoints (optional) +- HTTPS in production + +## 12. Future Enhancements (Out of Scope) + +- Task categories/tags +- Task priority levels +- Task search and filtering +- Export/import data +- Email notifications +- Task templates +- Progress tracking per goal +- Mobile app diff --git a/devdocs/v1/PRD.md b/devdocs/v1/PRD.md new file mode 100644 index 0000000..20fcbaf --- /dev/null +++ b/devdocs/v1/PRD.md @@ -0,0 +1,99 @@ + + + + +Task Table: + +- task_id: primary key +- title: str (required, non-empty) +- desc: str (optional, can be empty) +- status: enum{todo, doing, pending, done} +- goal_id: foreign key to goal table (required, each task belongs to exactly one goal) +- start_time: timestamp or None +- finished_time: timestamp or None +- order: float (for ordering unfinished tasks) + +Goal Table: + +- goal_id: primary key +- title: str (required, non-empty) +- activated: bool (deactivated goals and their tasks are hidden from Task Page GUI) + +User Table: + +- user_id: primary key +- username: str (unique, required) +- password_hash: str (required) +- role: enum{user, admin} (admin can configure goal limits) +- max_goals: int (maximum goals this user can create, configurable by admin) + + +GUI: + +Page 1. Goal CRUD +- List all goals with activate/deactivate toggle +- Create, edit, delete goals +- Shows goal title and activation status + +Page 2. Task CRUD +- Tasks are grouped by their parent goal +- Only activated goals and their tasks are visible +- Focus Rule: Each goal highlights its single "doing" task prominently +- Scroll View: Tasks displayed in a scrollable selector (like time picker widget) + - Center/focus area shows current task + - Unfinished tasks ordered by `order` field + - Finished tasks ordered by `finished_time` (most recent first) + - Drag-and-drop reordering for unfinished tasks +- Side Panel: Clicking a task opens a side panel for editing task details +- Task status transitions: + - User manually switches task to "doing" (only one per goal) + - When "doing" task is completed, status becomes "done" (no auto-promotion) + - User must manually set next task to "doing" + + +Constraints: + +1. **Focus Rule**: Under each goal, there should be only one "doing" task. The GUI must visually emphasize the "doing" task for each goal. + +2. **Ordering Rule**: + - Finished tasks are ordered by `finished_time` (descending) + - Unfinished tasks are ordered by `order` field (ascending) + - Users can reorder unfinished tasks via drag-and-drop + +3. **Scroll View**: Tasks are displayed in a linear scroll view selector (similar to HH:mm time picker). The current task is centered/focused. Users can drag-and-drop to reorder future tasks. + +4. **Task-Good Relationship**: Tasks cannot exist without a goal. Each task belongs to exactly one goal. + +5. **Goal Limit**: Each user can only create up to their `max_goals` limit. Admin can modify this limit per user. + +6. **Visibility**: Deactivated goals and all their tasks are hidden from the Task Page. + + +Business Rules: + +- Task title is required and cannot be empty +- Goal title is required and cannot be empty +- Tasks can be deleted (order values don't need to be continuous) +- Goals can be activated/deactivated (only affects GUI visibility) +- Only one "doing" task per goal at any time +- Users must manually set a task to "doing" status +- Simple user registration with username/password + + +Tech Stack: + +- Backend: Python + Flask (lightweight, good for small apps) +- Frontend: Vanilla JS + HTML/CSS (simple, no build step needed) +- Database: TinyDB (single JSON file, perfect for this scale) +- Authentication: Session-based with password hashing (bcrypt) +- Drag-and-drop: HTML5 Drag and Drop API or SortableJS + + +Multi-User Features: + +- User registration and login +- Each user has isolated goals and tasks +- Admin user can: + - Configure `max_goals_per_user` limit + - View all users (optional) +- Regular users can only see/manage their own data \ No newline at end of file