diff options
Diffstat (limited to 'issues')
| -rw-r--r-- | issues/001-hide-future-tasks-behind-fold.md | 51 | ||||
| -rw-r--r-- | issues/002-modal-menu-quick-add-bug-report.md | 47 | ||||
| -rw-r--r-- | issues/003-fix-tap-to-expand.md | 48 | ||||
| -rw-r--r-- | issues/004-integrate-terst-org.md | 62 | ||||
| -rw-r--r-- | issues/005-visual-task-timing-differentiation.md | 61 | ||||
| -rw-r--r-- | issues/006-reorder-tasks-by-urgency.md | 63 | ||||
| -rw-r--r-- | issues/007-fix-outdated-todoist-link.md | 54 | ||||
| -rw-r--r-- | issues/008-add-google-calendar-support.md | 85 | ||||
| -rw-r--r-- | issues/009-keep-completed-tasks-visible.md | 54 | ||||
| -rw-r--r-- | issues/010-fix-quick-add-timestamp.md | 76 | ||||
| -rw-r--r-- | issues/011-add-timeline-view.md | 86 | ||||
| -rw-r--r-- | issues/013-quick-add-shopping-list.md | 81 | ||||
| -rw-r--r-- | issues/015-random-landscape-background.md | 77 | ||||
| -rw-r--r-- | issues/016-click-task-edit-details.md | 80 |
14 files changed, 925 insertions, 0 deletions
diff --git a/issues/001-hide-future-tasks-behind-fold.md b/issues/001-hide-future-tasks-behind-fold.md new file mode 100644 index 0000000..f6012fe --- /dev/null +++ b/issues/001-hide-future-tasks-behind-fold.md @@ -0,0 +1,51 @@ +# [FEATURE] Hide future tasks behind a fold + +## Description +Hide tasks more than 3-7 days out behind a fold. + +## User Story +As a user, I want tasks more than N days out hidden by default so that I can focus on immediate priorities. + +## Technical Context +- Affects: `internal/handlers/handlers.go` (task filtering), `web/templates/partials/todoist-tasks.html` or `tasks-tab.html` +- Data already has `due_date`; filtering is a presentation concern + +## Test Strategy + +### Unit Test (Red) +**File:** `internal/handlers/handlers_test.go` + +Test a new filter function `FilterTasksByHorizon(tasks []Task, days int)` returns only tasks within range. + +```go +func TestFilterTasksByHorizon(t *testing.T) { + // Given tasks with varying due dates + // When filtered with horizon of 7 days + // Then only tasks within 7 days are in "visible" slice + // And remaining tasks are in "deferred" slice +} +``` + +### E2E Test (Red) +Verify collapsed section renders with count badge; click expands to show deferred tasks. + +## Proposed Approach + +1. Add config param `task_horizon_days` (default 7) +2. Partition tasks in handler into "visible" vs "deferred" +3. Render deferred in a collapsible `<details>` element with HTMX swap +4. Show count badge on collapsed header (e.g., "+12 later") + +## Affected Components +- `internal/handlers/handlers.go` +- `internal/config/config.go` +- `web/templates/partials/todoist-tasks.html` +- `web/templates/partials/tasks-tab.html` + +## Definition of Done +- [ ] Tasks beyond horizon hidden by default +- [ ] Collapsible UI shows count of hidden tasks +- [ ] Click to expand shows all deferred tasks +- [ ] Horizon days configurable +- [ ] Unit tests pass +- [ ] E2E test confirms behavior diff --git a/issues/002-modal-menu-quick-add-bug-report.md b/issues/002-modal-menu-quick-add-bug-report.md new file mode 100644 index 0000000..b518cf3 --- /dev/null +++ b/issues/002-modal-menu-quick-add-bug-report.md @@ -0,0 +1,47 @@ +# [FEATURE] Quick add & bug report in modal menu + +## Description +Put quick add (rename "add" to "quick add") and bug report in a unified modal menu. + +## User Story +As a user, I want quick-add and bug-report behind a unified modal so the UI is cleaner. + +## Technical Context +- Affects: `web/templates/index.html` (modal container), new partial `partials/modal-menu.html` +- Existing handlers for quick-add and bug already exist; this is UI consolidation +- Note: Issue #14 is a duplicate of this + +## Test Strategy + +### E2E/Integration Test (Red) +Test modal opens on trigger, contains both forms, submits correctly. + +``` +1. Click menu trigger -> modal opens +2. Assert "Add Task" and "Report Bug" tabs/options visible +3. Fill quick-add form -> submit -> task created +4. Switch to bug tab -> fill -> submit -> bug created +5. Modal closes after successful submit +``` + +## Proposed Approach + +1. Create shared modal partial with tab/toggle between "Add Task" and "Report Bug" +2. Use HTMX `hx-get` to lazy-load modal content +3. Add keyboard shortcut (e.g., `Cmd+K` or `Ctrl+K`) to invoke +4. Rename "Add" button to "Quick Add" throughout UI +5. Single FAB or menu button replaces separate add/bug buttons + +## Affected Components +- `web/templates/index.html` +- `web/templates/partials/modal-menu.html` (new) +- `web/static/` (JS for keyboard shortcut) +- Existing quick-add and bug handlers (no changes, just UI consolidation) + +## Definition of Done +- [ ] Single modal contains both forms +- [ ] Tab/toggle switches between quick-add and bug report +- [ ] Keyboard shortcut opens modal +- [ ] Both forms submit correctly +- [ ] Modal closes on successful submit +- [ ] Old separate UI elements removed diff --git a/issues/003-fix-tap-to-expand.md b/issues/003-fix-tap-to-expand.md new file mode 100644 index 0000000..a1cfd8c --- /dev/null +++ b/issues/003-fix-tap-to-expand.md @@ -0,0 +1,48 @@ +# [BUG] Tap to expand doesn't work + +## Description +Tap to expand doesn't work on task items. + +## Technical Context +- Likely a JS/HTMX event binding issue in task list items +- Affects: `partials/todoist-tasks.html` or `tasks-tab.html`, possibly JS in `web/static/` +- May be related to event delegation on dynamically-loaded content + +## Test Strategy + +### E2E Test (Red) +Playwright/browser test: click task row -> assert expanded content visible. + +``` +1. Load tasks page +2. Click on a task row +3. Assert expanded details section becomes visible +4. Assert expanded content contains expected task details +``` + +## Proposed Approach + +1. Inspect current expand trigger mechanism: + - Is it `hx-get` with `hx-trigger="click"`? + - Is it a JS click handler? + - Is it a `<details>` element? +2. Check for missing `hx-trigger` or event delegation issues on dynamically-loaded content +3. Ensure `htmx:afterSwap` rebinds event listeners if needed +4. Test on both desktop (click) and mobile (tap) + +## Debugging Steps +1. Open browser dev tools -> check for JS errors on click +2. Inspect element -> verify HTMX attributes present +3. Check Network tab -> see if request fires on click +4. If using JS handlers, verify they're attached after HTMX swaps + +## Affected Components +- `web/templates/partials/todoist-tasks.html` +- `web/templates/partials/tasks-tab.html` +- `web/static/` (if JS-based) + +## Definition of Done +- [ ] Clicking/tapping a task expands its details +- [ ] Works on desktop and mobile +- [ ] Works on initial load and after HTMX swaps +- [ ] E2E test confirms behavior diff --git a/issues/004-integrate-terst-org.md b/issues/004-integrate-terst-org.md new file mode 100644 index 0000000..9ec79bf --- /dev/null +++ b/issues/004-integrate-terst-org.md @@ -0,0 +1,62 @@ +# [FEATURE] Integrate terst.org start page and styling + +## Description +Integrate terst.org start page widgets and visual styling. + +## User Story +As a user, I want the dashboard to incorporate my terst.org start page widgets and match its visual styling for a unified experience. + +## Technical Context +- Two aspects: visual styling + widget integration +- Requires understanding terst.org's current design system and widget architecture +- May need API access or scraping depending on terst.org implementation + +## Test Strategy + +### E2E Test (Red) +``` +1. Load dashboard +2. Assert color scheme matches terst.org (primary colors, fonts) +3. Assert expected widgets render (TBD based on widget inventory) +``` + +## Proposed Approach + +### Phase 1: Visual Styling +1. **Audit terst.org design:** + - Extract color palette (primary, secondary, accent) + - Identify fonts and typography scale + - Note spacing/layout patterns +2. **Update Tailwind config:** + - Add terst.org colors to theme + - Update default styles to match +3. **Apply to existing components** + +### Phase 2: Widget Integration +1. **Inventory terst.org widgets:** + - What widgets exist? (clock, weather, links, etc.) + - How are they implemented? (static HTML, JS, API-backed?) +2. **Integration options:** + - Option A: Embed via iframe (quick but limited) + - Option B: Recreate widgets natively (more work, better integration) + - Option C: API calls to terst.org backend (if available) +3. **Implement priority widgets first** + +## Discovery Required +- [ ] Access terst.org to inventory widgets +- [ ] Extract color palette and design tokens +- [ ] Determine widget data sources + +## Affected Components +- `tailwind.config.js` (theme colors) +- `web/static/css/` (global styles) +- `web/templates/index.html` (widget containers) +- `web/templates/partials/` (new widget partials) +- Potentially `internal/handlers/` (widget data endpoints) + +## Definition of Done +- [ ] Color scheme matches terst.org +- [ ] Typography matches terst.org +- [ ] Priority widgets integrated +- [ ] Responsive layout preserved +- [ ] E2E test verifies styling diff --git a/issues/005-visual-task-timing-differentiation.md b/issues/005-visual-task-timing-differentiation.md new file mode 100644 index 0000000..4cc40b4 --- /dev/null +++ b/issues/005-visual-task-timing-differentiation.md @@ -0,0 +1,61 @@ +# [FEATURE] Visual differentiation for task timing + +## Description +Tasks after today should be grayed out, past deadline should be emphasized. + +## User Story +As a user, I want past-due tasks highlighted and future tasks dimmed so urgency is immediately visible. + +## Technical Context +- Pure presentation logic in template +- Affects: `partials/todoist-tasks.html`, `partials/tasks-tab.html` +- Requires comparing task `due_date` against current date + +## Test Strategy + +### Unit Test (Red) +**File:** `internal/handlers/handlers_test.go` + +Test helper function returns correct urgency class. + +```go +func TestTaskUrgencyClass(t *testing.T) { + tests := []struct { + name string + dueDate time.Time + expected string + }{ + {"overdue yesterday", yesterday, "overdue"}, + {"due today", today, "today"}, + {"due tomorrow", tomorrow, "future"}, + {"no due date", zeroTime, "none"}, + } + // ... +} +``` + +### E2E Test (Red) +Assert CSS classes applied correctly to tasks based on due date. + +## Proposed Approach + +1. Add template helper or compute urgency class in handler +2. Return struct with `UrgencyClass` field for each task +3. Apply Tailwind classes based on urgency: + - `text-red-600 font-bold` (overdue) + - `text-inherit` or default (today) + - `text-gray-400` (future) +4. Consider adding visual indicator icon (e.g., warning for overdue) + +## Affected Components +- `internal/handlers/handlers.go` +- `internal/models/types.go` (if adding field to view model) +- `web/templates/partials/todoist-tasks.html` +- `web/templates/partials/tasks-tab.html` + +## Definition of Done +- [ ] Overdue tasks visually emphasized (red/bold) +- [ ] Future tasks grayed out +- [ ] Today's tasks normal styling +- [ ] Unit tests for urgency calculation +- [ ] E2E test verifies correct classes applied diff --git a/issues/006-reorder-tasks-by-urgency.md b/issues/006-reorder-tasks-by-urgency.md new file mode 100644 index 0000000..8a5f968 --- /dev/null +++ b/issues/006-reorder-tasks-by-urgency.md @@ -0,0 +1,63 @@ +# [FEATURE] Reorder task list by urgency + +## Description +List past due tasks, then tasks with a specific time today, then 12am (all-day) tasks today, then future tasks. + +## User Story +As a user, I want tasks sorted by urgency tiers so I see what needs attention first. + +## Technical Context +- Affects: `internal/handlers/handlers.go` — sorting logic before template render +- Requires distinguishing "has specific time" vs "all-day" (midnight/00:00) +- Todoist API may return all-day tasks with time set to 00:00:00 + +## Test Strategy + +### Unit Test (Red) +**File:** `internal/handlers/handlers_test.go` + +```go +func TestSortTasksByUrgency(t *testing.T) { + tasks := []Task{ + {Name: "future", DueDate: tomorrow}, + {Name: "overdue", DueDate: yesterday}, + {Name: "today-timed", DueDate: todayAt(14, 30)}, + {Name: "today-allday", DueDate: todayAt(0, 0)}, + } + + sorted := SortTasksByUrgency(tasks) + + // Expected order: overdue, today-timed, today-allday, future + assert.Equal(t, "overdue", sorted[0].Name) + assert.Equal(t, "today-timed", sorted[1].Name) + assert.Equal(t, "today-allday", sorted[2].Name) + assert.Equal(t, "future", sorted[3].Name) +} +``` + +## Proposed Approach + +1. Define sort buckets with priority: + - Priority 0: Overdue (due_date < today) + - Priority 1: Today with specific time (due_date == today && time != 00:00) + - Priority 2: Today all-day (due_date == today && time == 00:00) + - Priority 3: Future (due_date > today) +2. Within each bucket, sort by due_date ascending +3. Implement stable sort to preserve original order within equal items + +## Traps to Avoid +- **Timezone handling:** Ensure consistent timezone (user's local or UTC?) for date comparisons +- **Null due dates:** Decide where tasks with no due date should appear (suggest: after future tasks) +- **All-day detection:** Todoist may use different conventions; verify API response format + +## Affected Components +- `internal/handlers/handlers.go` +- `internal/handlers/handlers_test.go` + +## Definition of Done +- [ ] Tasks sorted in specified order +- [ ] Overdue tasks appear first +- [ ] Timed tasks today before all-day tasks today +- [ ] Future tasks appear last +- [ ] Null due date tasks handled gracefully +- [ ] Unit tests cover all buckets and edge cases diff --git a/issues/007-fix-outdated-todoist-link.md b/issues/007-fix-outdated-todoist-link.md new file mode 100644 index 0000000..5d628f2 --- /dev/null +++ b/issues/007-fix-outdated-todoist-link.md @@ -0,0 +1,54 @@ +# [BUG] Fix outdated Todoist task link + +## Description +Fix outdated Todoist task link. + +## Technical Context +- Affects: `internal/api/todoist.go` (URL construction) or stored `url` field in DB +- Todoist may have changed their URL scheme +- Current URLs may be returning 404 or redirecting incorrectly + +## Test Strategy + +### Unit Test (Red) +**File:** `internal/api/todoist_test.go` + +```go +func TestBuildTaskURL(t *testing.T) { + taskID := "123456789" + url := BuildTaskURL(taskID) + + // Assert current Todoist URL format + // Option A: https://todoist.com/showTask?id=123456789 + // Option B: https://todoist.com/app/task/123456789 + assert.Equal(t, "https://todoist.com/app/task/123456789", url) +} +``` + +## Proposed Approach + +1. Research current Todoist web URL format: + - Check Todoist docs or inspect working links in browser + - Likely format: `https://todoist.com/app/task/{task_id}` or `https://todoist.com/showTask?id={task_id}` +2. Update URL builder in `internal/api/todoist.go` +3. Consider migration for existing cached URLs in database: + - Option A: Update on read (lazy migration) + - Option B: Run migration script to update all stored URLs +4. If URL comes from API response directly, verify we're using the correct field + +## Debugging Steps +1. Find a task ID in the database +2. Try current URL format in browser +3. Try alternative formats to find working one +4. Check Todoist API response for canonical URL field + +## Affected Components +- `internal/api/todoist.go` +- `internal/api/todoist_test.go` +- Potentially `internal/store/sqlite.go` (if migration needed) + +## Definition of Done +- [ ] Task links open correct Todoist page +- [ ] URL builder uses current format +- [ ] Existing cached URLs updated or migrated +- [ ] Unit test validates URL format diff --git a/issues/008-add-google-calendar-support.md b/issues/008-add-google-calendar-support.md new file mode 100644 index 0000000..f3a1f3b --- /dev/null +++ b/issues/008-add-google-calendar-support.md @@ -0,0 +1,85 @@ +# [FEATURE] Add Google Calendar support + +## Description +Add Google Calendar support. + +## User Story +As a user, I want my Google Calendar events displayed so I have a unified daily view. + +## Technical Context +- New integration following existing patterns in `internal/api/` +- OAuth2 flow required; store refresh token in `sync_tokens` table +- New partial for calendar events display + +## Test Strategy + +### Unit Test (Red) +**File:** `internal/api/gcal_test.go` + +```go +func TestParseCalendarEvents(t *testing.T) { + // Mock Google Calendar API response + // Assert events parsed correctly with title, time, all-day flag +} + +func TestGCalClient_FetchEvents(t *testing.T) { + // Mock HTTP client + // Assert correct API calls made + // Assert events returned in expected format +} +``` + +### Integration Test (Red) +Test cache/store roundtrip for events. + +```go +func TestGCalEventsCache(t *testing.T) { + // Store events in cache + // Retrieve from cache + // Assert data integrity +} +``` + +## Proposed Approach + +1. **OAuth Setup:** + - Add `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` to config + - Implement OAuth2 flow with PKCE + - Store refresh token in `sync_tokens` table + +2. **API Client:** + - Create `internal/api/gcal.go` implementing interface pattern from existing clients + - Use `google.golang.org/api/calendar/v3` + - Fetch events for configurable date range (default: today + 7 days) + +3. **Data Model:** + - Add `calendar_events` table or reuse existing structure + - Handle all-day vs timed events + +4. **UI:** + - New partial: `partials/gcal-events.html` + - Integrate into existing tabs or new calendar tab + +## Traps to Avoid +- **All-day events:** Google returns these differently (date vs dateTime) +- **Timezone:** Events have their own timezone; convert to user's local +- **Recurring events:** Decide whether to expand or show as single item +- **Token refresh:** Handle expired access tokens gracefully + +## Affected Components +- `internal/api/gcal.go` (new) +- `internal/api/gcal_test.go` (new) +- `internal/api/interfaces.go` (add interface) +- `internal/config/config.go` +- `internal/store/sqlite.go` (new table/queries) +- `web/templates/partials/gcal-events.html` (new) +- `cmd/dashboard/main.go` (wire up client) + +## Definition of Done +- [ ] OAuth2 flow authenticates with Google +- [ ] Events fetched from Google Calendar API +- [ ] Events cached in SQLite +- [ ] Events displayed in UI +- [ ] All-day and timed events handled correctly +- [ ] Token refresh works +- [ ] Unit and integration tests pass diff --git a/issues/009-keep-completed-tasks-visible.md b/issues/009-keep-completed-tasks-visible.md new file mode 100644 index 0000000..b80eaca --- /dev/null +++ b/issues/009-keep-completed-tasks-visible.md @@ -0,0 +1,54 @@ +# [FEATURE] Keep completed tasks visible until refresh + +## Description +Leave completed tasks in view until refresh and don't confirm completion. + +## User Story +As a user, I want completed tasks to stay visible (struck through) until I refresh so I can see my progress and undo mistakes. + +## Technical Context +- Affects: `partials/todoist-tasks.html` (HTMX swap behavior), possibly JS state +- Currently completion likely removes item via `hx-swap="delete"` or similar +- Need to change to in-place update with completed styling + +## Test Strategy + +### E2E Test (Red) +``` +1. Load tasks page with uncompleted task +2. Click complete button on task +3. Assert task still visible with .completed class (strikethrough) +4. Assert no confirmation dialog appeared +5. Refresh page +6. Assert completed task no longer visible +``` + +## Proposed Approach + +1. **Change completion endpoint response:** + - Instead of empty response (for delete), return updated task HTML + - Task HTML includes completed styling (strikethrough, muted colors) + +2. **Update HTMX swap:** + - Change from `hx-swap="delete"` or `hx-swap="outerHTML"` with empty + - To `hx-swap="outerHTML"` with completed task HTML + +3. **Remove confirmation:** + - Remove any `hx-confirm` attribute on complete button + - Or remove JS confirmation dialog if present + +4. **Completed task styling:** + - Add `.completed` class with `line-through text-gray-400` + - Optionally add "undo" button that appears on completed tasks + +## Affected Components +- `internal/handlers/handlers.go` (completion endpoint response) +- `web/templates/partials/todoist-tasks.html` +- `web/templates/partials/task-item.html` (if exists, for completed variant) + +## Definition of Done +- [ ] Completing a task keeps it visible with strikethrough +- [ ] No confirmation dialog on completion +- [ ] Task disappears on page refresh +- [ ] Optional: Undo button on completed tasks +- [ ] E2E test confirms behavior diff --git a/issues/010-fix-quick-add-timestamp.md b/issues/010-fix-quick-add-timestamp.md new file mode 100644 index 0000000..3c59325 --- /dev/null +++ b/issues/010-fix-quick-add-timestamp.md @@ -0,0 +1,76 @@ +# [BUG] Quick add date defaults to tomorrow in evening + +## Description +The date pre-filled in quick add defaults to tomorrow when used during the evening. + +## Symptom +When adding a task in the evening, the date field pre-fills with tomorrow's date instead of today. + +## Technical Context +- Likely a timezone or date boundary issue +- Server may be using UTC while user expects local time +- Or JavaScript date handling crossing midnight boundary incorrectly + +## Test Strategy + +### Unit Test (Red) +**File:** `internal/handlers/handlers_test.go` + +```go +func TestQuickAddDefaultDate(t *testing.T) { + tests := []struct { + name string + serverTime time.Time + userTZ string + expectedDate string + }{ + { + name: "evening in US Eastern", + serverTime: time.Date(2026, 1, 21, 23, 0, 0, 0, time.UTC), // 6pm ET + userTZ: "America/New_York", + expectedDate: "2026-01-21", // Should be today, not tomorrow + }, + { + name: "late night edge case", + serverTime: time.Date(2026, 1, 22, 4, 0, 0, 0, time.UTC), // 11pm ET + userTZ: "America/New_York", + expectedDate: "2026-01-21", + }, + } + // ... +} +``` + +## Proposed Approach + +1. **Identify the source:** + - Check quick-add handler: how is default date determined? + - Check template: is date set server-side or client-side JS? + - Check if using `time.Now()` (server's timezone) vs user's timezone + +2. **Likely fix:** + - If server-side: pass user's timezone preference and convert + - If client-side: use `new Date()` which uses browser's local time + - Ensure consistent timezone handling throughout + +3. **Verify date calculation:** + - `today` should be based on user's local date, not UTC date + - Evening in US Eastern (UTC-5) at 8pm = UTC next day 1am + +## Debugging Steps +1. Check current time on server vs browser +2. Inspect what value is sent in quick-add form +3. Check JavaScript date initialization if client-side +4. Check Go `time.Now()` usage if server-side + +## Affected Components +- `internal/handlers/handlers.go` (quick-add endpoint/template data) +- `web/templates/partials/` (quick-add form) +- `web/static/` (if JS sets date) +- Potentially user preferences for timezone + +## Definition of Done +- [ ] Quick add defaults to user's local today, regardless of time +- [ ] Works correctly in evening hours +- [ ] Works across timezone boundaries +- [ ] Unit test covers evening edge case diff --git a/issues/011-add-timeline-view.md b/issues/011-add-timeline-view.md new file mode 100644 index 0000000..49e744a --- /dev/null +++ b/issues/011-add-timeline-view.md @@ -0,0 +1,86 @@ +# [FEATURE] Add timeline view + +## Description +Add timeline view. + +## User Story +As a user, I want a timeline/agenda view so I can see my day's schedule visually. + +## Technical Context +- New view aggregating multiple data sources by time +- Requires merging tasks + meals + (future) calendar events +- New tab or view mode in existing UI + +## Test Strategy + +### Unit Test (Red) +**File:** `internal/handlers/handlers_test.go` + +```go +func TestBuildTimeline(t *testing.T) { + tasks := []Task{{Name: "Task 1", DueDate: todayAt(10, 0)}} + meals := []Meal{{Name: "Lunch", Date: today, Type: "lunch"}} // lunch = 12:00 + + timeline := BuildTimeline(tasks, meals) + + assert.Len(t, timeline, 2) + assert.Equal(t, "Task 1", timeline[0].Title) + assert.Equal(t, "Lunch", timeline[1].Title) +} + +func TestTimelineItemsSortedByTime(t *testing.T) { + // Items from different sources + // Assert sorted by timestamp regardless of source +} +``` + +### E2E Test (Red) +Timeline renders with correct order and time markers. + +## Proposed Approach + +1. **Data Structure:** + ```go + type TimelineItem struct { + Time time.Time + Title string + Source string // "task", "meal", "calendar" + Type string // source-specific type + URL string + Metadata map[string]any + } + ``` + +2. **Handler:** + - New endpoint `GET /timeline` or extend tab handler + - Aggregate items from tasks, meals, (future) calendar + - Sort by time ascending + - Group by hour or time block + +3. **UI:** + - Vertical timeline with time gutter on left + - Color-coded by source + - Expandable items for details + +4. **Meal time mapping:** + - breakfast: 08:00 + - lunch: 12:00 + - dinner: 18:00 + - snack: configurable or omit from timeline + +## Affected Components +- `internal/handlers/handlers.go` or `internal/handlers/timeline.go` (new) +- `internal/handlers/handlers_test.go` +- `internal/models/types.go` (TimelineItem struct) +- `web/templates/partials/timeline-tab.html` (new) +- `web/templates/index.html` (add tab) + +## Definition of Done +- [ ] Timeline view accessible from UI +- [ ] Tasks with times appear at correct position +- [ ] Meals appear at appropriate meal times +- [ ] Items sorted chronologically +- [ ] Visual time markers/gutter +- [ ] Color coding by source +- [ ] Unit tests for timeline building +- [ ] E2E test for rendering diff --git a/issues/013-quick-add-shopping-list.md b/issues/013-quick-add-shopping-list.md new file mode 100644 index 0000000..e09ab92 --- /dev/null +++ b/issues/013-quick-add-shopping-list.md @@ -0,0 +1,81 @@ +# [FEATURE] Quick add for shopping list items + +## Description +Add a quick add interface for shopping list items. + +## User Story +As a user, I want to quickly add items to my shopping list without navigating away. + +## Technical Context +- Requires decision on backend: PlanToEat API vs local SQLite +- May need new table `shopping_items` if local +- Could integrate with modal menu from issue #002 + +## Test Strategy + +### Unit Test (Red) +**File:** `internal/store/sqlite_test.go` + +```go +func TestAddShoppingItem(t *testing.T) { + store := setupTestStore(t) + + err := store.AddShoppingItem(ShoppingItem{ + Name: "Milk", + Quantity: "1 gallon", + }) + + assert.NoError(t, err) + + items, _ := store.GetShoppingItems() + assert.Len(t, items, 1) + assert.Equal(t, "Milk", items[0].Name) +} +``` + +### E2E Test (Red) +Quick-add form submits, item appears in list. + +## Proposed Approach + +1. **Decide backend:** + - Option A: PlanToEat API (if it supports shopping lists) + - Option B: Local SQLite table (simpler, offline-capable) + +2. **Database (if local):** + ```sql + CREATE TABLE shopping_items ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + quantity TEXT, + checked BOOLEAN DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + ``` + +3. **Handler:** + - `POST /api/shopping/add` — add item + - `GET /api/shopping` — list items + - `PUT /api/shopping/{id}/check` — toggle checked + - `DELETE /api/shopping/{id}` — remove item + +4. **UI:** + - Quick-add form in modal menu (integrate with #002) or meals tab + - Simple input: item name, optional quantity + - List view with checkboxes + +## Affected Components +- `internal/store/sqlite.go` (new table/methods) +- `internal/store/sqlite_test.go` +- `internal/handlers/handlers.go` (new endpoints) +- `web/templates/partials/shopping-list.html` (new) +- Potentially `web/templates/partials/modal-menu.html` (from #002) + +## Definition of Done +- [ ] Can add items via quick-add form +- [ ] Items persisted (API or local) +- [ ] Items displayed in list view +- [ ] Can check/uncheck items +- [ ] Can delete items +- [ ] Unit tests for store operations +- [ ] E2E test for add flow diff --git a/issues/015-random-landscape-background.md b/issues/015-random-landscape-background.md new file mode 100644 index 0000000..4f65c98 --- /dev/null +++ b/issues/015-random-landscape-background.md @@ -0,0 +1,77 @@ +# [FEATURE] Random landscape background image + +## Description +Use a random open licensed landscape image as the background, and replace it with a new one each refresh. + +## User Story +As a user, I want a fresh landscape background on each visit for visual variety. + +## Technical Context +- Affects page container/body styling +- Needs external image source with open license +- Consider performance and loading states + +## Test Strategy + +### Integration Test (Red) +Test image endpoint returns valid image URL with appropriate license. + +```go +func TestGetRandomBackgroundURL(t *testing.T) { + url := GetRandomBackgroundURL() + + assert.NotEmpty(t, url) + assert.Contains(t, url, "unsplash") // or other source + + // Verify URL is accessible + resp, err := http.Head(url) + assert.NoError(t, err) + assert.Equal(t, 200, resp.StatusCode) +} +``` + +## Proposed Approach + +1. **Image Source Options:** + - **Unsplash Source API:** `https://source.unsplash.com/1920x1080/?landscape,nature` + - Free, no API key needed for basic use + - Redirects to random image + - **Pexels API:** Requires API key + - **Local curated set:** Bundle images, no external dependency + +2. **Implementation:** + - Set CSS `background-image` on body or main container + - Add loading state (solid color) while image loads + - Use `background-size: cover` and `background-position: center` + +3. **Template change:** + ```html + <body style="background-image: url('{{.BackgroundURL}}'); background-size: cover;"> + ``` + + Or with CSS class and CSS variable. + +4. **Handler:** + - Generate/fetch random URL on each page load + - Pass to template context + +## Traps to Avoid +- **Slow loads:** Large images can delay page render + - Use appropriate resolution (1920x1080 max) + - Consider lazy loading or progressive enhancement +- **Image fails to load:** Have solid color fallback +- **Caching:** Browser may cache image; ensure URL changes per request +- **Dark text visibility:** May need overlay or text shadow for readability + +## Affected Components +- `internal/handlers/handlers.go` (add background URL to context) +- `web/templates/index.html` (apply background style) +- `web/static/css/` (fallback and loading styles) + +## Definition of Done +- [ ] Background image displays on page load +- [ ] Different image on each refresh +- [ ] Images are openly licensed +- [ ] Fallback color while loading +- [ ] Text remains readable over images +- [ ] Integration test verifies URL validity diff --git a/issues/016-click-task-edit-details.md b/issues/016-click-task-edit-details.md new file mode 100644 index 0000000..a1687c9 --- /dev/null +++ b/issues/016-click-task-edit-details.md @@ -0,0 +1,80 @@ +# [FEATURE] Click task to open editable details + +## Description +When I click a task, open details and let me update description. + +## User Story +As a user, I want to click a task to view and edit its description inline. + +## Technical Context +- Extends existing task display with detail view +- Requires new endpoints for fetching and updating task details +- HTMX-powered modal or slide-out panel + +## Test Strategy + +### Unit Test (Red) +**File:** `internal/handlers/handlers_test.go` + +```go +func TestUpdateTaskDescription(t *testing.T) { + // Setup mock Todoist client + mockClient := &MockTodoistClient{} + handler := NewHandler(mockClient) + + // Call update endpoint + req := httptest.NewRequest("PUT", "/tasks/123", + strings.NewReader(`{"description": "Updated desc"}`)) + + // Assert Todoist API called with correct params + mockClient.AssertCalled(t, "UpdateTask", "123", "Updated desc") +} +``` + +### E2E Test (Red) +``` +1. Click on a task row +2. Assert modal/drawer opens with task details +3. Assert description field is editable +4. Edit description and save +5. Assert modal closes +6. Assert description persisted (re-open to verify) +``` + +## Proposed Approach + +1. **Endpoints:** + - `GET /tasks/{id}/detail` — fetch task with full details + - `PUT /tasks/{id}` — update task (description, etc.) + +2. **UI Flow:** + - Click task row triggers `hx-get="/tasks/{id}/detail"` + - Response loads into modal or slide-out panel + - Form with editable description (textarea) + - Save button triggers `hx-put="/tasks/{id}"` + - On success, close modal and optionally update task row + +3. **Template:** + - New partial: `partials/task-detail.html` + - Contains: title (read-only or editable), description (editable), due date, labels, save/cancel buttons + +4. **API Integration:** + - Update Todoist via API when saving + - Update local cache + +## Affected Components +- `internal/handlers/handlers.go` (new endpoints) +- `internal/handlers/handlers_test.go` +- `internal/api/todoist.go` (UpdateTask method if not exists) +- `web/templates/partials/task-detail.html` (new) +- `web/templates/partials/todoist-tasks.html` (add click handler) +- `web/templates/index.html` (modal container if needed) + +## Definition of Done +- [ ] Clicking task opens detail view +- [ ] Description field is editable +- [ ] Save persists to Todoist +- [ ] Local cache updated +- [ ] Modal/drawer closes on save +- [ ] Unit tests for update handler +- [ ] E2E test for full flow |
