feat: unified calendar view and eisenhower matrix view

This commit is contained in:
2026-03-01 22:17:23 +00:00
parent c21cbf5e9b
commit d792f89fe6
12 changed files with 715 additions and 1 deletions

View File

@@ -60,6 +60,10 @@
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
Appointments
</a>
<a href="/calendar" class="nav-item {{ 'active' if active_nav == 'calendar' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/><rect x="7" y="14" width="3" height="3" rx="0.5"/><rect x="14" y="14" width="3" height="3" rx="0.5"/></svg>
Calendar
</a>
<a href="/decisions" class="nav-item {{ 'active' if active_nav == 'decisions' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"/><path d="M9 12l2 2 4-4"/></svg>
Decisions
@@ -76,6 +80,10 @@
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
Time Log
</a>
<a href="/time-budgets" class="nav-item {{ 'active' if active_nav == 'time_budgets' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/><path d="M16 3l2 2-2 2"/></svg>
Time Budgets
</a>
<a href="/capture" class="nav-item {{ 'active' if active_nav == 'capture' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"/><path d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/></svg>
Capture
@@ -183,7 +191,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
<span>Tasks</span>
</a>
<a href="/appointments" class="mobile-nav-item {% if active_nav == 'appointments' %}active{% endif %}">
<a href="/calendar" class="mobile-nav-item {% if active_nav == 'calendar' %}active{% endif %}">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
<span>Calendar</span>
</a>

58
templates/calendar.html Normal file
View File

@@ -0,0 +1,58 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<div>
<h1 class="page-title">Calendar</h1>
</div>
</div>
<!-- Month Navigation -->
<div class="cal-nav">
<a href="/calendar?year={{ prev_year }}&month={{ prev_month }}" class="btn btn-secondary btn-sm">&larr; Prev</a>
<span class="cal-month-label">{{ month_name }} {{ year }}</span>
<a href="/calendar?year={{ next_year }}&month={{ next_month }}" class="btn btn-secondary btn-sm">Next &rarr;</a>
{% if year != today.year or month != today.month %}
<a href="/calendar" class="btn btn-ghost btn-sm">Today</a>
{% endif %}
</div>
<!-- Legend -->
<div class="cal-legend">
<span class="cal-legend-item"><span class="cal-dot cal-dot-appointment"></span> Appointment</span>
<span class="cal-legend-item"><span class="cal-dot cal-dot-meeting"></span> Meeting</span>
<span class="cal-legend-item"><span class="cal-dot cal-dot-task"></span> Task</span>
</div>
<!-- Calendar Grid -->
<div class="cal-grid">
<div class="cal-header-row">
<div class="cal-header-cell">Sun</div>
<div class="cal-header-cell">Mon</div>
<div class="cal-header-cell">Tue</div>
<div class="cal-header-cell">Wed</div>
<div class="cal-header-cell">Thu</div>
<div class="cal-header-cell">Fri</div>
<div class="cal-header-cell">Sat</div>
</div>
{% for week in weeks %}
<div class="cal-week-row">
{% for day in week %}
<div class="cal-day-cell {{ 'cal-day-empty' if day == 0 }} {{ 'cal-day-today' if day > 0 and today.year == year and today.month == month and today.day == day }}">
{% if day > 0 %}
<div class="cal-day-num">{{ day }}</div>
<div class="cal-events">
{% for event in days_map.get(day, []) %}
<a href="{{ event.url }}" class="cal-event cal-event-{{ event.type }}" title="{{ event.title }}">
{% if event.time %}<span class="cal-event-time">{{ event.time }}</span>{% endif %}
<span class="cal-event-title">{{ event.title }}</span>
</a>
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endfor %}
</div>
{% endblock %}

View File

