summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-01-13 13:36:09 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-01-13 13:36:09 -1000
commita1fa857a2f5ab163ffe5abbdeeb0eba8fc9508e9 (patch)
tree11a25b75fb10e4b5262ca397b104c125432c7aaa /internal
parent2fb1ed729fbd61d70b38a11903fb35eabb2bdca1 (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.go25
-rw-r--r--internal/store/sqlite.go40
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(
+ &note.Filename,
+ &note.Title,
+ &note.Content,
+ &note.Modified,
+ &note.Path,
+ &tagsJSON,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ json.Unmarshal([]byte(tagsJSON), &note.Tags)
+ notes = append(notes, note)
+ }
+
+ return notes, rows.Err()
+}
+
// Meals operations
// SaveMeals saves multiple meals to the database