summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/dashboard/main.go7
-rw-r--r--internal/handlers/handlers.go10
-rw-r--r--internal/handlers/timeline.go7
-rw-r--r--web/templates/index.html2
-rw-r--r--web/templates/partials/timeline-tab.html79
5 files changed, 45 insertions, 60 deletions
diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go
index 9f69d46..7e58125 100644
--- a/cmd/dashboard/main.go
+++ b/cmd/dashboard/main.go
@@ -207,6 +207,13 @@ func main() {
// Rate limiter for agent auth (stricter - 10 requests/minute per IP)
agentAuthRateLimiter := appmiddleware.NewRateLimiter(10, time.Minute)
+ // Health check (no auth)
+ r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/plain")
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte("ok"))
+ })
+
// Public routes (no auth required)
r.Get("/login", authHandlers.HandleLoginPage)
r.With(authRateLimiter.Limit).Post("/login", authHandlers.HandleLogin)
diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go
index 5ecaf7c..eaef558 100644
--- a/internal/handlers/handlers.go
+++ b/internal/handlers/handlers.go
@@ -392,10 +392,14 @@ func (h *Handler) fetchCalendarEvents(ctx context.Context, forceRefresh bool) ([
}
}
- // If none enabled but we have a default from config, use that as a fallback
- // (or if we want to respect the toggle strictly, we should only use enabled ones)
if len(enabledIDs) == 0 {
- return nil, nil
+ // No source_configs synced yet — fall back to the configured calendar ID
+ if len(configs) == 0 && h.config.GoogleCalendarID != "" {
+ enabledIDs = []string{h.config.GoogleCalendarID}
+ } else {
+ // Configs exist but all disabled — respect that
+ return nil, nil
+ }
}
h.googleCalendarClient.SetCalendarIDs(enabledIDs)
diff --git a/internal/handlers/timeline.go b/internal/handlers/timeline.go
index bbdae51..2c3c6b6 100644
--- a/internal/handlers/timeline.go
+++ b/internal/handlers/timeline.go
@@ -69,6 +69,13 @@ func (h *Handler) HandleTimeline(w http.ResponseWriter, r *http.Request) {
log.Printf("Warning: failed to fetch calendar events: %v", err)
}
+ // Refresh meals cache before building timeline
+ if h.planToEatClient != nil {
+ if _, err := h.fetchMeals(r.Context(), false); err != nil {
+ log.Printf("Warning: failed to fetch meals: %v", err)
+ }
+ }
+
// Call BuildTimeline
items, err := BuildTimeline(r.Context(), h.store, start, end)
if err != nil {
diff --git a/web/templates/index.html b/web/templates/index.html
index febacf1..c34bacf 100644
--- a/web/templates/index.html
+++ b/web/templates/index.html
@@ -104,7 +104,7 @@
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
</a>
<a href="/scout/" class="tab-button" title="Scout">
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><rect x="2" y="6" width="7" height="12" rx="3.5" stroke-width="2"/><rect x="15" y="6" width="7" height="12" rx="3.5" stroke-width="2"/><path stroke-linecap="round" stroke-width="2" d="M9 12h6"/></svg>
</a>
</nav>
</div>
diff --git a/web/templates/partials/timeline-tab.html b/web/templates/partials/timeline-tab.html
index 8874440..e58f793 100644
--- a/web/templates/partials/timeline-tab.html
+++ b/web/templates/partials/timeline-tab.html
@@ -203,8 +203,7 @@
</details>
- <!-- Tomorrow Section (Calendar View) -->
- <!-- TODO: mirror widget's tomorrow layout — flat event+task list ordered by time, no collapsible, inline time labels -->
+ <!-- Tomorrow Section (flat chronological list, mirroring widget layout) -->
{{if .TomorrowItems}}
<details class="group" open>
<summary class="text-lg font-semibold mb-3 flex items-center gap-2 text-white/70 cursor-pointer hover:text-white/90 sticky top-0 bg-black/20 backdrop-blur-md py-2 z-10 rounded-lg px-2 list-none">
@@ -215,24 +214,37 @@
</svg>
</summary>
- <!-- Overdue + All-Day + Untimed Items Section -->
- <div class="mb-4 flex flex-wrap gap-1 p-2 bg-black/20 rounded-lg" id="tomorrow-untimed-section">
+ <!-- Flat chronological list: timed items first (sorted), then untimed -->
+ <div class="space-y-1 pl-1">
{{range .TomorrowItems}}
- {{if or .IsOverdue .IsAllDay}}
- <div class="untimed-item source-{{.Source}} {{if .IsOverdue}}overdue{{end}} {{if .IsCompleted}}opacity-50{{end}}">
+ <div class="flex items-center gap-2 py-2 px-2 rounded-lg hover:bg-white/5 {{if .IsCompleted}}opacity-50{{end}}">
+ <!-- Time label (fixed width) -->
+ <span class="text-xs text-white/40 w-14 shrink-0 text-right">
+ {{if and (not .IsAllDay) (or (ne .Time.Hour 0) (ne .Time.Minute 0))}}
+ {{.Time.Format "3:04 PM"}}
+ {{end}}
+ </span>
+ <!-- Source color bar -->
+ <div class="w-0.5 h-5 rounded-full shrink-0
+ {{if eq .Source "calendar"}}bg-purple-400{{else if eq .Source "doot"}}bg-teal-400{{else if eq .Source "todoist"}}bg-red-400{{else if eq .Source "trello"}}bg-blue-400{{else if eq .Source "plantoeat"}}bg-green-400{{else if eq .Source "gtasks"}}bg-yellow-400{{else}}bg-white/30{{end}}">
+ </div>
+ <!-- Checkbox for tasks -->
{{if or (eq .Type "task") (eq .Type "card") (eq .Type "gtask")}}
<input type="checkbox"
{{if .IsCompleted}}checked{{end}}
hx-post="{{if .IsCompleted}}/uncomplete-atom{{else}}/complete-atom{{end}}"
hx-vals='{"id": "{{.ID}}", "source": "{{.Source}}"{{if .ListID}}, "listId": "{{.ListID}}"{{end}}}'
- hx-target="closest .untimed-item"
+ hx-target="closest div.rounded-lg"
hx-swap="outerHTML"
- class="h-4 w-4 rounded bg-black/40 border-white/30 text-white/80 focus:ring-white/30 cursor-pointer flex-shrink-0">
+ class="h-4 w-4 rounded bg-black/40 border-white/30 text-white/80 focus:ring-white/30 cursor-pointer shrink-0">
{{end}}
- <span class="{{if .IsCompleted}}line-through text-white/50{{end}}">{{.Title}}</span>
- {{if .IsOverdue}}<span class="overdue-badge">overdue</span>{{end}}
+ <!-- Title -->
+ <span class="flex-1 text-sm text-white/80 {{if .IsCompleted}}line-through text-white/40{{end}} truncate">
+ {{.Title}}
+ {{if .EndTime}}<span class="text-white/30 text-xs ml-1">– {{.EndTime.Format "3:04 PM"}}</span>{{end}}
+ </span>
{{if .URL}}
- <a href="{{.URL}}" target="_blank" class="text-white/50 hover:text-white">
+ <a href="{{.URL}}" target="_blank" class="text-white/30 hover:text-white shrink-0">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
</svg>
@@ -240,52 +252,7 @@
{{end}}
</div>
{{end}}
- {{end}}
</div>
-
- <!-- Calendar Grid (dynamic hours) -->
- <div class="calendar-grid" id="tomorrow-calendar" data-start-hour="{{.TomorrowStartHour}}">
- <!-- Hour rows -->
- {{range .TomorrowHours}}
- <div class="calendar-hour" data-hour="{{.}}">
- <span class="calendar-hour-label">{{if eq . 0}}12am{{else if lt . 12}}{{.}}am{{else if eq . 12}}12pm{{else}}{{subtract . 12}}pm{{end}}</span>
- </div>
- {{end}}
-
- <!-- Timed events positioned by JavaScript -->
- {{range .TomorrowItems}}
- {{if and (not .IsOverdue) (not .IsAllDay)}}
- <div class="calendar-event source-{{.Source}} {{if .IsCompleted}}opacity-50{{end}}"
- data-hour="{{.Time.Hour}}"
- data-minute="{{.Time.Minute}}"
- data-end-hour="{{if .EndTime}}{{.EndTime.Hour}}{{else}}{{.Time.Hour}}{{end}}"
- data-end-minute="{{if .EndTime}}{{.EndTime.Minute}}{{else}}59{{end}}"
- data-id="{{.ID}}"
- data-source="{{.Source}}"
- data-completed="{{.IsCompleted}}"
- data-type="{{.Type}}"
- data-url="{{.URL}}"
- {{if .ListID}}data-list-id="{{.ListID}}"{{end}}
- style="display:none;">
- <div class="flex items-center gap-2">
- {{if or (eq .Type "task") (eq .Type "card") (eq .Type "gtask")}}
- <input type="checkbox"
- {{if .IsCompleted}}checked{{end}}
- hx-post="{{if .IsCompleted}}/uncomplete-atom{{else}}/complete-atom{{end}}"
- hx-vals='{"id": "{{.ID}}", "source": "{{.Source}}"{{if .ListID}}, "listId": "{{.ListID}}"{{end}}}'
- hx-target="closest .calendar-event"
- hx-swap="outerHTML"
- onclick="event.stopPropagation();"
- class="h-4 w-4 rounded bg-black/40 border-white/30 text-white/80 focus:ring-white/30 cursor-pointer flex-shrink-0">
- {{end}}
- <span class="calendar-event-title {{if .IsCompleted}}line-through text-white/50{{end}}">{{.Title}}</span>
- </div>
- <div class="calendar-event-time">{{.Time.Format "3:04 PM"}}{{if .EndTime}} - {{.EndTime.Format "3:04 PM"}}{{end}}</div>
- </div>
- {{end}}
- {{end}}
- </div>
-
</details>
{{end}}