@@ -0,0 +1,76 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<div>
<h1 class="page-title">Time Budgets <span class="page-count">({{ count }})</span></h1>
</div>
<a href="/time-budgets/create" class="btn btn-primary">+ New Budget</a>
</div>
{% if overcommitted %}
<div class="alert alert-warning mb-3">
<strong>Overcommitted!</strong> Your budgets total {{ "%.1f"|format(total_budgeted) }} hours/week, which exceeds the 168 hours available.
</div>
{% endif %}
{% if current_budgets %}
<div class="card mb-4">
<div class="card-header">
<span class="card-title">This Week's Budget vs Actual</span>
<span class="text-muted text-sm">{{ "%.1f"|format(total_budgeted) }}h budgeted total</span>
</div>
{% for b in current_budgets %}
<div class="list-row" style="flex-wrap: wrap; gap: 8px;">
<span class="domain-dot" style="background: {{ b.domain_color or '#4F6EF7' }}; flex-shrink: 0;"></span>
<div class="row-title" style="min-width: 120px;">
{{ b.domain_name }}
</div>
<div style="flex: 2; min-width: 200px; display: flex; align-items: center; gap: 8px;">
<div class="progress-bar" style="flex: 1; height: 8px;">
<div class="progress-fill" style="width: {{ [b.pct, 100] | min }}%; {{ 'background: var(--red);' if b.pct > 100 }}"></div>
</div>
</div>
<span class="row-meta" style="min-width: 100px; text-align: right;">
<strong>{{ b.actual_hours }}h</strong> / {{ b.weekly_hours_float }}h
</span>
<span class="row-meta" style="min-width: 40px; text-align: right; {{ 'color: var(--red); font-weight: 600;' if b.pct > 100 else ('color: var(--green);' if b.pct >= 80 else '') }}">
{{ b.pct }}%
</span>
</div>
{% endfor %}
</div>
{% endif %}
{% if all_budgets %}
<div class="card">
<div class="card-header">
<span class="card-title">All Budgets</span>
</div>
{% for b in all_budgets %}
<div class="list-row">
<span class="domain-dot" style="background: {{ b.domain_color or '#4F6EF7' }}; flex-shrink: 0;"></span>
<div class="row-title">
{{ b.domain_name }}
</div>
<span class="row-meta">{{ b.weekly_hours }}h / week</span>
<span class="row-meta">from {{ b.effective_from.strftime('%b %-d, %Y') if b.effective_from else '—' }}</span>
<div class="row-actions">
<a href="/time-budgets/{{ b.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form method="POST" action="/time-budgets/{{ b.id }}/delete" data-confirm="Delete this budget?">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red);">Delete</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state">
<div class="empty-state-icon">&#9201;</div>
<div class="empty-state-text">No time budgets defined</div>
<a href="/time-budgets/create" class="btn btn-primary">Create a Budget</a>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,47 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<div>
<h1 class="page-title">{{ 'Edit Time Budget' if budget else 'New Time Budget' }}</h1>
</div>
</div>
<div class="card" style="max-width: 600px;">
<form method="POST" action="{{ '/time-budgets/' ~ budget.id ~ '/edit' if budget else '/time-budgets/create' }}">
<div class="form-grid" style="grid-template-columns: 1fr;">
<div class="form-group">
<label class="form-label">Domain *</label>
<select name="domain_id" class="form-select" required>
<option value="">Select domain...</option>
{% for d in domains %}
<option value="{{ d.id }}" {{ 'selected' if budget and budget.domain_id|string == d.id|string }}>
{{ d.name }}
</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Weekly Hours *</label>
<input type="number" name="weekly_hours" class="form-input"
value="{{ budget.weekly_hours if budget else '' }}"
min="0" max="168" step="0.5" required
placeholder="e.g. 10">
</div>
<div class="form-group">
<label class="form-label">Effective From *</label>
<input type="date" name="effective_from" class="form-input"
value="{{ budget.effective_from.strftime('%Y-%m-%d') if budget and budget.effective_from else '' }}"
required>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save Changes' if budget else 'Create Budget' }}</button>
<a href="/time-budgets" class="btn btn-secondary">Cancel</a>
</div>
</div>
</form>
</div>
{% endblock %}