summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-08-24 06:20:52 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-08-24 06:20:52 +0000
commit5c659d9046add8c77bd7699961a910505dcb4bc2 (patch)
tree8bde9a62d98d2a21f030d4c6cbd3ceecb9a7adf8
parentc46c135f52090a082660cb06131282796f5cd592 (diff)
Remove confirmed-orphaned routes, handlers, and templatesHEADmaster
From the sitemap audit: /tabs/meals (same dead-tab pattern as the removed /tabs/conditions, only reachable via ?tab=meals with no nav button), /partials/lists (superseded by inline .Lists rendering in trello-board.html), /shopping/toggle and /shopping/mode/{store}/toggle (superseded by the one-way complete/filter model), plus the orphaned trello-boards.html and error-banner.html templates that nothing rendered or included. Rewrote the meals grouping test to exercise groupMeals() directly since that logic is still live via the timeline. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017GMEkHeqKz6FLkmizowBTK
-rw-r--r--.agent/design.md11
-rw-r--r--cmd/dashboard/main.go4
-rw-r--r--internal/handlers/handlers.go31
-rw-r--r--internal/handlers/handlers_test.go271
-rw-r--r--internal/handlers/shopping.go83
-rw-r--r--internal/handlers/tab_state_test.go8
-rw-r--r--web/static/js/app.js2
-rw-r--r--web/templates/partials/error-banner.html12
-rw-r--r--web/templates/partials/lists-options.html4
-rw-r--r--web/templates/partials/meals-tab.html10
-rw-r--r--web/templates/partials/plantoeat-meals.html35
-rw-r--r--web/templates/partials/trello-boards.html45
12 files changed, 13 insertions, 503 deletions
diff --git a/.agent/design.md b/.agent/design.md
index 47ff403..829f86f 100644
--- a/.agent/design.md
+++ b/.agent/design.md
@@ -351,15 +351,6 @@ Aggregated shopping lists from Trello + PlanToEat + user items.
- Large touch targets
- Fixed bottom quick-add
-### Meals View (`/tabs/meals`)
-
-PlanToEat meal schedule for the next 7 days.
-
-**Features:**
-- Meals grouped by date + meal type (breakfast/lunch/dinner)
-- Multiple recipes for same slot combined with " + " separator
-- Sorted by date, then meal type order
-
### Conditions Page (`/conditions`)
**Public route** - no authentication required.
@@ -828,7 +819,6 @@ npx tailwindcss -i web/static/css/input.css -o web/static/css/output.css --watch
| GET | `/tabs/timeline` | Timeline partial |
| GET | `/tabs/tasks` | Tasks partial |
| GET | `/tabs/planning` | Planning partial |
-| GET | `/tabs/meals` | Meals partial |
| GET | `/tabs/shopping` | Shopping partial |
| GET | `/conditions` | Live feeds page (public) |
| POST | `/complete-atom` | Complete task/card/bug |
@@ -838,7 +828,6 @@ npx tailwindcss -i web/static/css/input.css -o web/static/css/output.css --watch
| POST | `/bugs` | Submit bug report |
| POST | `/shopping/add` | Add shopping item |
| GET | `/shopping/mode/{store}` | Shopping mode view |
-| POST | `/shopping/mode/{store}/toggle` | Toggle shopping item |
| POST | `/shopping/mode/{store}/complete` | Complete shopping item |
| GET | `/settings` | Settings page |
| POST | `/settings/features` | Create feature toggle |
diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go
index c88dc45..c9e1078 100644
--- a/cmd/dashboard/main.go
+++ b/cmd/dashboard/main.go
@@ -334,7 +334,6 @@ func main() {
// Tab routes for HTMX
r.Get("/tabs/tasks", h.HandleTabTasks)
r.Get("/tabs/planning", h.HandleTabPlanning)
- r.Get("/tabs/meals", h.HandleTabMeals)
r.Get("/tabs/timeline", h.HandleTimeline)
r.Get("/tabs/shopping", h.HandleTabShopping)
@@ -355,7 +354,6 @@ func main() {
// Unified Quick Add (for Tasks tab)
r.Post("/unified-add", h.HandleUnifiedAdd)
- r.Get("/partials/lists", h.HandleGetListsOptions)
r.Get("/partials/shopping-lists", h.HandleGetShoppingLists)
// Task detail/edit
@@ -367,11 +365,9 @@ func main() {
// Shopping quick-add
r.Post("/shopping/add", h.HandleShoppingQuickAdd)
- r.Post("/shopping/toggle", h.HandleShoppingToggle)
// Shopping mode (focused single-store view)
r.Get("/shopping/mode/{store}", h.HandleShoppingMode)
- r.Post("/shopping/mode/{store}/toggle", h.HandleShoppingModeToggle)
r.Post("/shopping/mode/{store}/complete", h.HandleShoppingModeComplete)
// Passkey management (WebAuthn)
diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go
index fc75b61..69e1c65 100644
--- a/internal/handlers/handlers.go
+++ b/internal/handlers/handlers.go
@@ -787,23 +787,6 @@ func (h *Handler) HandleUnifiedAdd(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
-// HandleGetListsOptions returns HTML options for lists in a given board
-func (h *Handler) HandleGetListsOptions(w http.ResponseWriter, r *http.Request) {
- boardID := r.URL.Query().Get("board_id")
- if boardID == "" {
- JSONError(w, http.StatusBadRequest, "board_id is required", nil)
- return
- }
-
- lists, err := h.trelloClient.GetLists(r.Context(), boardID)
- if err != nil {
- JSONError(w, http.StatusInternalServerError, "Failed to fetch lists", err)
- return
- }
-
- HTMLResponse(w, h.renderer, "lists-options", lists)
-}
-
// taskDetailData is what the "task-detail" template renders (the modal
// opened from any tab's task card). Recurrence fields are doot-native only.
type taskDetailData struct {
@@ -1234,20 +1217,6 @@ type CombinedMeal struct {
Meals []models.Meal // original meal records
}
-// HandleTabMeals renders the Meals tab (PlanToEat)
-func (h *Handler) HandleTabMeals(w http.ResponseWriter, r *http.Request) {
- startDate := config.Today()
- endDate := startDate.AddDate(0, 0, 7)
-
- meals, err := h.store.GetMeals(startDate, endDate)
- if err != nil {
- JSONError(w, http.StatusInternalServerError, "Failed to fetch meals", err)
- return
- }
-
- HTMLResponse(w, h.renderer, "meals-tab", struct{ Meals []CombinedMeal }{groupMeals(meals)})
-}
-
// mealTypeOrder returns sort order for meal types
func mealTypeOrder(mealType string) int {
switch mealType {
diff --git a/internal/handlers/handlers_test.go b/internal/handlers/handlers_test.go
index ca24778..61fb75d 100644
--- a/internal/handlers/handlers_test.go
+++ b/internal/handlers/handlers_test.go
@@ -819,169 +819,6 @@ func TestShoppingComplete_ExternalItemMarkedChecked(t *testing.T) {
}
}
-// TestShoppingToggle_UpdatesItemState verifies toggle correctly updates state
-func TestShoppingToggle_UpdatesItemState(t *testing.T) {
- db, cleanup := setupTestDB(t)
- defer cleanup()
-
- h := &Handler{
- store: db,
- renderer: newTestRenderer(),
- config: &config.Config{},
- }
-
- // Add user item
- _ = db.SaveUserShoppingItem("Toggle Item", "TestStore")
- items, _ := db.GetUserShoppingItems()
- itemID := items[0].ID
-
- // Toggle to checked
- req := httptest.NewRequest("POST", "/shopping/toggle", nil)
- req.Form = map[string][]string{
- "id": {fmt.Sprintf("user-%d", itemID)},
- "source": {"user"},
- "checked": {"true"},
- }
- w := httptest.NewRecorder()
- h.HandleShoppingToggle(w, req)
-
- if w.Code != http.StatusOK {
- t.Errorf("Expected status 200, got %d", w.Code)
- }
-
- // Verify item is checked
- items, _ = db.GetUserShoppingItems()
- if !items[0].Checked {
- t.Error("Expected item to be checked after toggle")
- }
-
- // Toggle back to unchecked
- req = httptest.NewRequest("POST", "/shopping/toggle", nil)
- req.Form = map[string][]string{
- "id": {fmt.Sprintf("user-%d", itemID)},
- "source": {"user"},
- "checked": {"false"},
- }
- w = httptest.NewRecorder()
- h.HandleShoppingToggle(w, req)
-
- items, _ = db.GetUserShoppingItems()
- if items[0].Checked {
- t.Error("Expected item to be unchecked after second toggle")
- }
-}
-
-// TestShoppingToggle_ExternalSources verifies toggle works for external sources
-func TestShoppingToggle_ExternalSources(t *testing.T) {
- db, cleanup := setupTestDB(t)
- defer cleanup()
-
- h := &Handler{
- store: db,
- renderer: newTestRenderer(),
- config: &config.Config{},
- }
-
- tests := []struct {
- source string
- itemID string
- }{
- {"trello", "trello-toggle-123"},
- {"plantoeat", "pte-toggle-456"},
- }
-
- for _, tc := range tests {
- t.Run(tc.source, func(t *testing.T) {
- // Toggle to checked
- req := httptest.NewRequest("POST", "/shopping/toggle", nil)
- req.Form = map[string][]string{
- "id": {tc.itemID},
- "source": {tc.source},
- "checked": {"true"},
- }
- w := httptest.NewRecorder()
- h.HandleShoppingToggle(w, req)
-
- if w.Code != http.StatusOK {
- t.Errorf("Expected status 200, got %d", w.Code)
- }
-
- checks, _ := db.GetShoppingItemChecks(tc.source)
- if !checks[tc.itemID] {
- t.Errorf("Expected %s item to be checked", tc.source)
- }
- })
- }
-}
-
-// TestShoppingToggle_UnknownSource verifies error for unknown source
-func TestShoppingToggle_UnknownSource(t *testing.T) {
- db, cleanup := setupTestDB(t)
- defer cleanup()
-
- h := &Handler{
- store: db,
- config: &config.Config{},
- }
-
- req := httptest.NewRequest("POST", "/shopping/toggle", nil)
- req.Form = map[string][]string{
- "id": {"unknown-123"},
- "source": {"unknown"},
- "checked": {"true"},
- }
- w := httptest.NewRecorder()
- h.HandleShoppingToggle(w, req)
-
- if w.Code != http.StatusBadRequest {
- t.Errorf("Expected status 400 for unknown source, got %d", w.Code)
- }
-}
-
-// TestShoppingModeToggle_ReturnsUpdatedList verifies shopping mode toggle returns items
-func TestShoppingModeToggle_ReturnsUpdatedList(t *testing.T) {
- db, cleanup := setupTestDB(t)
- defer cleanup()
-
- h := &Handler{
- store: db,
- renderer: newTestRenderer(),
- config: &config.Config{},
- }
-
- // Add items
- _ = db.SaveUserShoppingItem("Item 1", "TestStore")
- _ = db.SaveUserShoppingItem("Item 2", "TestStore")
-
- items, _ := db.GetUserShoppingItems()
- itemID := items[0].ID
-
- // Toggle in shopping mode
- req := httptest.NewRequest("POST", "/shopping/mode/TestStore/toggle", nil)
- req.Form = map[string][]string{
- "id": {fmt.Sprintf("user-%d", itemID)},
- "source": {"user"},
- "checked": {"true"},
- }
-
- rctx := chi.NewRouteContext()
- rctx.URLParams.Add("store", "TestStore")
- req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx))
-
- w := httptest.NewRecorder()
- h.HandleShoppingModeToggle(w, req)
-
- if w.Code != http.StatusOK {
- t.Errorf("Expected status 200, got %d", w.Code)
- }
-
- // Response should be HTML (items list)
- contentType := w.Header().Get("Content-Type")
- if !strings.Contains(contentType, "text/html") {
- t.Errorf("Expected HTML response, got %s", contentType)
- }
-}
-
// TestShoppingTabFiltersCheckedItems verifies checked items excluded from tab
func TestShoppingTabFiltersCheckedItems(t *testing.T) {
db, cleanup := setupTestDB(t)
@@ -1057,26 +894,6 @@ func TestHandleTabTasks(t *testing.T) {
}
}
-func TestHandleTabMeals(t *testing.T) {
- db, cleanup := setupTestDB(t)
- defer cleanup()
-
- h := &Handler{
- store: db,
- renderer: newTestRenderer(),
- config: &config.Config{CacheTTLMinutes: 5},
- }
-
- req := httptest.NewRequest("GET", "/tabs/meals", nil)
- w := httptest.NewRecorder()
-
- h.HandleTabMeals(w, req)
-
- if w.Code != http.StatusOK {
- t.Errorf("Expected status 200, got %d", w.Code)
- }
-}
-
func TestHandleTabPlanning(t *testing.T) {
db, cleanup := setupTestDB(t)
defer cleanup()
@@ -2120,35 +1937,6 @@ func TestHandleSetTaskRecurrence_InvalidFreq_Returns400(t *testing.T) {
}
}
-// =============================================================================
-// HandleGetListsOptions template tests
-// =============================================================================
-
-// TestHandleGetListsOptions_RendersTemplate verifies that HandleGetListsOptions uses
-// the renderer with the "lists-options" template.
-func TestHandleGetListsOptions_RendersTemplate(t *testing.T) {
- h, cleanup := setupTestHandler(t)
- defer cleanup()
-
- h.trelloClient = &mockTrelloClient{boards: []models.Board{}}
-
- req := httptest.NewRequest("GET", "/trello/lists?board_id=board1", nil)
- w := httptest.NewRecorder()
- h.HandleGetListsOptions(w, req)
-
- mr := h.renderer.(*MockRenderer)
- var found bool
- for _, call := range mr.Calls {
- if call.Name == "lists-options" {
- found = true
- break
- }
- }
- if !found {
- t.Error("Expected renderer to be called with 'lists-options' template")
- }
-}
-
// TestHandleSyncSources_AddsLogEntry verifies that syncing sources creates a sync log entry.
func TestHandleSyncSources_AddsLogEntry(t *testing.T) {
h, cleanup := setupTestHandler(t)
@@ -2339,67 +2127,32 @@ func TestHandleTabPlanning_TomorrowBoundary(t *testing.T) {
}
// =============================================================================
-// HandleTabMeals grouping test
+// groupMeals grouping test
// =============================================================================
-// TestHandleTabMeals_GroupingMergesRecipes verifies that multiple meals sharing
-// the same date+mealType are combined into a single CombinedMeal entry whose
-// RecipeNames contains all merged recipe names.
-func TestHandleTabMeals_GroupingMergesRecipes(t *testing.T) {
- db, cleanup := setupTestDB(t)
- defer cleanup()
-
+// TestGroupMeals_MergesRecipes verifies that multiple meals sharing the same
+// date+mealType are combined into a single CombinedMeal entry whose
+// RecipeNames contains all merged recipe names. groupMeals is still live
+// logic used by the timeline (see timeline_logic.go).
+func TestGroupMeals_MergesRecipes(t *testing.T) {
today := config.Today()
meals := []models.Meal{
{ID: "m1", RecipeName: "Pasta", Date: today, MealType: "dinner", RecipeURL: "http://example.com/pasta"},
{ID: "m2", RecipeName: "Salad", Date: today, MealType: "dinner", RecipeURL: "http://example.com/salad"},
{ID: "m3", RecipeName: "Oatmeal", Date: today, MealType: "breakfast", RecipeURL: "http://example.com/oatmeal"},
}
- if err := db.SaveMeals(meals); err != nil {
- t.Fatalf("Failed to save meals: %v", err)
- }
- renderer := NewMockRenderer()
- h := &Handler{
- store: db,
- renderer: renderer,
- config: &config.Config{CacheTTLMinutes: 5},
- }
-
- req := httptest.NewRequest("GET", "/tabs/meals", nil)
- w := httptest.NewRecorder()
- h.HandleTabMeals(w, req)
-
- if w.Code != http.StatusOK {
- t.Errorf("Expected 200, got %d", w.Code)
- }
-
- var mealsCall *RenderCall
- for i, call := range renderer.Calls {
- if call.Name == "meals-tab" {
- c := renderer.Calls[i]
- mealsCall = &c
- break
- }
- }
- if mealsCall == nil {
- t.Fatal("Expected meals-tab to be rendered")
- }
-
- data, ok := mealsCall.Data.(struct{ Meals []CombinedMeal })
- if !ok {
- t.Fatalf("Expected meals data struct, got %T", mealsCall.Data)
- }
+ combined := groupMeals(meals)
// m1 + m2 share date+dinner → 1 CombinedMeal; m3 is breakfast → 1 CombinedMeal
- if len(data.Meals) != 2 {
- t.Errorf("Expected 2 combined meals, got %d", len(data.Meals))
+ if len(combined) != 2 {
+ t.Errorf("Expected 2 combined meals, got %d", len(combined))
}
var dinner *CombinedMeal
- for i := range data.Meals {
- if data.Meals[i].MealType == "dinner" {
- dinner = &data.Meals[i]
+ for i := range combined {
+ if combined[i].MealType == "dinner" {
+ dinner = &combined[i]
break
}
}
diff --git a/internal/handlers/shopping.go b/internal/handlers/shopping.go
index e8e80da..1c58126 100644
--- a/internal/handlers/shopping.go
+++ b/internal/handlers/shopping.go
@@ -73,47 +73,6 @@ func (h *Handler) HandleShoppingQuickAdd(w http.ResponseWriter, r *http.Request)
}{allStores, grouped})
}
-// HandleShoppingToggle toggles a shopping item's checked state
-func (h *Handler) HandleShoppingToggle(w http.ResponseWriter, r *http.Request) {
- if err := r.ParseForm(); err != nil {
- JSONError(w, http.StatusBadRequest, "Failed to parse form", err)
- return
- }
-
- id := r.FormValue("id")
- source := r.FormValue("source")
- checked := r.FormValue("checked") == "true"
-
- switch source {
- case "user":
- var userID int64
- if _, err := fmt.Sscanf(id, "user-%d", &userID); err != nil {
- JSONError(w, http.StatusBadRequest, "Invalid user item ID", err)
- return
- }
- if err := h.store.ToggleUserShoppingItem(userID, checked); err != nil {
- JSONError(w, http.StatusInternalServerError, "Failed to toggle item", err)
- return
- }
- case "trello", "plantoeat":
- // Store checked state locally for external sources
- if err := h.store.SetShoppingItemChecked(source, id, checked); err != nil {
- JSONError(w, http.StatusInternalServerError, "Failed to toggle item", err)
- return
- }
- default:
- JSONError(w, http.StatusBadRequest, "Unknown source", nil)
- return
- }
-
- // Return refreshed shopping tab
- stores := h.aggregateShoppingLists(r.Context())
- HTMLResponse(w, h.renderer, "shopping-tab", struct {
- Stores []models.ShoppingStore
- Grouped bool
- }{stores, true})
-}
-
// HandleShoppingMode renders the focused shopping mode for a single store
func (h *Handler) HandleShoppingMode(w http.ResponseWriter, r *http.Request) {
storeName := chi.URLParam(r, "store")
@@ -143,48 +102,6 @@ func (h *Handler) HandleShoppingMode(w http.ResponseWriter, r *http.Request) {
HTMLResponse(w, h.renderer, "shopping-mode.html", data)
}
-// HandleShoppingModeToggle toggles an item in shopping mode and returns updated list
-func (h *Handler) HandleShoppingModeToggle(w http.ResponseWriter, r *http.Request) {
- storeName := chi.URLParam(r, "store")
- if err := r.ParseForm(); err != nil {
- JSONError(w, http.StatusBadRequest, "Failed to parse form", err)
- return
- }
-
- id := r.FormValue("id")
- source := r.FormValue("source")
- checked := r.FormValue("checked") == "true"
-
- // Toggle the item
- switch source {
- case "user":
- var userID int64
- if _, err := fmt.Sscanf(id, "user-%d", &userID); err != nil {
- JSONError(w, http.StatusBadRequest, "Invalid user item ID", err)
- return
- }
- if err := h.store.ToggleUserShoppingItem(userID, checked); err != nil {
- JSONError(w, http.StatusInternalServerError, "Failed to toggle item", err)
- return
- }
- case "trello", "plantoeat":
- if err := h.store.SetShoppingItemChecked(source, id, checked); err != nil {
- JSONError(w, http.StatusInternalServerError, "Failed to toggle item", err)
- return
- }
- }
-
- // URL decode the store name
- storeName, _ = url.QueryUnescape(storeName)
-
- // Return updated item list partial
- allStores := h.aggregateShoppingLists(r.Context())
- HTMLResponse(w, h.renderer, "shopping-mode-items", struct {
- StoreName string
- Items []models.UnifiedShoppingItem
- }{storeName, models.FlattenItemsForStore(allStores, storeName)})
-}
-
// HandleShoppingModeComplete removes an item from the shopping list
func (h *Handler) HandleShoppingModeComplete(w http.ResponseWriter, r *http.Request) {
storeName := chi.URLParam(r, "store")
diff --git a/internal/handlers/tab_state_test.go b/internal/handlers/tab_state_test.go
index 9b34033..bf833af 100644
--- a/internal/handlers/tab_state_test.go
+++ b/internal/handlers/tab_state_test.go
@@ -24,7 +24,6 @@ func TestHandleDashboard_TabState(t *testing.T) {
<body>
<button class="tab-button tab-button-active" hx-get="/tabs/tasks">Tasks</button>
<button class="tab-button tab-button-active" hx-get="/tabs/planning">Planning</button>
-<button class="tab-button tab-button-active" hx-get="/tabs/meals">Meals</button>
</body>
</html>`
_, err := w.Write([]byte(html))
@@ -63,13 +62,6 @@ func TestHandleDashboard_TabState(t *testing.T) {
expectedActive: `class="tab-button tab-button-active"`,
expectedHxGet: `hx-get="/tabs/planning"`,
},
- {
- name: "meals tab from query param",
- url: "/?tab=meals",
- expectedTab: "meals",
- expectedActive: `class="tab-button tab-button-active"`,
- expectedHxGet: `hx-get="/tabs/meals"`,
- },
}
for _, tt := range tests {
diff --git a/web/static/js/app.js b/web/static/js/app.js
index 37d2a30..8058dc2 100644
--- a/web/static/js/app.js
+++ b/web/static/js/app.js
@@ -320,7 +320,7 @@ function toggleTask(taskId) {
const SWIPE_THRESHOLD = 50; // Minimum px for a swipe
// Get ordered list of tab names (main tabs only for swipe)
- const TAB_ORDER = ['timeline', 'shopping', 'conditions', 'tasks', 'planning', 'meals'];
+ const TAB_ORDER = ['timeline', 'shopping', 'tasks', 'planning'];
function handleSwipe() {
const swipeDistance = touchEndX - touchStartX;
diff --git a/web/templates/partials/error-banner.html b/web/templates/partials/error-banner.html
deleted file mode 100644
index 628424f..0000000
--- a/web/templates/partials/error-banner.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{{define "error-banner"}}
-{{if .Errors}}
-<div class="bg-red-900/50 border border-red-500/30 text-red-300 px-4 py-3 rounded-lg mb-6">
- <p class="font-medium">Errors:</p>
- <ul class="list-disc list-inside">
- {{range .Errors}}
- <li>{{.}}</li>
- {{end}}
- </ul>
-</div>
-{{end}}
-{{end}}
diff --git a/web/templates/partials/lists-options.html b/web/templates/partials/lists-options.html
deleted file mode 100644
index f08fdcf..0000000
--- a/web/templates/partials/lists-options.html
+++ /dev/null
@@ -1,4 +0,0 @@
-{{define "lists-options"}}
-{{range .}}<option value="{{.ID}}">{{.Name}}</option>
-{{end}}
-{{end}}
diff --git a/web/templates/partials/meals-tab.html b/web/templates/partials/meals-tab.html
deleted file mode 100644
index 97c3a4e..0000000
--- a/web/templates/partials/meals-tab.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{{define "meals-tab"}}
-<div class="space-y-6"
- hx-get="/tabs/meals"
- hx-trigger="refresh-tasks from:body"
- hx-target="#tab-content"
- hx-swap="innerHTML">
- <!-- PlanToEat Meals Section -->
- {{template "plantoeat-meals" .}}
-</div>
-{{end}}
diff --git a/web/templates/partials/plantoeat-meals.html b/web/templates/partials/plantoeat-meals.html
deleted file mode 100644
index f05c98c..0000000
--- a/web/templates/partials/plantoeat-meals.html
+++ /dev/null
@@ -1,35 +0,0 @@
-{{define "plantoeat-meals"}}
-<section class="card text-shadow-sm">
- <!-- Section Header with Brand Color -->
- <div class="flex items-center gap-3 mb-6">
- <div class="w-1 h-8 bg-plantoeat rounded"></div>
- <h2 class="text-2xl font-light text-white tracking-wide">Upcoming Meals</h2>
- </div>
-
- {{if .Meals}}
- <div class="space-y-3">
- {{range .Meals}}
- <div class="border-l-4 border-plantoeat bg-black/30 pl-4 py-3 rounded-r-lg hover:bg-black/40 transition-colors">
- <p class="font-medium text-white">{{range $i, $name := .RecipeNames}}{{if $i}} + {{end}}{{$name}}{{end}}</p>
- <div class="flex justify-between items-center mt-2">
- <span class="text-sm text-white/60">{{.Date.Format "Mon, Jan 2"}}</span>
- <span class="badge bg-green-900/50 text-green-300 capitalize">
- {{.MealType}}
- </span>
- </div>
- </div>
- {{end}}
- </div>
- {{else}}
- <div class="text-center py-16">
- <svg class="mx-auto h-12 w-12 text-white/30" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 21v-4m0 0V5a2 2 0 012-2h6.5l1 1H21l-3 6 3 6h-8.5l-1-1H5a2 2 0 00-2 2zm9-13.5V9" />
- </svg>
- <h3 class="mt-4 text-lg font-medium text-white">No meals planned</h3>
- <p class="mt-2 text-sm text-white/50">
- Schedule your meals to see them here.
- </p>
- </div>
- {{end}}
-</section>
-{{end}}
diff --git a/web/templates/partials/trello-boards.html b/web/templates/partials/trello-boards.html
deleted file mode 100644
index d51446d..0000000
--- a/web/templates/partials/trello-boards.html
+++ /dev/null
@@ -1,45 +0,0 @@
-{{define "trello-boards"}}
-{{if .Boards}}
-<section class="card section-spacing text-shadow-sm">
- <!-- Section Header with Brand Color -->
- <div class="flex items-center gap-3 mb-6">
- <div class="w-1 h-8 bg-trello rounded"></div>
- <h2 class="text-2xl font-light text-white tracking-wide">Trello Boards</h2>
- </div>
-
- <!-- Active Boards Grid (boards with cards) -->
- <div class="card-grid mb-6">
- {{range .Boards}}
- {{if .Cards}}
- {{template "trello-board" .}}
- {{end}}
- {{end}}
- </div>
-
- <!-- Empty Boards Collapsible (boards without cards) -->
- <details class="mt-6 border-t border-white/10 pt-6">
- <summary class="cursor-pointer flex items-center justify-between group">
- <span class="text-sm font-medium text-white/50 group-hover:text-white/80 transition-colors">
- Empty Boards
- </span>
- <svg class="w-5 h-5 text-white/40 group-hover:text-white/60 transition-all"
- fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
- </svg>
- </summary>
- <div class="mt-4">
- <div class="card-grid">
- {{range .Boards}}
- {{if not .Cards}}
- <div class="border border-white/10 rounded-lg p-4 bg-black/30 opacity-70 hover:opacity-100 transition-opacity">
- <h3 class="font-medium text-white/70">{{.Name}}</h3>
- <p class="text-sm text-white/40 mt-2">No cards</p>
- </div>
- {{end}}
- {{end}}
- </div>
- </div>
- </details>
-</section>
-{{end}}
-{{end}}