diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-01-13 13:36:09 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-01-13 13:36:09 -1000 |
| commit | a1fa857a2f5ab163ffe5abbdeeb0eba8fc9508e9 (patch) | |
| tree | 11a25b75fb10e4b5262ca397b104c125432c7aaa /internal | |
| parent | 2fb1ed729fbd61d70b38a11903fb35eabb2bdca1 (diff) | |
Implement Phase 2 Steps 3-5: Sorting and Search improvements
Step 3 - Trello Smart Sorting:
- Update GetBoards SQL with LEFT JOIN and MAX(c.id) for activity sorting
- Update GetBoardsWithCards to find max card ID per board
- Sort by: 1) Has cards, 2) Newest card activity, 3) Board name
- Trello IDs are chronologically sortable (newer > older)
Step 4 - Todoist Due-First Sorting:
- Update GetTasks ORDER BY with CASE WHEN due_date IS NULL
- Sort by: 1) Incomplete, 2) Has due date, 3) Earliest date, 4) Priority
- Tasks with due dates appear before tasks without due dates
Step 5 - Obsidian Search:
- Add SearchNotes method with LIKE queries on title/content
- Update HandleNotes to check 'q' query param and HX-Target header
- Implement smart partial rendering (obsidian-notes vs notes-tab)
- Add search input with 300ms debounce and HTMX integration
- Real-time search without page reload
Mark Steps 1-5 as complete in PHASE_2_SURGICAL_PLAN.md
All tests passing
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/handlers/tabs.go | 25 | ||||
| -rw-r--r-- | internal/store/sqlite.go | 40 |
2 files changed, 61 insertions, 4 deletions
diff --git a/internal/handlers/tabs.go b/internal/handlers/tabs.go index fa60a10..448dfbe 100644 --- a/internal/handlers/tabs.go +++ b/internal/handlers/tabs.go @@ -132,8 +132,19 @@ func (h *TabsHandler) HandlePlanning(w http.ResponseWriter, r *http.Request) { // HandleNotes renders the Notes tab (Obsidian notes) func (h *TabsHandler) HandleNotes(w http.ResponseWriter, r *http.Request) { - // Fetch Obsidian notes - notes, err := h.store.GetNotes(20) + // Check for search query parameter + query := r.URL.Query().Get("q") + + var notes []models.Note + var err error + + // If search query is present, search notes; otherwise, get all notes + if query != "" { + notes, err = h.store.SearchNotes(query) + } else { + notes, err = h.store.GetNotes(20) + } + if err != nil { http.Error(w, "Failed to fetch notes", http.StatusInternalServerError) log.Printf("Error fetching notes: %v", err) @@ -146,7 +157,15 @@ func (h *TabsHandler) HandleNotes(w http.ResponseWriter, r *http.Request) { Notes: notes, } - if err := h.templates.ExecuteTemplate(w, "notes-tab", data); err != nil { + // Check HX-Target header for partial update + hxTarget := r.Header.Get("HX-Target") + templateName := "notes-tab" + if hxTarget == "notes-results" { + // Render only the notes list for HTMX partial update + templateName = "obsidian-notes" + } + + if err := h.templates.ExecuteTemplate(w, templateName, data); err != nil { http.Error(w, "Failed to render template", http.StatusInternalServerError) log.Printf("Error rendering notes tab: %v", err) } diff --git a/internal/store/sqlite.go b/internal/store/sqlite.go index fa8f2b1..c97d0af 100644 --- a/internal/store/sqlite.go +++ b/internal/store/sqlite.go @@ -126,7 +126,7 @@ func (s *Store) GetTasks() ([]models.Task, error) { rows, err := s.db.Query(` SELECT id, content, description, project_id, project_name, due_date, priority, completed, labels, url, created_at FROM tasks - ORDER BY completed ASC, due_date ASC, priority DESC + ORDER BY completed ASC, CASE WHEN due_date IS NULL THEN 1 ELSE 0 END, due_date ASC, priority DESC `) if err != nil { return nil, err @@ -248,6 +248,44 @@ func (s *Store) GetNotes(limit int) ([]models.Note, error) { return notes, rows.Err() } +// SearchNotes searches notes by title or content +func (s *Store) SearchNotes(query string) ([]models.Note, error) { + searchPattern := "%" + query + "%" + rows, err := s.db.Query(` + SELECT filename, title, content, modified, path, tags + FROM notes + WHERE title LIKE ? OR content LIKE ? + ORDER BY modified DESC + `, searchPattern, searchPattern) + if err != nil { + return nil, err + } + defer rows.Close() + + var notes []models.Note + for rows.Next() { + var note models.Note + var tagsJSON string + + err := rows.Scan( + ¬e.Filename, + ¬e.Title, + ¬e.Content, + ¬e.Modified, + ¬e.Path, + &tagsJSON, + ) + if err != nil { + return nil, err + } + + json.Unmarshal([]byte(tagsJSON), ¬e.Tags) + notes = append(notes, note) + } + + return notes, rows.Err() +} + // Meals operations // SaveMeals saves multiple meals to the database |
