summaryrefslogtreecommitdiff
path: root/web/templates/partials/timeline-tab.html
diff options
context:
space:
mode:
Diffstat (limited to 'web/templates/partials/timeline-tab.html')
-rw-r--r--web/templates/partials/timeline-tab.html221
1 files changed, 218 insertions, 3 deletions
diff --git a/web/templates/partials/timeline-tab.html b/web/templates/partials/timeline-tab.html
index b1045c6..8745d1d 100644
--- a/web/templates/partials/timeline-tab.html
+++ b/web/templates/partials/timeline-tab.html
@@ -1,11 +1,92 @@
{{define "timeline-tab"}}
+<style>
+ .calendar-grid {
+ position: relative;
+ border-left: 2px solid rgba(255,255,255,0.15);
+ margin-left: 50px;
+ min-height: 640px;
+ }
+ .calendar-hour {
+ position: relative;
+ height: 40px;
+ border-bottom: 1px solid rgba(255,255,255,0.05);
+ }
+ .calendar-hour-label {
+ position: absolute;
+ left: -50px;
+ top: -8px;
+ font-size: 0.7em;
+ color: rgba(255,255,255,0.4);
+ width: 44px;
+ text-align: right;
+ }
+ .calendar-event {
+ position: absolute;
+ left: 8px;
+ right: 8px;
+ background: rgba(0,0,0,0.5);
+ border-radius: 6px;
+ padding: 4px 8px;
+ font-size: 0.8em;
+ border-left: 3px solid;
+ overflow: hidden;
+ z-index: 1;
+ backdrop-filter: blur(4px);
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3);
+ }
+ .calendar-event:hover {
+ z-index: 10;
+ background: rgba(0,0,0,0.7);
+ }
+ .calendar-event.source-todoist { border-color: #e44332; }
+ .calendar-event.source-trello { border-color: #0079bf; }
+ .calendar-event.source-plantoeat { border-color: #5cb85c; }
+ .calendar-event.source-calendar { border-color: #9b59b6; }
+ .calendar-event.source-gtasks { border-color: #f39c12; }
+ .calendar-event-title {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ color: white;
+ font-weight: 500;
+ }
+ .calendar-event-time {
+ font-size: 0.75em;
+ color: rgba(255,255,255,0.6);
+ }
+ .untimed-item {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ background: rgba(0,0,0,0.4);
+ padding: 4px 10px;
+ border-radius: 4px;
+ margin: 2px;
+ font-size: 0.85em;
+ border-left: 3px solid;
+ }
+ .untimed-item.source-todoist { border-color: #e44332; }
+ .untimed-item.source-trello { border-color: #0079bf; }
+ .untimed-item.source-plantoeat { border-color: #5cb85c; }
+ .untimed-item.source-calendar { border-color: #9b59b6; }
+ .untimed-item.source-gtasks { border-color: #f39c12; }
+ .untimed-item.overdue { opacity: 0.8; }
+ .overdue-badge {
+ background: rgba(220,53,69,0.3);
+ color: #ff6b6b;
+ font-size: 0.65em;
+ padding: 1px 4px;
+ border-radius: 3px;
+ }
+</style>
+
<div class="space-y-6 text-shadow-sm"
hx-get="/tabs/timeline"
hx-trigger="refresh-tasks from:body"
hx-target="#tab-content"
hx-swap="innerHTML">
- <!-- Today Section (Expanded, Collapsible) -->
+ <!-- Today Section (Calendar View) -->
{{if .TodayItems}}
<details class="group" open>
<summary class="text-lg font-semibold mb-3 flex items-center gap-2 text-white/90 cursor-pointer hover:text-white sticky top-0 bg-black/20 backdrop-blur-md py-2 z-10 rounded-lg px-2 list-none">
@@ -15,11 +96,145 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</summary>
- <div class="space-y-2 relative pl-4 border-l border-white/10 ml-2">
+
+ <!-- Overdue + All-Day + Untimed Items Section -->
+ <div class="mb-4 flex flex-wrap gap-1 p-2 bg-black/20 rounded-lg" id="untimed-section">
{{range .TodayItems}}
- {{template "timeline-item" .}}
+ {{if or .IsOverdue .IsAllDay}}
+ <div class="untimed-item source-{{.Source}} {{if .IsOverdue}}overdue{{end}} {{if .IsCompleted}}opacity-50{{end}}">
+ {{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="#tab-content"
+ hx-swap="innerHTML"
+ 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="{{if .IsCompleted}}line-through text-white/50{{end}}">{{.Title}}</span>
+ {{if .IsOverdue}}<span class="overdue-badge">overdue</span>{{end}}
+ {{if .URL}}
+ <a href="{{.URL}}" target="_blank" class="text-white/50 hover:text-white">
+ <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>
+ </a>
+ {{end}}
+ </div>
+ {{end}}
{{end}}
</div>
+
+ <!-- Calendar Grid (6am-10pm) -->
+ <div class="calendar-grid" id="today-calendar">
+ <!-- Hour rows -->
+ <div class="calendar-hour" data-hour="6"><span class="calendar-hour-label">6am</span></div>
+ <div class="calendar-hour" data-hour="7"><span class="calendar-hour-label">7am</span></div>
+ <div class="calendar-hour" data-hour="8"><span class="calendar-hour-label">8am</span></div>
+ <div class="calendar-hour" data-hour="9"><span class="calendar-hour-label">9am</span></div>
+ <div class="calendar-hour" data-hour="10"><span class="calendar-hour-label">10am</span></div>
+ <div class="calendar-hour" data-hour="11"><span class="calendar-hour-label">11am</span></div>
+ <div class="calendar-hour" data-hour="12"><span class="calendar-hour-label">12pm</span></div>
+ <div class="calendar-hour" data-hour="13"><span class="calendar-hour-label">1pm</span></div>
+ <div class="calendar-hour" data-hour="14"><span class="calendar-hour-label">2pm</span></div>
+ <div class="calendar-hour" data-hour="15"><span class="calendar-hour-label">3pm</span></div>
+ <div class="calendar-hour" data-hour="16"><span class="calendar-hour-label">4pm</span></div>
+ <div class="calendar-hour" data-hour="17"><span class="calendar-hour-label">5pm</span></div>
+ <div class="calendar-hour" data-hour="18"><span class="calendar-hour-label">6pm</span></div>
+ <div class="calendar-hour" data-hour="19"><span class="calendar-hour-label">7pm</span></div>
+ <div class="calendar-hour" data-hour="20"><span class="calendar-hour-label">8pm</span></div>
+ <div class="calendar-hour" data-hour="21"><span class="calendar-hour-label">9pm</span></div>
+ <div class="calendar-hour" data-hour="22"><span class="calendar-hour-label">10pm</span></div>
+
+ <!-- Timed events positioned by JavaScript -->
+ {{range .TodayItems}}
+ {{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="#tab-content"
+ hx-swap="innerHTML"
+ 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>
+
+ <script>
+ (function() {
+ const calendar = document.getElementById('today-calendar');
+ const events = calendar.querySelectorAll('.calendar-event');
+ const hourHeight = 40;
+ const startHour = 6;
+ const endHour = 22;
+
+ events.forEach(function(el) {
+ const hour = parseInt(el.dataset.hour);
+ const minute = parseInt(el.dataset.minute);
+ const endHourVal = parseInt(el.dataset.endHour);
+ const endMinute = parseInt(el.dataset.endMinute);
+
+ // Skip events outside the visible range
+ if (hour < startHour || hour > endHour) {
+ el.remove();
+ return;
+ }
+
+ // Calculate position
+ const top = (hour - startHour) * hourHeight + (minute / 60) * hourHeight;
+
+ // Calculate height based on duration
+ let durationMinutes;
+ if (endHourVal > hour || (endHourVal === hour && endMinute > minute)) {
+ durationMinutes = (endHourVal - hour) * 60 + (endMinute - minute);
+ } else {
+ durationMinutes = 55; // Default ~1 hour for tasks without duration
+ }
+ const height = Math.max(28, (durationMinutes / 60) * hourHeight - 4);
+
+ el.style.top = top + 'px';
+ el.style.height = height + 'px';
+ el.style.display = 'block';
+
+ // Click to open URL
+ const url = el.dataset.url;
+ if (url) {
+ el.style.cursor = 'pointer';
+ el.addEventListener('click', function(e) {
+ if (e.target.tagName !== 'INPUT') {
+ window.open(url, '_blank');
+ }
+ });
+ }
+ });
+
+ // Hide untimed section if empty
+ const untimedSection = document.getElementById('untimed-section');
+ if (untimedSection && untimedSection.children.length === 0) {
+ untimedSection.style.display = 'none';
+ }
+ })();
+ </script>
</details>
{{end}}