Initial commit

This commit is contained in:
2026-03-03 00:44:33 +00:00
commit 5297da485f
126 changed files with 54767 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
<a href="/appointments">Appointments</a>
<span class="sep">/</span>
<span>{{ appointment.title }}</span>
</div>
<div class="detail-header">
<div style="display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;">
<div>
<h1 class="detail-title">{{ appointment.title }}</h1>
<div class="detail-meta">
{% if appointment.all_day %}
<span class="detail-meta-item">
<span class="status-badge status-active">All Day</span>
</span>
{% endif %}
<span class="detail-meta-item">
<svg width="14" height="14" 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>
{% if appointment.all_day %}
{{ appointment.start_at.strftime('%A, %B %-d, %Y') }}
{% if appointment.end_at and appointment.end_at.strftime('%Y-%m-%d') != appointment.start_at.strftime('%Y-%m-%d') %}
&ndash; {{ appointment.end_at.strftime('%A, %B %-d, %Y') }}
{% endif %}
{% else %}
{{ appointment.start_at.strftime('%A, %B %-d, %Y at %-I:%M %p') }}
{% if appointment.end_at %}
&ndash; {{ appointment.end_at.strftime('%-I:%M %p') }}
{% endif %}
{% endif %}
</span>
{% if appointment.location %}
<span class="detail-meta-item">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
{{ appointment.location }}
</span>
{% endif %}
{% if appointment.recurrence %}
<span class="detail-meta-item">
<span class="row-tag">{{ appointment.recurrence }}</span>
</span>
{% endif %}
</div>
</div>
<div style="display: flex; gap: 8px; flex-shrink: 0;">
<a href="/appointments/{{ appointment.id }}/edit" class="btn btn-secondary btn-sm">Edit</a>
<form method="POST" action="/appointments/{{ appointment.id }}/delete" data-confirm="Delete this appointment?">
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
</form>
</div>
</div>
</div>
{% if appointment.description %}
<div class="card mb-4">
<div class="card-title mb-2">Description</div>
<div class="detail-body">{{ appointment.description }}</div>
</div>
{% endif %}
{% if appointment.tags %}
<div style="display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 16px;">
{% for tag in appointment.tags %}
<span class="row-tag">{{ tag }}</span>
{% endfor %}
</div>
{% endif %}
<!-- Attendees -->
<div class="card">
<div class="card-header">
<span class="card-title">Attendees ({{ contacts | length }})</span>
</div>
{% if contacts %}
{% for c in contacts %}
<div class="list-row">
<div class="row-title">
<a href="/contacts/{{ c.id }}">{{ c.first_name }} {{ c.last_name or '' }}</a>
</div>
{% if c.company %}
<span class="row-meta">{{ c.company }}</span>
{% endif %}
{% if c.email %}
<span class="row-meta">{{ c.email }}</span>
{% endif %}
{% if c.role %}
<span class="row-tag">{{ c.role }}</span>
{% endif %}
</div>
{% endfor %}
{% else %}
<div class="empty-state" style="padding: 24px;">
<div class="text-muted text-sm">No attendees added</div>
</div>
{% endif %}
</div>
<div class="text-muted text-xs mt-3">
Created {{ appointment.created_at.strftime('%B %-d, %Y') }}
{% if appointment.updated_at and appointment.updated_at != appointment.created_at %}
&middot; Updated {{ appointment.updated_at.strftime('%B %-d, %Y') }}
{% endif %}
</div>
{% endblock %}

View File

@@ -0,0 +1,130 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
<a href="/appointments">Appointments</a>
<span class="sep">/</span>
<span>{{ "Edit" if appointment else "New Appointment" }}</span>
</div>
<div class="page-header">
<h1 class="page-title">{{ "Edit Appointment" if appointment else "New Appointment" }}</h1>
</div>
<div class="card" style="max-width: 720px;">
<form method="POST" action="{{ '/appointments/' ~ appointment.id ~ '/edit' if appointment else '/appointments/create' }}">
<div class="form-grid">
<div class="form-group full-width">
<label class="form-label">Title *</label>
<input type="text" name="title" class="form-input" required value="{{ appointment.title if appointment else '' }}">
</div>
<div class="form-group">
<label class="form-label">Start Date *</label>
<input type="date" name="start_date" class="form-input" required
value="{{ appointment.start_at.strftime('%Y-%m-%d') if appointment and appointment.start_at else '' }}">
</div>
<div class="form-group" id="start-time-group">
<label class="form-label">Start Time</label>
<input type="time" name="start_time" class="form-input" id="start-time-input"
value="{{ appointment.start_at.strftime('%H:%M') if appointment and appointment.start_at and not appointment.all_day else '' }}">
</div>
<div class="form-group">
<label class="form-label">End Date</label>
<input type="date" name="end_date" class="form-input"
value="{{ appointment.end_at.strftime('%Y-%m-%d') if appointment and appointment.end_at else '' }}">
</div>
<div class="form-group" id="end-time-group">
<label class="form-label">End Time</label>
<input type="time" name="end_time" class="form-input" id="end-time-input"
value="{{ appointment.end_at.strftime('%H:%M') if appointment and appointment.end_at and not appointment.all_day else '' }}">
</div>
<div class="form-group">
<label class="form-label" style="display: flex; align-items: center; gap: 8px;">
<input type="checkbox" name="all_day" id="all-day-check"
{{ 'checked' if appointment and appointment.all_day else '' }}
onchange="toggleAllDay(this.checked)"
style="width: 16px; height: 16px;">
All Day Event
</label>
</div>
<div class="form-group">
<label class="form-label">Recurrence</label>
<select name="recurrence" class="form-select">
<option value="">None</option>
<option value="daily" {{ 'selected' if appointment and appointment.recurrence == 'daily' }}>Daily</option>
<option value="weekly" {{ 'selected' if appointment and appointment.recurrence == 'weekly' }}>Weekly</option>
<option value="monthly" {{ 'selected' if appointment and appointment.recurrence == 'monthly' }}>Monthly</option>
</select>
</div>
<div class="form-group full-width">
<label class="form-label">Location</label>
<input type="text" name="location" class="form-input" placeholder="Address, room, or video link"
value="{{ appointment.location if appointment and appointment.location else '' }}">
</div>
<div class="form-group full-width">
<label class="form-label">Description</label>
<textarea name="description" class="form-textarea" rows="3" placeholder="Notes about this appointment...">{{ appointment.description if appointment and appointment.description else '' }}</textarea>
</div>
<div class="form-group full-width">
<label class="form-label">Tags</label>
<input type="text" name="tags" class="form-input" placeholder="Comma-separated tags"
value="{{ appointment.tags | join(', ') if appointment and appointment.tags else '' }}">
</div>
{% if contacts %}
<div class="form-group full-width">
<label class="form-label">Attendees</label>
<div style="display: flex; flex-wrap: wrap; gap: 8px; padding: 8px; background: var(--surface2); border-radius: var(--radius); border: 1px solid var(--border); max-height: 200px; overflow-y: auto;">
{% for c in contacts %}
<label style="display: flex; align-items: center; gap: 6px; padding: 4px 10px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); font-size: 0.85rem; cursor: pointer; white-space: nowrap;">
<input type="checkbox" name="contact_ids" value="{{ c.id }}"
{{ 'checked' if c.id|string in selected_contacts else '' }}
style="width: 14px; height: 14px;">
{{ c.first_name }} {{ c.last_name or '' }}
{% if c.company %}<span style="color: var(--muted); font-size: 0.78rem;">({{ c.company }})</span>{% endif %}
</label>
{% endfor %}
</div>
</div>
{% endif %}
<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ "Update Appointment" if appointment else "Create Appointment" }}</button>
<a href="{{ '/appointments/' ~ appointment.id if appointment else '/appointments' }}" class="btn btn-secondary">Cancel</a>
</div>
</div>
</form>
</div>
<script>
function toggleAllDay(checked) {
const startTime = document.getElementById('start-time-input');
const endTime = document.getElementById('end-time-input');
if (checked) {
startTime.disabled = true;
startTime.style.opacity = '0.4';
endTime.disabled = true;
endTime.style.opacity = '0.4';
} else {
startTime.disabled = false;
startTime.style.opacity = '1';
endTime.disabled = false;
endTime.style.opacity = '1';
}
}
// Init on load
document.addEventListener('DOMContentLoaded', () => {
const cb = document.getElementById('all-day-check');
if (cb && cb.checked) toggleAllDay(true);
});
</script>
{% endblock %}

View File

@@ -0,0 +1,72 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<div>
<h1 class="page-title">Appointments <span class="page-count">({{ count }})</span></h1>
</div>
<a href="/appointments/new" class="btn btn-primary">+ New Appointment</a>
</div>
<!-- Filters -->
<div class="filters-bar">
<a href="/appointments?timeframe=upcoming" class="btn {{ 'btn-primary' if timeframe == 'upcoming' else 'btn-secondary' }} btn-sm">Upcoming</a>
<a href="/appointments?timeframe=past" class="btn {{ 'btn-primary' if timeframe == 'past' else 'btn-secondary' }} btn-sm">Past</a>
<a href="/appointments?timeframe=all" class="btn {{ 'btn-primary' if timeframe == 'all' else 'btn-secondary' }} btn-sm">All</a>
</div>
{% if appointments %}
<div class="card">
{% set current_date = namespace(value='') %}
{% for appt in appointments %}
{% set appt_date = appt.start_at.strftime('%A, %B %-d, %Y') if appt.start_at else 'No Date' %}
{% if appt_date != current_date.value %}
{% if not loop.first %}</div>{% endif %}
<div class="date-group-label" style="padding: 12px 12px 4px; font-size: 0.78rem; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; {% if not loop.first %}border-top: 1px solid var(--border); margin-top: 4px;{% endif %}">
{{ appt_date }}
</div>
<div>
{% set current_date.value = appt_date %}
{% endif %}
<div class="list-row">
<div style="flex-shrink: 0; min-width: 60px;">
{% if appt.all_day %}
<span class="status-badge status-active" style="font-size: 0.72rem;">All Day</span>
{% elif appt.start_at %}
<span style="font-size: 0.85rem; font-weight: 600; color: var(--text);">{{ appt.start_at.strftime('%-I:%M %p') }}</span>
{% endif %}
</div>
<div class="row-title">
<a href="/appointments/{{ appt.id }}">{{ appt.title }}</a>
</div>
{% if appt.location %}
<span class="row-meta">{{ appt.location }}</span>
{% endif %}
{% if appt.recurrence %}
<span class="row-tag">{{ appt.recurrence }}</span>
{% endif %}
{% if appt.contact_count and appt.contact_count > 0 %}
<span class="row-meta">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: -2px;"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
{{ appt.contact_count }}
</span>
{% endif %}
<div class="row-actions">
<a href="/appointments/{{ appt.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form method="POST" action="/appointments/{{ appt.id }}/delete" data-confirm="Delete this appointment?">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red);">Delete</button>
</form>
</div>
</div>
{% endfor %}
</div>
</div>
{% else %}
<div class="empty-state">
<div class="empty-state-icon">📅</div>
<div class="empty-state-text">No appointments {{ 'upcoming' if timeframe == 'upcoming' else 'found' }}</div>
<a href="/appointments/new" class="btn btn-primary">Schedule an Appointment</a>
</div>
{% endif %}
{% endblock %}

22
templates/area_form.html Normal file
View File

@@ -0,0 +1,22 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb"><a href="/areas">Areas</a><span class="sep">/</span><span>{{ 'Edit' if item else 'New Area' }}</span></div>
<div class="page-header"><h1 class="page-title">{{ 'Edit Area' if item else 'New Area' }}</h1></div>
<div class="card">
<form method="post" action="{{ '/areas/' ~ item.id ~ '/edit' if item else '/areas/create' }}">
<div class="form-grid">
<div class="form-group"><label class="form-label">Name *</label><input type="text" name="name" class="form-input" required value="{{ item.name if item else '' }}"></div>
<div class="form-group"><label class="form-label">Domain *</label>
<select name="domain_id" class="form-select" required>
{% for d in domains %}<option value="{{ d.id }}" {{ 'selected' if (item and item.domain_id|string == d.id|string) or (not item and prefill_domain_id == d.id|string) }}>{{ d.name }}</option>{% endfor %}
</select></div>
<div class="form-group"><label class="form-label">Status</label>
<select name="status" class="form-select">
<option value="active" {{ 'selected' if not item or item.status == 'active' }}>Active</option>
<option value="inactive" {{ 'selected' if item and item.status == 'inactive' }}>Inactive</option>
</select></div>
<div class="form-group full-width"><label class="form-label">Description</label><textarea name="description" class="form-textarea" rows="3">{{ item.description if item and item.description else '' }}</textarea></div>
<div class="form-actions"><button type="submit" class="btn btn-primary">{{ 'Save' if item else 'Create' }}</button><a href="/areas" class="btn btn-secondary">Cancel</a></div>
</div>
</form></div>
{% endblock %}

25
templates/areas.html Normal file
View File

@@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Areas<span class="page-count">{{ items|length }}</span></h1>
<a href="/areas/create" class="btn btn-primary">+ New Area</a>
</div>
{% if items %}
<div class="card">
{% for item in items %}
<div class="list-row">
<span class="row-domain-tag" style="background:{{ item.domain_color or '#4F6EF7' }}22;color:{{ item.domain_color or '#4F6EF7' }}">{{ item.domain_name }}</span>
<span class="row-title">{{ item.name }}</span>
{% if item.description %}<span class="row-meta">{{ item.description[:60] }}</span>{% endif %}
<span class="status-badge status-{{ item.status }}">{{ item.status }}</span>
<div class="row-actions">
<a href="/areas/{{ item.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form action="/areas/{{ item.id }}/delete" method="post" data-confirm="Delete this area?" style="display:inline"><button class="btn btn-ghost btn-xs" style="color:var(--red)">Del</button></form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No areas yet</div><a href="/areas/create" class="btn btn-primary">Create Area</a></div>
{% endif %}
{% endblock %}

241
templates/base.html Normal file
View File

@@ -0,0 +1,241 @@
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ page_title or "Life OS" }} - Life OS</title>
<link rel="stylesheet" href="/static/style.css">
<style>
/* Critical mob-nav positioning - inline to guarantee it loads */
.mob-nav{display:none}
@media(max-width:768px){
.mob-nav{display:flex!important;position:fixed!important;bottom:0!important;left:0!important;right:0!important;height:56px!important;background:var(--surface)!important;border-top:1px solid var(--border)!important;z-index:9999!important;flex-direction:row!important;flex-wrap:nowrap!important;align-items:center!important;justify-content:space-around!important;padding:0!important;margin:0!important;overflow:hidden!important}
.mob-overlay{display:none!important;position:fixed!important;top:0!important;left:0!important;right:0!important;bottom:0!important;background:rgba(0,0,0,.5)!important;z-index:10000!important}
.mob-overlay.open{display:block!important}
.mob-more{display:none!important;position:fixed!important;bottom:56px!important;left:0!important;right:0!important;background:var(--surface)!important;border-top:1px solid var(--border)!important;border-radius:12px 12px 0 0!important;z-index:10001!important;padding:16px!important;grid-template-columns:repeat(3,1fr)!important;gap:8px!important}
.mob-more.open{display:grid!important}
}
</style>
</head>
<body>
<div class="app-layout">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<a href="/" class="sidebar-logo">Life<span>OS</span></a>
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<a href="/" class="nav-item {{ 'active' if active_nav == 'dashboard' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
Dashboard
</a>
<a href="/focus" class="nav-item {{ 'active' if active_nav == 'focus' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>
Focus
{% if sidebar.focus_count %}<span class="badge">{{ sidebar.focus_count }}</span>{% endif %}
</a>
<a href="/tasks" class="nav-item {{ 'active' if active_nav == 'tasks' }}">
<svg class="nav-icon" 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>
All Tasks
</a>
<a href="/projects" class="nav-item {{ 'active' if active_nav == 'projects' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
Projects
</a>
<a href="/notes" class="nav-item {{ 'active' if active_nav == 'notes' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>
Notes
</a>
<a href="/contacts" class="nav-item {{ 'active' if active_nav == 'contacts' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
Contacts
</a>
<a href="/links" class="nav-item {{ 'active' if active_nav == 'links' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
Links
</a>
<a href="/lists" class="nav-item {{ 'active' if active_nav == 'lists' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
Lists
</a>
<a href="/processes" class="nav-item {{ 'active' if active_nav == 'processes' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
Processes
</a>
<a href="/meetings" class="nav-item {{ 'active' if active_nav == 'meetings' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
Meetings
</a>
<a href="/appointments" class="nav-item {{ 'active' if active_nav == 'appointments' }}">
<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="/eisenhower" class="nav-item {{ 'active' if active_nav == 'eisenhower' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="8" height="8" rx="1"/><rect x="13" y="3" width="8" height="8" rx="1"/><rect x="3" y="13" width="8" height="8" rx="1"/><rect x="13" y="13" width="8" height="8" rx="1"/></svg>
Eisenhower
</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
</a>
<a href="/files" class="nav-item {{ 'active' if active_nav == 'files' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
Files
</a>
<a href="/weblinks" class="nav-item {{ 'active' if active_nav == 'bookmarks' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
Bookmarks
</a>
<a href="/time" class="nav-item {{ 'active' if active_nav == 'time' }}">
<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
{% if sidebar.capture_count %}<span class="badge">{{ sidebar.capture_count }}</span>{% endif %}
</a>
</div>
<!-- Domain hierarchy -->
<div class="nav-section">
<div class="nav-section-label">Domains</div>
{% for domain in sidebar.domain_tree %}
<div class="domain-group">
<div class="domain-header" data-domain-id="{{ domain.id }}">
<span class="domain-dot" style="background: {{ domain.color or '#4F6EF7' }}"></span>
{{ domain.name }}
</div>
<div class="domain-children" data-domain-id="{{ domain.id }}">
{% for area in domain.areas %}
<div class="area-label">{{ area.name }}</div>
{% for p in area.projects %}
<a href="/projects/{{ p.id }}" class="project-link">{{ p.name }}</a>
{% endfor %}
{% endfor %}
{% for p in domain.standalone_projects %}
<a href="/projects/{{ p.id }}" class="project-link">{{ p.name }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<div class="nav-section" style="margin-top: auto; padding-bottom: 12px;">
<a href="/history" class="nav-item {{ 'active' if active_nav == 'history' }}">
<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>
History
</a>
<a href="/admin/trash" class="nav-item {{ 'active' if active_nav == 'trash' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
Trash
</a>
<a href="/domains" class="nav-item {{ 'active' if active_nav == 'domains' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 1v6m0 6v6m-7-7h6m6 0h6"/></svg>
Manage Domains
</a>
<a href="/areas" class="nav-item {{ 'active' if active_nav == 'areas' }}">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="9" y1="21" x2="9" y2="9"/></svg>
Manage Areas
</a>
<div class="nav-item" onclick="toggleTheme()" style="cursor: pointer;">
<svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
Toggle Theme
</div>
</div>
</nav>
</aside>
<!-- Main content -->
<main class="main-content">
<header class="topbar">
{% if request.state.environment == 'development' %}
<span class="topbar-env">DEV</span>
{% endif %}
<div class="topbar-spacer"></div>
<!-- Timer Pill (populated by JS) -->
<div id="timer-pill" class="timer-pill hidden">
<div class="timer-pill-dot"></div>
<a id="timer-pill-task" class="timer-pill-task" href="#"></a>
<span id="timer-pill-elapsed" class="timer-pill-elapsed">0:00</span>
<form method="POST" action="/time/stop" style="display:inline;">
<button type="submit" class="timer-pill-stop" title="Stop timer">&#9632;</button>
</form>
</div>
<form class="quick-capture-form" method="POST" action="/capture/add">
<input type="hidden" name="redirect_to" value="{{ request.url.path }}{% if request.url.query %}?{{ request.url.query }}{% endif %}">
<span class="quick-capture-icon">+</span>
<input type="text" name="raw_text" class="quick-capture-input" placeholder="Quick capture..." autocomplete="off" required>
<button type="submit" class="quick-capture-submit" title="Capture">+</button>
</form>
<button class="search-trigger" onclick="openSearch()" title="Search (Cmd/K)">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<span>Search...</span>
<kbd>&#8984;K</kbd>
</button>
</header>
<div class="page-content">
{% block content %}{% endblock %}
</div>
</main>
</div>
<!-- Search Modal -->
<div id="search-modal" class="search-modal hidden">
<div class="search-modal-backdrop" onclick="closeSearch()"></div>
<div class="search-modal-content">
<div class="search-modal-input-wrap">
<svg class="search-modal-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="search-input" class="search-modal-input" placeholder="Search tasks, projects, notes..." autocomplete="off">
<kbd class="search-modal-esc" onclick="closeSearch()">Esc</kbd>
</div>
<div id="search-results" class="search-results"></div>
</div>
</div>
<script src="/static/app.js"></script>
<div class="mob-nav" id="mobNav" style="position:fixed;bottom:0;left:0;right:0;z-index:9999">
<a href="/" class="mob-nav__item {% if request.url.path == '/' %}mob-nav__item--active{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/></svg>
<span>Home</span>
</a>
<a href="/focus" class="mob-nav__item {% if '/focus' in request.url.path %}mob-nav__item--active{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>
<span>Focus</span>
</a>
<a href="/tasks" class="mob-nav__item {% if '/tasks' in request.url.path %}mob-nav__item--active{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11"/></svg>
<span>Tasks</span>
</a>
<a href="/capture" class="mob-nav__item {% if '/capture' in request.url.path %}mob-nav__item--active{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
<span>Capture</span>
</a>
<button class="mob-nav__item" id="mobMoreBtn" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
<span>More</span>
</button>
</div>
<div class="mob-overlay" id="mobOverlay"></div>
<div class="mob-more" id="mobMore">
<a href="/calendar" class="mob-more__item"><span>Calendar</span></a>
<a href="/notes" class="mob-more__item"><span>Notes</span></a>
<a href="/meetings" class="mob-more__item"><span>Meetings</span></a>
<a href="/decisions" class="mob-more__item"><span>Decisions</span></a>
<a href="/contacts" class="mob-more__item"><span>Contacts</span></a>
<a href="/processes" class="mob-more__item"><span>Processes</span></a>
<a href="/weblinks" class="mob-more__item"><span>Bookmarks</span></a>
<a href="/admin" class="mob-more__item"><span>Admin</span></a>
</div>
</body>
</html>

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 %}

110
templates/capture.html Normal file
View File

@@ -0,0 +1,110 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Capture<span class="page-count">{{ items|length }}</span></h1>
</div>
<!-- Quick capture input -->
<div class="card mb-4 capture-form-card">
<form action="/capture/add" method="post">
<label class="form-label mb-2">Quick Capture (one item per line)</label>
<textarea name="raw_text" class="form-textarea" rows="3" placeholder="Type or paste to capture..." required></textarea>
<details class="mt-2">
<summary class="text-sm text-muted" style="cursor:pointer">Context (optional)</summary>
<div class="form-grid mt-2" style="grid-template-columns:1fr 1fr">
<div class="form-group">
<label class="form-label">Project</label>
<select name="project_id" class="form-select">
<option value="">None</option>
{% for d in sidebar.domain_tree %}
<optgroup label="{{ d.name }}">
{% for a in d.areas %}{% for p in a.projects %}
<option value="{{ p.id }}">{{ p.name }}</option>
{% endfor %}{% endfor %}
{% for p in d.standalone_projects %}
<option value="{{ p.id }}">{{ p.name }}</option>
{% endfor %}
</optgroup>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Area</label>
<select name="area_id" class="form-select">
<option value="">None</option>
{% for d in sidebar.domain_tree %}
{% for a in d.areas %}
<option value="{{ a.id }}">{{ d.name }} / {{ a.name }}</option>
{% endfor %}
{% endfor %}
</select>
</div>
</div>
</details>
<div class="mt-2"><button type="submit" class="btn btn-primary btn-capture">Capture</button></div>
</form>
</div>
<!-- Filter tabs -->
<div class="tab-strip mb-3">
<a href="/capture?show=inbox" class="tab-item {{ 'active' if show == 'inbox' or show not in ('processed', 'all') }}">Inbox</a>
<a href="/capture?show=processed" class="tab-item {{ 'active' if show == 'processed' }}">Processed</a>
<a href="/capture?show=all" class="tab-item {{ 'active' if show == 'all' }}">All</a>
</div>
{% if items %}
{% for item in items %}
{# Batch header #}
{% if item._batch_first and item.import_batch_id %}
<div class="flex items-center justify-between mb-2 mt-3" style="padding:6px 12px;background:var(--surface2);border-radius:var(--radius-sm)">
<span class="text-xs text-muted">Batch &middot; {{ batches[item.import_batch_id|string] }} items</span>
<form action="/capture/batch/{{ item.import_batch_id }}/undo" method="post" style="display:inline" data-confirm="Delete all items in this batch?">
<button class="btn btn-ghost btn-xs" style="color:var(--red)">Undo batch</button>
</form>
</div>
{% endif %}
<div class="capture-item {{ 'completed' if item.processed }}">
<div class="capture-text {{ 'text-muted' if item.processed }}" style="{{ 'text-decoration:line-through' if item.processed }}">{{ item.raw_text }}</div>
{% if item.processed %}
<div class="capture-actions">
{% if item.converted_to_type and item.converted_to_type != 'dismissed' %}
<span class="row-tag">{{ item.converted_to_type|replace('_', ' ') }}</span>
{% if item.converted_to_id %}
{% if item.converted_to_type == 'list_item' and item.list_id %}
<a href="/lists/{{ item.list_id }}" class="btn btn-ghost btn-xs">View &rarr;</a>
{% elif item.converted_to_type == 'link' %}
<a href="/links" class="btn btn-ghost btn-xs">View &rarr;</a>
{% elif item.converted_to_type in ('task', 'note', 'project', 'contact', 'decision') %}
<a href="/{{ item.converted_to_type }}s/{{ item.converted_to_id }}" class="btn btn-ghost btn-xs">View &rarr;</a>
{% endif %}
{% endif %}
{% elif item.converted_to_type == 'dismissed' %}
<span class="row-tag" style="color:var(--muted)">dismissed</span>
{% endif %}
</div>
{% else %}
<div class="capture-actions">
<select onchange="if(this.value) window.location.href=this.value" class="filter-select" style="font-size:0.75rem;padding:3px 6px">
<option value="">Convert...</option>
{% for key, label in convert_types.items() %}
<option value="/capture/{{ item.id }}/convert/{{ key }}">{{ label }}</option>
{% endfor %}
</select>
<form action="/capture/{{ item.id }}/dismiss" method="post" style="display:inline"><button class="btn btn-ghost btn-xs">Dismiss</button></form>
<form action="/capture/{{ item.id }}/delete" method="post" style="display:inline"><button class="btn btn-ghost btn-xs" style="color:var(--red)">&times;</button></form>
</div>
{% endif %}
</div>
{% endfor %}
{% else %}
<div class="empty-state">
<div class="empty-state-icon">&#128229;</div>
<div class="empty-state-text">
{% if show == 'processed' %}No processed items{% elif show == 'all' %}No capture items{% else %}Inbox is empty{% endif %}
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,153 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
<a href="/capture">Capture</a> <span class="sep">/</span> Convert to {{ type_label }}
</div>
<div class="page-header">
<h1 class="page-title">Convert to {{ type_label }}</h1>
</div>
<div class="card">
<!-- Original text -->
<div class="form-group mb-3">
<label class="form-label">Original Text</label>
<div style="padding:10px 12px;background:var(--surface2);border-radius:var(--radius);font-size:0.92rem;color:var(--text-secondary)">{{ item.raw_text }}</div>
</div>
<form action="/capture/{{ item.id }}/to-{{ convert_type }}" method="post">
<div class="form-grid">
{% if convert_type == 'task' %}
<div class="form-group full-width">
<label class="form-label">Title</label>
<input type="text" name="title" class="form-input" value="{{ item.raw_text }}" required>
</div>
<div class="form-group">
<label class="form-label">Domain *</label>
<select name="domain_id" class="form-select" required>
{% for d in sidebar.domain_tree %}
<option value="{{ d.id }}">{{ d.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Project</label>
<select name="project_id" class="form-select">
<option value="">None</option>
{% for d in sidebar.domain_tree %}
<optgroup label="{{ d.name }}">
{% for a in d.areas %}{% for p in a.projects %}
<option value="{{ p.id }}" {{ 'selected' if item.project_id and p.id|string == item.project_id|string }}>{{ p.name }}</option>
{% endfor %}{% endfor %}
{% for p in d.standalone_projects %}
<option value="{{ p.id }}" {{ 'selected' if item.project_id and p.id|string == item.project_id|string }}>{{ p.name }}</option>
{% endfor %}
</optgroup>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Priority</label>
<select name="priority" class="form-select">
<option value="1">1 - Critical</option>
<option value="2">2 - High</option>
<option value="3" selected>3 - Normal</option>
<option value="4">4 - Low</option>
</select>
</div>
{% elif convert_type == 'note' %}
<div class="form-group full-width">
<label class="form-label">Title</label>
<input type="text" name="title" class="form-input" value="{{ item.raw_text[:100] }}" required>
</div>
<div class="form-group">
<label class="form-label">Domain</label>
<select name="domain_id" class="form-select">
<option value="">None</option>
{% for d in sidebar.domain_tree %}
<option value="{{ d.id }}">{{ d.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Project</label>
<select name="project_id" class="form-select">
<option value="">None</option>
{% for d in sidebar.domain_tree %}
<optgroup label="{{ d.name }}">
{% for a in d.areas %}{% for p in a.projects %}
<option value="{{ p.id }}" {{ 'selected' if item.project_id and p.id|string == item.project_id|string }}>{{ p.name }}</option>
{% endfor %}{% endfor %}
{% for p in d.standalone_projects %}
<option value="{{ p.id }}" {{ 'selected' if item.project_id and p.id|string == item.project_id|string }}>{{ p.name }}</option>
{% endfor %}
</optgroup>
{% endfor %}
</select>
</div>
{% elif convert_type == 'project' %}
<div class="form-group full-width">
<label class="form-label">Project Name</label>
<input type="text" name="name" class="form-input" value="{{ item.raw_text }}" required>
</div>
<div class="form-group">
<label class="form-label">Domain *</label>
<select name="domain_id" class="form-select" required>
{% for d in sidebar.domain_tree %}
<option value="{{ d.id }}">{{ d.name }}</option>
{% endfor %}
</select>
</div>
{% elif convert_type == 'list_item' %}
<div class="form-group full-width">
<label class="form-label">Content</label>
<input type="text" name="content" class="form-input" value="{{ item.raw_text }}" required>
</div>
<div class="form-group">
<label class="form-label">Add to List *</label>
<select name="list_id" class="form-select" required>
{% for lst in all_lists %}
<option value="{{ lst.id }}" {{ 'selected' if item.list_id and lst.id|string == item.list_id|string }}>{{ lst.name }}</option>
{% endfor %}
</select>
</div>
{% elif convert_type == 'contact' %}
<div class="form-group">
<label class="form-label">First Name *</label>
<input type="text" name="first_name" class="form-input" value="{{ first_name }}" required>
</div>
<div class="form-group">
<label class="form-label">Last Name</label>
<input type="text" name="last_name" class="form-input" value="{{ last_name }}">
</div>
{% elif convert_type == 'decision' %}
<div class="form-group full-width">
<label class="form-label">Decision Title</label>
<input type="text" name="title" class="form-input" value="{{ item.raw_text }}" required>
</div>
{% elif convert_type == 'link' %}
<div class="form-group full-width">
<label class="form-label">Label</label>
<input type="text" name="label" class="form-input" value="{{ prefill_label }}" required>
</div>
<div class="form-group full-width">
<label class="form-label">URL</label>
<input type="url" name="url" class="form-input" value="{{ prefill_url }}" placeholder="https://" required>
</div>
{% endif %}
<div class="form-actions">
<button type="submit" class="btn btn-primary">Convert to {{ type_label }}</button>
<a href="/capture" class="btn btn-secondary">Cancel</a>
</div>
</div>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb"><a href="/contacts">Contacts</a><span class="sep">/</span><span>{{ item.first_name }} {{ item.last_name or '' }}</span></div>
<div class="detail-header">
<div class="flex items-center justify-between">
<h1 class="detail-title">{{ item.first_name }} {{ item.last_name or '' }}</h1>
<div class="flex gap-2">
<a href="/contacts/{{ item.id }}/edit" class="btn btn-secondary btn-sm">Edit</a>
<form action="/contacts/{{ item.id }}/delete" method="post" data-confirm="Delete?" style="display:inline"><button class="btn btn-danger btn-sm">Delete</button></form>
</div>
</div>
<div class="detail-meta mt-2">
{% if item.company %}<span class="row-tag">{{ item.company }}</span>{% endif %}
{% if item.role %}<span class="detail-meta-item">{{ item.role }}</span>{% endif %}
</div>
</div>
<div class="card">
<div class="form-grid" style="gap:12px">
{% if item.email %}<div class="form-group"><div class="form-label">Email</div><a href="mailto:{{ item.email }}">{{ item.email }}</a></div>{% endif %}
{% if item.phone %}<div class="form-group"><div class="form-label">Phone</div><a href="tel:{{ item.phone }}">{{ item.phone }}</a></div>{% endif %}
{% if item.notes %}<div class="form-group full-width"><div class="form-label">Notes</div><div class="detail-body" style="white-space:pre-wrap">{{ item.notes }}</div></div>{% endif %}
{% if item.tags %}<div class="form-group full-width"><div class="form-label">Tags</div><div class="flex gap-2">{% for tag in item.tags %}<span class="row-tag">{{ tag }}</span>{% endfor %}</div></div>{% endif %}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb"><a href="/contacts">Contacts</a><span class="sep">/</span><span>{{ 'Edit' if item else 'New Contact' }}</span></div>
<div class="page-header"><h1 class="page-title">{{ 'Edit Contact' if item else 'New Contact' }}</h1></div>
<div class="card">
<form method="post" action="{{ '/contacts/' ~ item.id ~ '/edit' if item else '/contacts/create' }}">
<div class="form-grid">
<div class="form-group"><label class="form-label">First Name *</label><input type="text" name="first_name" class="form-input" required value="{{ item.first_name if item else '' }}"></div>
<div class="form-group"><label class="form-label">Last Name</label><input type="text" name="last_name" class="form-input" value="{{ item.last_name if item and item.last_name else '' }}"></div>
<div class="form-group"><label class="form-label">Company</label><input type="text" name="company" class="form-input" value="{{ item.company if item and item.company else '' }}"></div>
<div class="form-group"><label class="form-label">Role</label><input type="text" name="role" class="form-input" value="{{ item.role if item and item.role else '' }}"></div>
<div class="form-group"><label class="form-label">Email</label><input type="email" name="email" class="form-input" value="{{ item.email if item and item.email else '' }}"></div>
<div class="form-group"><label class="form-label">Phone</label><input type="tel" name="phone" class="form-input" value="{{ item.phone if item and item.phone else '' }}"></div>
<div class="form-group full-width"><label class="form-label">Notes</label><textarea name="notes" class="form-textarea" rows="4">{{ item.notes if item and item.notes else '' }}</textarea></div>
<div class="form-group full-width"><label class="form-label">Tags</label><input type="text" name="tags" class="form-input" value="{{ item.tags|join(', ') if item and item.tags else '' }}"></div>
<div class="form-actions"><button type="submit" class="btn btn-primary">{{ 'Save' if item else 'Create' }}</button><a href="/contacts" class="btn btn-secondary">Cancel</a></div>
</div>
</form></div>
{% endblock %}

25
templates/contacts.html Normal file
View File

@@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Contacts<span class="page-count">{{ items|length }}</span></h1>
<a href="/contacts/create" class="btn btn-primary">+ New Contact</a>
</div>
{% if items %}
<div class="card">
{% for item in items %}
<div class="list-row">
<span class="row-title"><a href="/contacts/{{ item.id }}">{{ item.first_name }} {{ item.last_name or '' }}</a></span>
{% if item.company %}<span class="row-tag">{{ item.company }}</span>{% endif %}
{% if item.role %}<span class="row-meta">{{ item.role }}</span>{% endif %}
{% if item.email %}<span class="row-meta">{{ item.email }}</span>{% endif %}
<div class="row-actions">
<a href="/contacts/{{ item.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form action="/contacts/{{ item.id }}/delete" method="post" data-confirm="Delete?" style="display:inline"><button class="btn btn-ghost btn-xs" style="color:var(--red)">Del</button></form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state"><div class="empty-state-icon">&#128100;</div><div class="empty-state-text">No contacts yet</div><a href="/contacts/create" class="btn btn-primary">Add Contact</a></div>
{% endif %}
{% endblock %}

140
templates/dashboard.html Normal file
View File

@@ -0,0 +1,140 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Dashboard</h1>
</div>
<!-- Stats -->
<div class="dashboard-grid mb-4">
<a href="/tasks/?status=open" class="stat-card-link">
<div class="stat-card">
<div class="stat-value">{{ stats.open_tasks or 0 }}</div>
<div class="stat-label">Open Tasks</div>
</div>
</a>
<a href="/tasks/?status=in_progress" class="stat-card-link">
<div class="stat-card">
<div class="stat-value">{{ stats.in_progress or 0 }}</div>
<div class="stat-label">In Progress</div>
</div>
</a>
<a href="/tasks/?status=done" class="stat-card-link">
<div class="stat-card">
<div class="stat-value">{{ stats.done_this_week or 0 }}</div>
<div class="stat-label">Done This Week</div>
</div>
</a>
<a href="/focus/" class="stat-card-link">
<div class="stat-card">
<div class="stat-value">{{ focus_items|length }}</div>
<div class="stat-label">Today's Focus</div>
</div>
</a>
</div>
<div class="dashboard-grid">
<!-- Today's Focus -->
<div class="card">
<div class="card-header">
<h2 class="card-title">Today's Focus</h2>
<a href="/focus" class="btn btn-ghost btn-sm">View All</a>
</div>
{% if focus_items %}
{% for item in focus_items %}
<div class="focus-item {{ 'completed' if item.completed }}">
<span class="priority-dot priority-{{ item.priority }}"></span>
<a href="/tasks/{{ item.task_id }}" class="focus-title">{{ item.title }}</a>
{% if item.project_name %}
<span class="row-meta">{{ item.project_name }}</span>
{% endif %}
</div>
{% endfor %}
{% else %}
<div class="empty-state">
<div class="empty-state-text">No focus items for today</div>
<a href="/focus" class="btn btn-primary btn-sm">Set Focus</a>
</div>
{% endif %}
</div>
<!-- Overdue + Upcoming -->
<div class="card">
<div class="card-header">
<h2 class="card-title">Upcoming</h2>
</div>
{% if overdue_tasks %}
<div class="text-xs text-muted mb-2" style="font-weight:600; color: var(--red);">OVERDUE</div>
{% for t in overdue_tasks %}
<div class="list-row">
<span class="priority-dot priority-{{ t.priority }}"></span>
<span class="row-title"><a href="/tasks/{{ t.id }}">{{ t.title }}</a></span>
<span class="row-meta overdue">{{ t.due_date }}</span>
</div>
{% endfor %}
{% endif %}
{% if upcoming_tasks %}
<div class="text-xs text-muted mb-2 {{ 'mt-3' if overdue_tasks }}" style="font-weight:600;">NEXT 7 DAYS</div>
{% for t in upcoming_tasks %}
<div class="list-row">
<span class="priority-dot priority-{{ t.priority }}"></span>
<span class="row-title"><a href="/tasks/{{ t.id }}">{{ t.title }}</a></span>
<span class="row-meta">{{ t.due_date }}</span>
</div>
{% endfor %}
{% endif %}
{% if not overdue_tasks and not upcoming_tasks %}
<div class="empty-state">
<div class="empty-state-text">No upcoming deadlines</div>
</div>
{% endif %}
</div>
</div>
<!-- Project Deadlines -->
{% if overdue_projects or upcoming_projects %}
<div class="card mt-4">
<div class="card-header">
<h2 class="card-title">Project Deadlines</h2>
<a href="/projects/" class="btn btn-ghost btn-sm">All Projects</a>
</div>
{% if overdue_projects %}
<div class="text-xs text-muted mb-2" style="font-weight:600; color: var(--red);">OVERDUE</div>
{% for p in overdue_projects %}
<div class="list-row">
<span class="priority-dot priority-{{ p.priority }}"></span>
<span class="row-title"><a href="/projects/{{ p.id }}">{{ p.name }}</a></span>
{% if p.domain_name %}
<span class="row-meta">{{ p.domain_name }}</span>
{% endif %}
<div class="project-progress-mini">
<div class="project-progress-bar" style="width: {{ ((p.done_count / p.task_count * 100) if p.task_count else 0)|int }}%"></div>
</div>
<span class="row-meta" style="min-width: 32px; text-align: right; font-size: 0.72rem;">{{ p.done_count }}/{{ p.task_count }}</span>
<span class="row-meta overdue">{{ p.target_date }}</span>
</div>
{% endfor %}
{% endif %}
{% if upcoming_projects %}
<div class="text-xs text-muted mb-2 {{ 'mt-3' if overdue_projects }}" style="font-weight:600;">NEXT 30 DAYS</div>
{% for p in upcoming_projects %}
<div class="list-row">
<span class="priority-dot priority-{{ p.priority }}"></span>
<span class="row-title"><a href="/projects/{{ p.id }}">{{ p.name }}</a></span>
{% if p.domain_name %}
<span class="row-meta">{{ p.domain_name }}</span>
{% endif %}
<div class="project-progress-mini">
<div class="project-progress-bar" style="width: {{ ((p.done_count / p.task_count * 100) if p.task_count else 0)|int }}%"></div>
</div>
<span class="row-meta" style="min-width: 32px; text-align: right; font-size: 0.72rem;">{{ p.done_count }}/{{ p.task_count }}</span>
<span class="row-meta">{{ p.target_date }}</span>
</div>
{% endfor %}
{% endif %}
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,55 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
<a href="/decisions">Decisions</a>
<span class="sep">/</span>
<span>{{ item.title }}</span>
</div>
<div class="detail-header">
<h1 class="detail-title">{{ item.title }}</h1>
<div class="flex gap-2">
<a href="/decisions/{{ item.id }}/edit" class="btn btn-secondary btn-sm">Edit</a>
<form action="/decisions/{{ item.id }}/delete" method="post" data-confirm="Delete this decision?" style="display:inline">
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
</form>
</div>
</div>
<div class="detail-meta mt-2">
<span class="status-badge status-{{ item.status }}">{{ item.status }}</span>
<span class="row-tag impact-{{ item.impact }}">{{ item.impact }} impact</span>
{% if item.decided_at %}<span class="detail-meta-item">Decided: {{ item.decided_at }}</span>{% endif %}
{% if meeting %}<span class="detail-meta-item">Meeting: <a href="/meetings/{{ meeting.id }}">{{ meeting.title }}</a></span>{% endif %}
{% if item.tags %}
<div class="mt-1">{% for tag in item.tags %}<span class="row-tag">{{ tag }}</span>{% endfor %}</div>
{% endif %}
</div>
{% if superseded_by %}
<div class="card mt-3" style="border-left: 3px solid var(--amber);">
<div style="padding: 12px 16px;">
<strong style="color: var(--amber);">Superseded by:</strong>
<a href="/decisions/{{ superseded_by.id }}">{{ superseded_by.title }}</a>
</div>
</div>
{% endif %}
{% if item.rationale %}
<div class="card mt-3">
<div class="card-header"><h3 class="card-title">Rationale</h3></div>
<div class="detail-body" style="padding: 12px 16px;">{{ item.rationale }}</div>
</div>
{% endif %}
{% if supersedes %}
<div class="card mt-3">
<div class="card-header"><h3 class="card-title">Supersedes</h3></div>
{% for dec in supersedes %}
<div class="list-row">
<span class="row-title"><a href="/decisions/{{ dec.id }}">{{ dec.title }}</a></span>
</div>
{% endfor %}
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,87 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">{{ page_title }}</h1>
</div>
<div class="card">
<form method="post" action="{{ '/decisions/' ~ item.id ~ '/edit' if item else '/decisions/create' }}">
<div class="form-grid">
<div class="form-group full-width">
<label class="form-label">Title *</label>
<input type="text" name="title" class="form-input" required
value="{{ item.title if item else '' }}" placeholder="Summary of the decision...">
</div>
<div class="form-group">
<label class="form-label">Status</label>
<select name="status" class="form-select">
<option value="proposed" {{ 'selected' if (item and item.status == 'proposed') or not item }}>Proposed</option>
<option value="accepted" {{ 'selected' if item and item.status == 'accepted' }}>Accepted</option>
<option value="rejected" {{ 'selected' if item and item.status == 'rejected' }}>Rejected</option>
<option value="superseded" {{ 'selected' if item and item.status == 'superseded' }}>Superseded</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Impact</label>
<select name="impact" class="form-select">
<option value="low" {{ 'selected' if item and item.impact == 'low' }}>Low</option>
<option value="medium" {{ 'selected' if (item and item.impact == 'medium') or not item }}>Medium</option>
<option value="high" {{ 'selected' if item and item.impact == 'high' }}>High</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Decision Date</label>
<input type="date" name="decided_at" class="form-input"
value="{{ item.decided_at if item and item.decided_at else '' }}">
</div>
<div class="form-group">
<label class="form-label">Meeting</label>
<select name="meeting_id" class="form-select">
<option value="">None</option>
{% for m in meetings %}
<option value="{{ m.id }}"
{{ 'selected' if (item and item.meeting_id and item.meeting_id|string == m.id|string) or (not item and prefill_meeting_id == m.id|string) }}>
{{ m.title }} ({{ m.meeting_date }})
</option>
{% endfor %}
</select>
</div>
{% if item and other_decisions is defined %}
<div class="form-group">
<label class="form-label">Superseded By</label>
<select name="superseded_by_id" class="form-select">
<option value="">None</option>
{% for d in other_decisions %}
<option value="{{ d.id }}" {{ 'selected' if item.superseded_by_id and item.superseded_by_id|string == d.id|string }}>
{{ d.title }}
</option>
{% endfor %}
</select>
</div>
{% endif %}
<div class="form-group full-width">
<label class="form-label">Rationale</label>
<textarea name="rationale" class="form-textarea" rows="6" placeholder="Why was this decided? What alternatives were considered?">{{ item.rationale if item and item.rationale else '' }}</textarea>
</div>
<div class="form-group">
<label class="form-label">Tags</label>
<input type="text" name="tags" class="form-input" placeholder="tag1, tag2, ..."
value="{{ item.tags|join(', ') if item and item.tags else '' }}">
</div>
</div>
{% if prefill_task_id is defined and prefill_task_id %}<input type="hidden" name="task_id" value="{{ prefill_task_id }}">{% endif %}
<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save Changes' if item else 'Record Decision' }}</button>
<a href="{{ '/tasks/' ~ prefill_task_id ~ '?tab=decisions' if prefill_task_id is defined and prefill_task_id else ('/decisions/' ~ item.id if item else '/decisions') }}" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
{% endblock %}

53
templates/decisions.html Normal file
View File

@@ -0,0 +1,53 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Decisions<span class="page-count">{{ items|length }}</span></h1>
<a href="/decisions/create" class="btn btn-primary">+ New Decision</a>
</div>
<form class="filters-bar" method="get" action="/decisions">
<select name="status" class="filter-select" onchange="this.form.submit()">
<option value="">All Statuses</option>
<option value="proposed" {{ 'selected' if current_status == 'proposed' }}>Proposed</option>
<option value="accepted" {{ 'selected' if current_status == 'accepted' }}>Accepted</option>
<option value="rejected" {{ 'selected' if current_status == 'rejected' }}>Rejected</option>
<option value="superseded" {{ 'selected' if current_status == 'superseded' }}>Superseded</option>
</select>
<select name="impact" class="filter-select" onchange="this.form.submit()">
<option value="">All Impact</option>
<option value="high" {{ 'selected' if current_impact == 'high' }}>High</option>
<option value="medium" {{ 'selected' if current_impact == 'medium' }}>Medium</option>
<option value="low" {{ 'selected' if current_impact == 'low' }}>Low</option>
</select>
</form>
{% if items %}
<div class="card mt-3">
{% for item in items %}
<div class="list-row">
<span class="row-title"><a href="/decisions/{{ item.id }}">{{ item.title }}</a></span>
<span class="status-badge status-{{ item.status }}">{{ item.status }}</span>
<span class="row-tag impact-{{ item.impact }}">{{ item.impact }}</span>
{% if item.decided_at %}
<span class="row-meta">{{ item.decided_at }}</span>
{% endif %}
{% if item.meeting_title %}
<span class="row-meta">{{ item.meeting_title }}</span>
{% endif %}
<div class="row-actions">
<a href="/decisions/{{ item.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form action="/decisions/{{ item.id }}/delete" method="post" data-confirm="Delete this decision?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Del</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state mt-3">
<div class="empty-state-icon">&#9878;</div>
<div class="empty-state-text">No decisions recorded yet</div>
<a href="/decisions/create" class="btn btn-primary">Record First Decision</a>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb"><a href="/domains">Domains</a><span class="sep">/</span><span>{{ 'Edit' if item else 'New Domain' }}</span></div>
<div class="page-header"><h1 class="page-title">{{ 'Edit Domain' if item else 'New Domain' }}</h1></div>
<div class="card">
<form method="post" action="{{ '/domains/' ~ item.id ~ '/edit' if item else '/domains/create' }}">
<div class="form-grid">
<div class="form-group"><label class="form-label">Name *</label><input type="text" name="name" class="form-input" required value="{{ item.name if item else '' }}"></div>
<div class="form-group"><label class="form-label">Color</label><input type="color" name="color" class="form-input" value="{{ item.color if item and item.color else '#4F6EF7' }}" style="height:42px;padding:4px"></div>
<div class="form-group full-width"><label class="form-label">Description</label><textarea name="description" class="form-textarea" rows="3">{{ item.description if item and item.description else '' }}</textarea></div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save' if item else 'Create' }}</button>
<a href="/domains" class="btn btn-secondary">Cancel</a>
</div>
</div>
</form></div>
{% endblock %}

26
templates/domains.html Normal file
View File

@@ -0,0 +1,26 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Domains<span class="page-count">{{ items|length }}</span></h1>
<a href="/domains/create" class="btn btn-primary">+ New Domain</a>
</div>
{% if items %}
<div class="card">
{% for item in items %}
<div class="list-row">
<span class="domain-dot" style="background: {{ item.color or '#4F6EF7' }}"></span>
<span class="row-title"><a href="/tasks?domain_id={{ item.id }}">{{ item.name }}</a></span>
{% if item.description %}<span class="row-meta">{{ item.description }}</span>{% endif %}
<div class="row-actions">
<a href="/domains/{{ item.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form action="/domains/{{ item.id }}/delete" method="post" data-confirm="Delete this domain and all its contents?" style="display:inline">
<button class="btn btn-ghost btn-xs" style="color:var(--red)">Del</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No domains. Create your first life domain.</div><a href="/domains/create" class="btn btn-primary">Create Domain</a></div>
{% endif %}
{% endblock %}

184
templates/eisenhower.html Normal file
View File

@@ -0,0 +1,184 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1>Eisenhower Matrix</h1>
<span class="text-muted">{{ total }} open tasks classified by priority &amp; urgency</span>
</div>
<!-- Filters -->
<form class="filters-bar" method="get" action="/eisenhower" id="eis-filters">
<select name="domain_id" class="filter-select" id="eis-domain" onchange="this.form.submit()">
<option value="">All Domains</option>
{% for d in domains %}
<option value="{{ d.id }}" {{ 'selected' if current_domain_id == d.id|string }}>{{ d.name }}</option>
{% endfor %}
</select>
<select name="project_id" class="filter-select" id="eis-project" onchange="this.form.submit()">
<option value="">All Projects</option>
{% for p in projects %}
<option value="{{ p.id }}" {{ 'selected' if current_project_id == p.id|string }}>{{ p.name }}</option>
{% endfor %}
</select>
<select name="status" class="filter-select" onchange="this.form.submit()">
<option value="">All Statuses</option>
<option value="open" {{ 'selected' if current_status == 'open' }}>Open</option>
<option value="in_progress" {{ 'selected' if current_status == 'in_progress' }}>In Progress</option>
<option value="blocked" {{ 'selected' if current_status == 'blocked' }}>Blocked</option>
</select>
<select name="context" class="filter-select" onchange="this.form.submit()">
<option value="">All Contexts</option>
{% for ct in context_types %}
<option value="{{ ct.value }}" {{ 'selected' if current_context == ct.value }}>{{ ct.label }}</option>
{% endfor %}
</select>
</form>
<div class="eisenhower-grid">
<!-- Axis labels -->
<div class="eisenhower-y-label">
<span>Important</span>
</div>
<!-- Q1: Urgent + Important -->
<div class="eisenhower-quadrant eisenhower-q1">
<div class="eisenhower-quadrant-header">
<h3>Do First</h3>
<span class="eisenhower-quadrant-subtitle">Urgent &amp; Important</span>
<span class="badge badge-red">{{ counts.do_first }}</span>
</div>
<div class="eisenhower-task-list">
{% for task in quadrants.do_first %}
<a href="/tasks/{{ task.id }}" class="eisenhower-task">
<span class="priority-dot priority-{{ task.priority }}"></span>
<span class="eisenhower-task-title">{{ task.title }}</span>
{% if task.due_date %}
<span class="eisenhower-task-due {% if task.due_date < today %}overdue{% endif %}">
{{ task.due_date.strftime('%b %-d') }}
</span>
{% endif %}
{% if task.project_name %}
<span class="eisenhower-task-project">{{ task.project_name }}</span>
{% endif %}
</a>
{% else %}
<div class="eisenhower-empty">No tasks</div>
{% endfor %}
</div>
</div>
<!-- Q2: Not Urgent + Important -->
<div class="eisenhower-quadrant eisenhower-q2">
<div class="eisenhower-quadrant-header">
<h3>Schedule</h3>
<span class="eisenhower-quadrant-subtitle">Not Urgent &amp; Important</span>
<span class="badge badge-accent">{{ counts.schedule }}</span>
</div>
<div class="eisenhower-task-list">
{% for task in quadrants.schedule %}
<a href="/tasks/{{ task.id }}" class="eisenhower-task">
<span class="priority-dot priority-{{ task.priority }}"></span>
<span class="eisenhower-task-title">{{ task.title }}</span>
{% if task.due_date %}
<span class="eisenhower-task-due">{{ task.due_date.strftime('%b %-d') }}</span>
{% endif %}
{% if task.project_name %}
<span class="eisenhower-task-project">{{ task.project_name }}</span>
{% endif %}
</a>
{% else %}
<div class="eisenhower-empty">No tasks</div>
{% endfor %}
</div>
</div>
<!-- Second row y-label -->
<div class="eisenhower-y-label">
<span>Not Important</span>
</div>
<!-- Q3: Urgent + Not Important -->
<div class="eisenhower-quadrant eisenhower-q3">
<div class="eisenhower-quadrant-header">
<h3>Delegate</h3>
<span class="eisenhower-quadrant-subtitle">Urgent &amp; Not Important</span>
<span class="badge badge-amber">{{ counts.delegate }}</span>
</div>
<div class="eisenhower-task-list">
{% for task in quadrants.delegate %}
<a href="/tasks/{{ task.id }}" class="eisenhower-task">
<span class="priority-dot priority-{{ task.priority }}"></span>
<span class="eisenhower-task-title">{{ task.title }}</span>
{% if task.due_date %}
<span class="eisenhower-task-due {% if task.due_date < today %}overdue{% endif %}">
{{ task.due_date.strftime('%b %-d') }}
</span>
{% endif %}
{% if task.project_name %}
<span class="eisenhower-task-project">{{ task.project_name }}</span>
{% endif %}
</a>
{% else %}
<div class="eisenhower-empty">No tasks</div>
{% endfor %}
</div>
</div>
<!-- Q4: Not Urgent + Not Important -->
<div class="eisenhower-quadrant eisenhower-q4">
<div class="eisenhower-quadrant-header">
<h3>Eliminate</h3>
<span class="eisenhower-quadrant-subtitle">Not Urgent &amp; Not Important</span>
<span class="badge badge-muted">{{ counts.eliminate }}</span>
</div>
<div class="eisenhower-task-list">
{% for task in quadrants.eliminate %}
<a href="/tasks/{{ task.id }}" class="eisenhower-task">
<span class="priority-dot priority-{{ task.priority }}"></span>
<span class="eisenhower-task-title">{{ task.title }}</span>
{% if task.due_date %}
<span class="eisenhower-task-due">{{ task.due_date.strftime('%b %-d') }}</span>
{% endif %}
{% if task.project_name %}
<span class="eisenhower-task-project">{{ task.project_name }}</span>
{% endif %}
</a>
{% else %}
<div class="eisenhower-empty">No tasks</div>
{% endfor %}
</div>
</div>
<!-- X-axis labels -->
<div class="eisenhower-x-spacer"></div>
<div class="eisenhower-x-label">Urgent</div>
<div class="eisenhower-x-label">Not Urgent</div>
</div>
<script>
(function() {
var domainSel = document.getElementById('eis-domain');
var projectSel = document.getElementById('eis-project');
var currentProjectId = '{{ current_project_id }}';
var form = document.getElementById('eis-filters');
domainSel.addEventListener('change', function() {
var did = domainSel.value;
if (!did) { form.submit(); return; }
fetch('/projects/api/by-domain?domain_id=' + encodeURIComponent(did))
.then(function(r) { return r.json(); })
.then(function(projects) {
projectSel.innerHTML = '<option value="">All Projects</option>';
projects.forEach(function(p) {
var opt = document.createElement('option');
opt.value = p.id;
opt.textContent = p.name;
if (p.id === currentProjectId) opt.selected = true;
projectSel.appendChild(opt);
});
form.submit();
})
.catch(function() { form.submit(); });
});
})();
</script>
{% endblock %}

View File

@@ -0,0 +1,52 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
<a href="/files">Files</a>
<span class="sep">/</span>
{% if folder and folder != '/' %}
<a href="/files?folder={{ folder }}">{{ folder }}</a>
<span class="sep">/</span>
{% endif %}
<span>{{ item.original_filename }}</span>
</div>
<div class="detail-header">
<h1 class="detail-title">{{ item.original_filename }}</h1>
<div class="flex gap-2">
<a href="/files/{{ item.id }}/download" class="btn btn-primary btn-sm">Download</a>
<form action="/files/{{ item.id }}/delete" method="post" data-confirm="Delete this file?" style="display:inline">
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
</form>
</div>
</div>
<div class="detail-meta mt-2">
<span class="detail-meta-item">Folder: {{ folder }}</span>
{% if item.mime_type %}<span class="detail-meta-item">Type: {{ item.mime_type }}</span>{% endif %}
{% if item.size_bytes %}<span class="detail-meta-item">Size: {{ "%.1f"|format(item.size_bytes / 1024) }} KB</span>{% endif %}
{% if item.description %}<span class="detail-meta-item">{{ item.description }}</span>{% endif %}
{% if item.tags %}
<div class="mt-1">
{% for tag in item.tags %}<span class="row-tag">{{ tag }}</span>{% endfor %}
</div>
{% endif %}
</div>
{% if can_preview %}
<div class="card mt-3" style="padding: 16px;">
{% if item.mime_type and item.mime_type.startswith('image/') %}
<img src="/files/{{ item.id }}/serve" alt="{{ item.original_filename }}" style="max-width: 100%; height: auto; border-radius: var(--radius);">
{% elif item.mime_type == 'application/pdf' %}
<iframe src="/files/{{ item.id }}/serve" style="width: 100%; height: 600px; border: none; border-radius: var(--radius);"></iframe>
{% elif item.mime_type and item.mime_type.startswith('text/') %}
<iframe src="/files/{{ item.id }}/serve" style="width: 100%; height: 400px; border: 1px solid var(--border); border-radius: var(--radius); background: #fff;"></iframe>
{% endif %}
</div>
{% else %}
<div class="empty-state mt-3">
<div class="empty-state-icon">&#128196;</div>
<div class="empty-state-text">Preview not available for this file type</div>
<a href="/files/{{ item.id }}/download" class="btn btn-primary">Download Instead</a>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,52 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Upload File</h1>
</div>
<div class="card">
<form method="post" action="/files/upload" enctype="multipart/form-data">
{% if context_type %}
<input type="hidden" name="context_type" value="{{ context_type }}">
<input type="hidden" name="context_id" value="{{ context_id }}">
{% endif %}
<div class="form-grid">
<div class="form-group full-width">
<label class="form-label">File *</label>
<input type="file" name="file" class="form-input" required>
</div>
<div class="form-group">
<label class="form-label">Folder</label>
<select name="folder" class="form-input">
<option value="">/ (root)</option>
{% for f in folders %}
<option value="{{ f }}" {{ 'selected' if prefill_folder == f }}>{{ f }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">New Folder</label>
<input type="text" name="new_folder" class="form-input" placeholder="Or create new folder...">
</div>
<div class="form-group full-width">
<label class="form-label">Description</label>
<input type="text" name="description" class="form-input" placeholder="Optional description...">
</div>
<div class="form-group">
<label class="form-label">Tags</label>
<input type="text" name="tags" class="form-input" placeholder="tag1, tag2, ...">
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Upload</button>
<a href="/files" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
{% endblock %}

91
templates/files.html Normal file
View File

@@ -0,0 +1,91 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Files<span class="page-count">{{ items|length }}</span></h1>
<div class="flex gap-2">
<form action="/files/sync" method="post" style="display:inline">
<button type="submit" class="btn btn-secondary">Sync Files</button>
</form>
<a href="/files/upload{{ '?folder=' ~ current_folder if current_folder }}{{ '?context_type=' ~ context_type ~ '&context_id=' ~ context_id if context_type }}" class="btn btn-primary">+ Upload File</a>
</div>
</div>
{% if sync_result and (sync_result.added > 0 or sync_result.removed > 0) %}
<div class="flash-message" style="background: var(--accent-soft); border: 1px solid var(--accent); border-radius: var(--radius); padding: 8px 12px; margin-bottom: 16px; color: var(--text); font-size: 0.85rem;">
Synced: {{ sync_result.added }} file{{ 's' if sync_result.added != 1 }} added, {{ sync_result.removed }} removed
</div>
{% endif %}
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 16px;">
<label style="color: var(--muted); font-size: 0.85rem; white-space: nowrap;">Folder:</label>
<select class="form-input" style="max-width: 280px; padding: 6px 10px; font-size: 0.85rem;" onchange="window.location.href=this.value">
<option value="/files" {{ 'selected' if current_folder is none }}>All folders</option>
<option value="/files?folder=" {{ 'selected' if current_folder is not none and current_folder == '' }}>/ (root)</option>
{% for f in folders %}
<option value="/files?folder={{ f }}" {{ 'selected' if current_folder == f }}>{% if '/' in f %}&nbsp;&nbsp;{{ f.split('/')[-1] }}{% else %}{{ f }}{% endif %}</option>
{% endfor %}
</select>
</div>
{% set sort_base = '/files?' ~ ('folder=' ~ current_folder ~ '&' if current_folder is not none else '') %}
{% if items %}
<div class="card" style="overflow-x: auto;">
<table class="data-table" style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="border-bottom: 1px solid var(--border); text-align: left;">
<th style="padding: 10px 12px; font-weight: 600; font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em;">
<a href="{{ sort_base }}sort={{ 'path_desc' if current_sort == 'path' else 'path' }}" style="color: var(--muted); text-decoration: none;">
Path {{ '▲' if current_sort == 'path' else ('▼' if current_sort == 'path_desc' else '') }}
</a>
</th>
<th style="padding: 10px 12px; font-weight: 600; font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em;">
<a href="{{ sort_base }}sort={{ 'name_desc' if current_sort == 'name' else 'name' }}" style="color: var(--muted); text-decoration: none;">
Name {{ '▲' if current_sort == 'name' else ('▼' if current_sort == 'name_desc' else '') }}
</a>
</th>
<th style="padding: 10px 12px; font-weight: 600; font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em;">Type</th>
<th style="padding: 10px 12px; font-weight: 600; font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em;">Size</th>
<th style="padding: 10px 12px; font-weight: 600; font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em;">
<a href="{{ sort_base }}sort={{ 'date_asc' if current_sort == 'date' else 'date' }}" style="color: var(--muted); text-decoration: none;">
Date {{ '▼' if current_sort == 'date' else ('▲' if current_sort == 'date_asc' else '') }}
</a>
</th>
<th style="padding: 10px 12px;"></th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr style="border-bottom: 1px solid var(--border);">
<td style="padding: 10px 12px; color: var(--muted); font-size: 0.85rem;">{{ item.folder }}</td>
<td style="padding: 10px 12px;">
<a href="/files/{{ item.id }}/preview" style="color: var(--accent);">{{ item.original_filename }}</a>
</td>
<td style="padding: 10px 12px;">
{% if item.mime_type %}<span class="row-tag">{{ item.mime_type.split('/')|last }}</span>{% endif %}
</td>
<td style="padding: 10px 12px; color: var(--muted); font-size: 0.85rem; white-space: nowrap;">
{% if item.size_bytes %}{{ "%.1f"|format(item.size_bytes / 1024) }} KB{% endif %}
</td>
<td style="padding: 10px 12px; color: var(--muted); font-size: 0.85rem; white-space: nowrap;">
{{ item.created_at.strftime('%Y-%m-%d') if item.created_at else '' }}
</td>
<td style="padding: 10px 12px; text-align: right; white-space: nowrap;">
<a href="/files/{{ item.id }}/download" class="btn btn-ghost btn-xs">Download</a>
<form action="/files/{{ item.id }}/delete" method="post" data-confirm="Delete this file?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Del</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="empty-state">
<div class="empty-state-icon">&#128193;</div>
<div class="empty-state-text">No files{{ ' in this folder' if current_folder is not none else ' uploaded yet' }}</div>
<a href="/files/upload" class="btn btn-primary">Upload First File</a>
</div>
{% endif %}
{% endblock %}

109
templates/focus.html Normal file
View File

@@ -0,0 +1,109 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Daily Focus</h1>
<div class="flex items-center gap-2">
{% if total_estimated %}<span class="text-sm text-muted">~{{ total_estimated }}min estimated</span>{% endif %}
</div>
</div>
<div class="flex items-center gap-2 mb-4">
<a href="/focus?focus_date={{ (focus_date|string)[:10] }}" class="btn btn-ghost btn-sm">Today</a>
<span class="text-sm" style="font-weight:600">{{ focus_date }}</span>
</div>
<!-- Focus items -->
{% if items %}
{% for item in items %}
<div class="focus-item {{ 'completed' if item.completed }}">
<form action="/focus/{{ item.id }}/toggle" method="post" style="display:inline">
<div class="row-check">
<input type="checkbox" id="f-{{ item.id }}" {{ 'checked' if item.completed }} onchange="this.form.submit()">
<label for="f-{{ item.id }}"></label>
</div>
</form>
<span class="priority-dot priority-{{ item.priority }}"></span>
<a href="/tasks/{{ item.task_id }}" class="focus-title">{{ item.title }}</a>
{% if item.project_name %}<span class="row-tag">{{ item.project_name }}</span>{% endif %}
{% if item.estimated_minutes %}<span class="focus-meta">~{{ item.estimated_minutes }}min</span>{% endif %}
{% if item.due_date %}<span class="focus-meta">{{ item.due_date }}</span>{% endif %}
<form action="/focus/{{ item.id }}/remove" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs" style="color:var(--red)" title="Remove from focus">&times;</button>
</form>
</div>
{% endfor %}
{% else %}
<div class="empty-state mb-4"><div class="empty-state-text">No focus items for this day</div></div>
{% endif %}
<!-- Add task to focus -->
<div class="card mt-4">
<div class="card-header"><h2 class="card-title">Add to Focus</h2></div>
<form class="filters-bar" method="get" action="/focus" id="focus-filters" style="padding: 8px 12px; border-bottom: 1px solid var(--border);">
<input type="hidden" name="focus_date" value="{{ focus_date }}">
<select name="domain_id" class="filter-select" id="focus-domain" onchange="this.form.submit()">
<option value="">All Domains</option>
{% for d in domains %}
<option value="{{ d.id }}" {{ 'selected' if current_domain_id == d.id|string }}>{{ d.name }}</option>
{% endfor %}
</select>
<select name="area_id" class="filter-select" onchange="this.form.submit()">
<option value="">All Areas</option>
{% for a in areas %}
<option value="{{ a.id }}" {{ 'selected' if current_area_id == a.id|string }}>{{ a.name }}</option>
{% endfor %}
</select>
<select name="project_id" class="filter-select" id="focus-project" onchange="this.form.submit()">
<option value="">All Projects</option>
{% for p in projects %}
<option value="{{ p.id }}" {{ 'selected' if current_project_id == p.id|string }}>{{ p.name }}</option>
{% endfor %}
</select>
</form>
{% for t in available_tasks[:15] %}
<div class="list-row">
<span class="priority-dot priority-{{ t.priority }}"></span>
<span class="row-title">{{ t.title }}</span>
{% if t.project_name %}<span class="row-tag">{{ t.project_name }}</span>{% endif %}
{% if t.due_date %}<span class="row-meta">{{ t.due_date }}</span>{% endif %}
<form action="/focus/add" method="post" style="display:inline">
<input type="hidden" name="task_id" value="{{ t.id }}">
<input type="hidden" name="focus_date" value="{{ focus_date }}">
<button class="btn btn-ghost btn-xs" style="color:var(--green)">+ Focus</button>
</form>
</div>
{% else %}
<div style="padding: 16px; color: var(--muted); font-size: 0.85rem;">No available tasks matching filters</div>
{% endfor %}
</div>
<script>
(function() {
var domainSel = document.getElementById('focus-domain');
var projectSel = document.getElementById('focus-project');
var currentProjectId = '{{ current_project_id }}';
var form = document.getElementById('focus-filters');
domainSel.addEventListener('change', function() {
var did = domainSel.value;
if (!did) { form.submit(); return; }
fetch('/projects/api/by-domain?domain_id=' + encodeURIComponent(did))
.then(function(r) { return r.json(); })
.then(function(projects) {
projectSel.innerHTML = '<option value="">All Projects</option>';
projects.forEach(function(p) {
var opt = document.createElement('option');
opt.value = p.id;
opt.textContent = p.name;
if (p.id === currentProjectId) opt.selected = true;
projectSel.appendChild(opt);
});
form.submit();
})
.catch(function() { form.submit(); });
});
})();
</script>
{% endblock %}

32
templates/history.html Normal file
View File

@@ -0,0 +1,32 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Change History<span class="page-count">{{ items|length }}</span></h1>
</div>
<form class="filters-bar" method="get" action="/history">
<select name="entity_type" class="filter-select" onchange="this.form.submit()">
<option value="">All Types</option>
{% for t in type_options %}
<option value="{{ t.value }}" {{ 'selected' if current_type == t.value }}>{{ t.label }}</option>
{% endfor %}
</select>
</form>
{% if items %}
<div class="card mt-3">
{% for item in items %}
<div class="list-row">
<span class="row-tag" style="min-width: 70px; text-align: center;">{{ item.type_label }}</span>
<span class="row-title"><a href="{{ item.url }}">{{ item.label }}</a></span>
<span class="status-badge {{ 'status-active' if item.action == 'created' else 'status-open' }}">{{ item.action }}</span>
<span class="row-meta">{{ item.updated_at.strftime('%Y-%m-%d %H:%M') if item.updated_at else '' }}</span>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state mt-3">
<div class="empty-state-text">No recent changes</div>
</div>
{% endif %}
{% endblock %}

21
templates/link_form.html Normal file
View File

@@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb"><a href="/links">Links</a><span class="sep">/</span><span>{{ 'Edit' if item else 'New Link' }}</span></div>
<div class="page-header"><h1 class="page-title">{{ 'Edit Link' if item else 'New Link' }}</h1></div>
<div class="card">
<form method="post" action="{{ '/links/' ~ item.id ~ '/edit' if item else '/links/create' }}">
<div class="form-grid">
<div class="form-group"><label class="form-label">Label *</label><input type="text" name="label" class="form-input" required value="{{ item.label if item else '' }}"></div>
<div class="form-group"><label class="form-label">URL *</label><input type="url" name="url" class="form-input" required value="{{ item.url if item else '' }}"></div>
<div class="form-group"><label class="form-label">Domain</label>
<select name="domain_id" class="form-select"><option value="">-- None --</option>{% for d in domains %}<option value="{{ d.id }}" {{ 'selected' if (item and item.domain_id and item.domain_id|string == d.id|string) or (not item and prefill_domain_id == d.id|string) }}>{{ d.name }}</option>{% endfor %}</select></div>
<div class="form-group"><label class="form-label">Project</label>
<select name="project_id" class="form-select"><option value="">-- None --</option>{% for p in projects %}<option value="{{ p.id }}" {{ 'selected' if (item and item.project_id and item.project_id|string == p.id|string) or (not item and prefill_project_id == p.id|string) }}>{{ p.name }}</option>{% endfor %}</select></div>
<div class="form-group full-width"><label class="form-label">Tags</label><input type="text" name="tags" class="form-input" placeholder="tag1, tag2, ..." value="{{ item.tags|join(', ') if item and item.tags else '' }}"></div>
<div class="form-group full-width"><label class="form-label">Description</label><textarea name="description" class="form-textarea" rows="3">{{ item.description if item and item.description else '' }}</textarea></div>
{% if prefill_task_id is defined and prefill_task_id %}<input type="hidden" name="task_id" value="{{ prefill_task_id }}">{% endif %}
{% if prefill_meeting_id is defined and prefill_meeting_id %}<input type="hidden" name="meeting_id" value="{{ prefill_meeting_id }}">{% endif %}
<div class="form-actions"><button type="submit" class="btn btn-primary">{{ 'Save' if item else 'Create' }}</button><a href="/links" class="btn btn-secondary">Cancel</a></div>
</div>
</form></div>
{% endblock %}

25
templates/links.html Normal file
View File

@@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Links<span class="page-count">{{ items|length }}</span></h1>
<a href="/links/create" class="btn btn-primary">+ New Link</a>
</div>
{% if items %}
<div class="card">
{% for item in items %}
<div class="list-row">
{% if item.domain_color %}<span class="row-domain-tag" style="background:{{ item.domain_color }}22;color:{{ item.domain_color }}">{{ item.domain_name }}</span>{% endif %}
<span class="row-title"><a href="{{ item.url }}" target="_blank">{{ item.label }}</a></span>
{% if item.project_name %}<span class="row-tag">{{ item.project_name }}</span>{% endif %}
<span class="row-meta">{{ item.url[:50] }}{% if item.url|length > 50 %}...{% endif %}</span>
<div class="row-actions">
<a href="/links/{{ item.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form action="/links/{{ item.id }}/delete" method="post" data-confirm="Delete?" style="display:inline"><button class="btn btn-ghost btn-xs" style="color:var(--red)">Del</button></form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state"><div class="empty-state-icon">&#128279;</div><div class="empty-state-text">No links yet</div><a href="/links/create" class="btn btn-primary">Add Link</a></div>
{% endif %}
{% endblock %}

132
templates/list_detail.html Normal file
View File

@@ -0,0 +1,132 @@
{% extends "base.html" %}
{% block content %}
<!-- Breadcrumb -->
<div class="breadcrumb">
<a href="/lists">Lists</a>
<span class="sep">/</span>
{% if domain %}<span style="color: {{ domain.color or 'var(--accent)' }}">{{ domain.name }}</span><span class="sep">/</span>{% endif %}
{% if project %}<a href="/projects/{{ project.id }}">{{ project.name }}</a><span class="sep">/</span>{% endif %}
<span>{{ item.name }}</span>
</div>
<div class="detail-header">
<h1 class="detail-title">{{ item.name }}</h1>
<div class="flex gap-2">
<a href="/lists/{{ item.id }}/edit" class="btn btn-secondary btn-sm">Edit</a>
<form action="/lists/{{ item.id }}/delete" method="post" data-confirm="Delete this list?" style="display:inline">
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
</form>
</div>
</div>
<div class="detail-meta mt-2">
<span class="detail-meta-item">
<span class="row-tag">{{ item.list_type }}</span>
</span>
{% if item.description %}
<p class="text-secondary mt-1">{{ item.description }}</p>
{% endif %}
{% if item.tags %}
<div class="mt-1">
{% for tag in item.tags %}
<span class="row-tag">{{ tag }}</span>
{% endfor %}
</div>
{% endif %}
</div>
<!-- Add item form -->
<form class="quick-add mt-3" action="/lists/{{ item.id }}/items/add" method="post">
<input type="text" name="content" placeholder="Add item..." required>
<button type="submit" class="btn btn-primary btn-sm">Add</button>
</form>
<!-- List items -->
{% if list_items %}
<div class="card mt-2">
{% for li in list_items %}
<div class="list-row {{ 'completed' if li.completed }}">
{% if item.list_type == 'checklist' %}
<div class="row-check">
<form action="/lists/{{ item.id }}/items/{{ li.id }}/toggle" method="post" style="display:inline">
<input type="checkbox" id="li-{{ li.id }}" {{ 'checked' if li.completed }}
onchange="this.form.submit()">
<label for="li-{{ li.id }}"></label>
</form>
</div>
{% endif %}
<span class="row-title" style="{{ 'text-decoration: line-through; opacity: 0.6;' if li.completed }}">
{{ li.content }}
</span>
<div class="row-actions">
<form action="/lists/{{ item.id }}/items/{{ li.id }}/delete" method="post" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Del</button>
</form>
</div>
</div>
<!-- Child items -->
{% for child in child_map.get(li.id|string, []) %}
<div class="list-row {{ 'completed' if child.completed }}" style="padding-left: 48px;">
{% if item.list_type == 'checklist' %}
<div class="row-check">
<form action="/lists/{{ item.id }}/items/{{ child.id }}/toggle" method="post" style="display:inline">
<input type="checkbox" id="li-{{ child.id }}" {{ 'checked' if child.completed }}
onchange="this.form.submit()">
<label for="li-{{ child.id }}"></label>
</form>
</div>
{% endif %}
<span class="row-title" style="{{ 'text-decoration: line-through; opacity: 0.6;' if child.completed }}">
{{ child.content }}
</span>
<div class="row-actions">
<form action="/lists/{{ item.id }}/items/{{ child.id }}/delete" method="post" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Del</button>
</form>
</div>
</div>
{% endfor %}
{% endfor %}
</div>
{% else %}
<div class="empty-state mt-3">
<div class="empty-state-icon">&#9744;</div>
<div class="empty-state-text">No items yet. Add one above.</div>
</div>
{% endif %}
<!-- Contacts -->
<div class="card mt-4">
<div class="card-header">
<h3 class="card-title">Contacts<span class="page-count">{{ contacts|length }}</span></h3>
</div>
<form action="/lists/{{ item.id }}/contacts/add" method="post" class="flex gap-2 items-end" style="padding: 12px; border-bottom: 1px solid var(--border);">
<div class="form-group" style="flex:1; margin:0;">
<select name="contact_id" class="form-select" required>
<option value="">Select contact...</option>
{% for c in all_contacts %}
<option value="{{ c.id }}">{{ c.first_name }} {{ c.last_name or '' }}</option>
{% endfor %}
</select>
</div>
<div class="form-group" style="flex:1; margin:0;">
<input type="text" name="role" class="form-input" placeholder="Role (optional)">
</div>
<button type="submit" class="btn btn-primary btn-sm">Add</button>
</form>
{% for c in contacts %}
<div class="list-row">
<span class="row-title"><a href="/contacts/{{ c.id }}">{{ c.first_name }} {{ c.last_name or '' }}</a></span>
{% if c.role %}<span class="row-tag">{{ c.role }}</span>{% endif %}
<div class="row-actions">
<form action="/lists/{{ item.id }}/contacts/{{ c.id }}/remove" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs" title="Remove">Remove</button>
</form>
</div>
</div>
{% else %}
<div style="padding: 12px; color: var(--muted); font-size: 0.85rem;">No contacts linked</div>
{% endfor %}
</div>
{% endblock %}

84
templates/list_form.html Normal file
View File

@@ -0,0 +1,84 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">{{ page_title }}</h1>
</div>
<div class="card">
<form method="post" action="{{ '/lists/' ~ item.id ~ '/edit' if item else '/lists/create' }}">
<div class="form-grid">
<div class="form-group full-width">
<label class="form-label">Name *</label>
<input type="text" name="name" class="form-input" required
value="{{ item.name if item else '' }}">
</div>
<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 (item and item.domain_id|string == d.id|string) or (not item and prefill_domain_id == d.id|string) }}>
{{ d.name }}
</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Area</label>
<select name="area_id" class="form-select">
<option value="">None</option>
{% for a in areas %}
<option value="{{ a.id }}"
{{ 'selected' if item and item.area_id and item.area_id|string == a.id|string }}>
{{ a.name }}
</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Project</label>
<select name="project_id" class="form-select">
<option value="">None</option>
{% for p in projects %}
<option value="{{ p.id }}"
{{ 'selected' if (item and item.project_id and item.project_id|string == p.id|string) or (not item and prefill_project_id == p.id|string) }}>
{{ p.name }}
</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Type</label>
<select name="list_type" class="form-select">
<option value="checklist" {{ 'selected' if item and item.list_type == 'checklist' }}>Checklist</option>
<option value="ordered" {{ 'selected' if item and item.list_type == 'ordered' }}>Ordered</option>
<option value="reference" {{ 'selected' if item and item.list_type == 'reference' }}>Reference</option>
</select>
</div>
<div class="form-group full-width">
<label class="form-label">Description</label>
<textarea name="description" class="form-textarea" rows="3">{{ item.description if item and item.description else '' }}</textarea>
</div>
<div class="form-group">
<label class="form-label">Tags</label>
<input type="text" name="tags" class="form-input" placeholder="tag1, tag2, ..."
value="{{ item.tags|join(', ') if item and item.tags else '' }}">
</div>
</div>
{% if prefill_task_id is defined and prefill_task_id %}<input type="hidden" name="task_id" value="{{ prefill_task_id }}">{% endif %}
{% if prefill_meeting_id is defined and prefill_meeting_id %}<input type="hidden" name="meeting_id" value="{{ prefill_meeting_id }}">{% endif %}
<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save Changes' if item else 'Create List' }}</button>
<a href="{{ '/tasks/' ~ prefill_task_id ~ '?tab=lists' if prefill_task_id is defined and prefill_task_id else ('/lists/' ~ item.id if item else '/lists') }}" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
{% endblock %}

94
templates/lists.html Normal file
View File

@@ -0,0 +1,94 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Lists<span class="page-count">{{ items|length }}</span></h1>
<a href="/lists/create" class="btn btn-primary">+ New List</a>
</div>
<!-- Filters -->
<form class="filters-bar" method="get" action="/lists" id="list-filters">
<select name="domain_id" class="filter-select" id="domain-filter" onchange="this.form.submit()">
<option value="">All Domains</option>
{% for d in domains %}
<option value="{{ d.id }}" {{ 'selected' if current_domain_id == d.id|string }}>{{ d.name }}</option>
{% endfor %}
</select>
<select name="project_id" class="filter-select" id="project-filter" onchange="this.form.submit()">
<option value="">All Projects</option>
{% for p in projects %}
<option value="{{ p.id }}" {{ 'selected' if current_project_id == p.id|string }}>{{ p.name }}</option>
{% endfor %}
</select>
</form>
{% if items %}
<div class="card mt-3">
{% for item in items %}
<div class="list-row">
<span class="row-title"><a href="/lists/{{ item.id }}">{{ item.name }}</a></span>
<span class="row-meta">
{{ item.completed_count }}/{{ item.item_count }} items
</span>
{% if item.item_count > 0 %}
<div class="progress-bar" style="width: 80px;">
<div class="progress-fill" style="width: {{ (item.completed_count / item.item_count * 100) if item.item_count > 0 else 0 }}%"></div>
</div>
{% endif %}
<span class="row-tag">{{ item.list_type }}</span>
{% if item.domain_name %}
<span class="row-domain-tag" style="background: {{ item.domain_color or '#4F6EF7' }}22; color: {{ item.domain_color or '#4F6EF7' }}">{{ item.domain_name }}</span>
{% endif %}
{% if item.project_name %}
<span class="row-tag">{{ item.project_name }}</span>
{% endif %}
<div class="row-actions">
<a href="/lists/{{ item.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form action="/lists/{{ item.id }}/delete" method="post" data-confirm="Delete this list?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Del</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state mt-3">
<div class="empty-state-icon">&#9776;</div>
<div class="empty-state-text">No lists yet</div>
<a href="/lists/create" class="btn btn-primary">Create First List</a>
</div>
{% endif %}
<script>
(function() {
var domainSel = document.getElementById('domain-filter');
var projectSel = document.getElementById('project-filter');
var currentProjectId = '{{ current_project_id }}';
domainSel.addEventListener('change', function() {
var did = domainSel.value;
if (!did) {
// No domain filter - submit immediately, server returns all projects
document.getElementById('list-filters').submit();
return;
}
// Fetch projects for this domain
fetch('/projects/api/by-domain?domain_id=' + encodeURIComponent(did))
.then(function(r) { return r.json(); })
.then(function(projects) {
projectSel.innerHTML = '<option value="">All Projects</option>';
projects.forEach(function(p) {
var opt = document.createElement('option');
opt.value = p.id;
opt.textContent = p.name;
if (p.id === currentProjectId) opt.selected = true;
projectSel.appendChild(opt);
});
document.getElementById('list-filters').submit();
})
.catch(function() {
document.getElementById('list-filters').submit();
});
});
})();
</script>
{% endblock %}

View File

@@ -0,0 +1,237 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
<a href="/meetings">Meetings</a>
<span class="sep">/</span>
<span>{{ item.title }}</span>
</div>
<div class="detail-header">
<h1 class="detail-title">{{ item.title }}</h1>
<div class="flex gap-2">
<a href="/meetings/{{ item.id }}/edit" class="btn btn-secondary btn-sm">Edit</a>
<form action="/meetings/{{ item.id }}/delete" method="post" data-confirm="Delete this meeting?" style="display:inline">
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
</form>
</div>
</div>
<div class="detail-meta mt-2">
<span class="detail-meta-item">{{ item.meeting_date }}</span>
<span class="status-badge status-{{ item.status }}">{{ item.status }}</span>
{% if item.location %}<span class="detail-meta-item">{{ item.location }}</span>{% endif %}
{% if item.start_at and item.end_at %}
<span class="detail-meta-item">{{ item.start_at.strftime('%H:%M') }} - {{ item.end_at.strftime('%H:%M') }}</span>
{% endif %}
{% if projects %}
{% for p in projects %}
<span class="detail-meta-item"><a href="/projects/{{ p.id }}" style="color: {{ p.domain_color or 'var(--accent)' }};">{{ p.name }}</a></span>
{% endfor %}
{% endif %}
{% if item.tags %}
<div class="mt-1">{% for tag in item.tags %}<span class="row-tag">{{ tag }}</span>{% endfor %}</div>
{% endif %}
</div>
<!-- Tabs -->
<div class="tab-strip">
<a href="/meetings/{{ item.id }}?tab=overview" class="tab-item {{ 'active' if tab == 'overview' }}">Overview</a>
<a href="/meetings/{{ item.id }}?tab=notes" class="tab-item {{ 'active' if tab == 'notes' }}">Notes{% if counts.notes %} ({{ counts.notes }}){% endif %}</a>
<a href="/meetings/{{ item.id }}?tab=links" class="tab-item {{ 'active' if tab == 'links' }}">Links{% if counts.links %} ({{ counts.links }}){% endif %}</a>
<a href="/meetings/{{ item.id }}?tab=files" class="tab-item {{ 'active' if tab == 'files' }}">Files{% if counts.files %} ({{ counts.files }}){% endif %}</a>
<a href="/meetings/{{ item.id }}?tab=lists" class="tab-item {{ 'active' if tab == 'lists' }}">Lists{% if counts.lists %} ({{ counts.lists }}){% endif %}</a>
<a href="/meetings/{{ item.id }}?tab=decisions" class="tab-item {{ 'active' if tab == 'decisions' }}">Decisions{% if counts.decisions %} ({{ counts.decisions }}){% endif %}</a>
<a href="/meetings/{{ item.id }}?tab=processes" class="tab-item {{ 'active' if tab == 'processes' }}">Processes</a>
<a href="/meetings/{{ item.id }}?tab=contacts" class="tab-item {{ 'active' if tab == 'contacts' }}">Contacts{% if counts.contacts %} ({{ counts.contacts }}){% endif %}</a>
</div>
{% if tab == 'overview' %}
<!-- Agenda -->
{% if item.agenda %}
<div class="card mb-4">
<div class="card-header"><h3 class="card-title">Agenda</h3></div>
<div class="detail-body" style="padding: 12px 16px;">{{ item.agenda }}</div>
</div>
{% endif %}
<!-- Meeting Notes -->
{% if item.notes_body %}
<div class="card mb-4">
<div class="card-header"><h3 class="card-title">Notes</h3></div>
<div class="detail-body" style="padding: 12px 16px;">{{ item.notes_body }}</div>
</div>
{% endif %}
<!-- Transcript -->
{% if item.transcript %}
<div class="card mb-4">
<div class="card-header"><h3 class="card-title">Transcript</h3></div>
<div class="detail-body" style="padding: 12px 16px; font-family: var(--font-mono); font-size: 0.82rem; white-space: pre-wrap;">{{ item.transcript }}</div>
</div>
{% endif %}
<!-- Action Items -->
<div class="card mb-4">
<div class="card-header">
<h3 class="card-title">Action Items<span class="page-count">{{ action_items|length }}</span></h3>
</div>
<form class="quick-add" action="/meetings/{{ item.id }}/action-item" method="post" style="border-bottom: 1px solid var(--border);">
<input type="text" name="title" placeholder="Add action item..." required>
<select name="domain_id" class="filter-select" required style="width: auto;">
{% for d in domains %}
<option value="{{ d.id }}">{{ d.name }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-primary btn-sm">Add</button>
</form>
{% for task in action_items %}
<div class="list-row {{ 'completed' if task.status == 'done' }}">
<div class="row-check">
<form action="/tasks/{{ task.id }}/toggle" method="post" style="display:inline">
<input type="checkbox" id="mt-{{ task.id }}" {{ 'checked' if task.status == 'done' }}
onchange="this.form.submit()">
<label for="mt-{{ task.id }}"></label>
</form>
</div>
<span class="priority-dot priority-{{ task.priority }}"></span>
<span class="row-title"><a href="/tasks/{{ task.id }}">{{ task.title }}</a></span>
{% if task.project_name %}<span class="row-tag">{{ task.project_name }}</span>{% endif %}
<span class="status-badge status-{{ task.status }}">{{ task.status|replace('_', ' ') }}</span>
</div>
{% else %}
<div style="padding: 16px; color: var(--muted); font-size: 0.85rem;">No action items yet</div>
{% endfor %}
</div>
<!-- Decisions -->
{% if decisions %}
<div class="card">
<div class="card-header"><h3 class="card-title">Decisions<span class="page-count">{{ decisions|length }}</span></h3></div>
{% for dec in decisions %}
<div class="list-row">
<span class="row-title"><a href="/decisions/{{ dec.id }}">{{ dec.title }}</a></span>
<span class="status-badge status-{{ dec.status }}">{{ dec.status }}</span>
<span class="row-tag">{{ dec.impact }}</span>
</div>
{% endfor %}
</div>
{% endif %}
{% elif tab == 'notes' %}
<a href="/notes/create?meeting_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New Note</a>
{% for n in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/notes/{{ n.id }}">{{ n.title }}</a></span>
<span class="row-meta">{{ n.updated_at.strftime('%Y-%m-%d') if n.updated_at else '' }}</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No notes linked to this meeting</div></div>
{% endfor %}
{% elif tab == 'links' %}
<a href="/links/create?meeting_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New Link</a>
{% for w in tab_data %}
<div class="list-row">
<span class="row-title"><a href="{{ w.url }}" target="_blank">{{ w.label }}</a></span>
<span class="row-meta">{{ w.url[:50] }}{% if w.url|length > 50 %}...{% endif %}</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No links linked to this meeting</div></div>
{% endfor %}
{% elif tab == 'files' %}
<a href="/files/upload?context_type=meeting&context_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ Upload File</a>
{% for f in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/files/{{ f.id }}">{{ f.original_filename }}</a></span>
<span class="row-meta">{{ f.created_at.strftime('%Y-%m-%d') if f.created_at else '' }}</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No files attached to this meeting</div></div>
{% endfor %}
{% elif tab == 'lists' %}
<a href="/lists/create?meeting_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New List</a>
{% for l in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/lists/{{ l.id }}">{{ l.name }}</a></span>
<span class="row-meta">{{ l.item_count }} items</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No lists linked to this meeting</div></div>
{% endfor %}
{% elif tab == 'decisions' %}
<div class="card mb-4">
<form action="/meetings/{{ item.id }}/decisions/add" method="post" class="flex gap-2 items-end" style="padding: 12px;">
<div class="form-group" style="flex:1; margin:0;">
<label class="form-label">Decision</label>
<select name="decision_id" class="form-select" required>
<option value="">Select decision...</option>
{% for d in all_decisions %}
<option value="{{ d.id }}">{{ d.title }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary btn-sm">Link</button>
</form>
</div>
<a href="/decisions/create?meeting_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New Decision</a>
{% for d in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/decisions/{{ d.id }}">{{ d.title }}</a></span>
<span class="status-badge status-{{ d.status }}">{{ d.status }}</span>
{% if d.impact %}<span class="row-tag">{{ d.impact }}</span>{% endif %}
{% if d.decided_at %}<span class="row-meta">{{ d.decided_at.strftime('%Y-%m-%d') if d.decided_at else '' }}</span>{% endif %}
<div class="row-actions">
<form action="/meetings/{{ item.id }}/decisions/{{ d.id }}/remove" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs" title="Unlink">Unlink</button>
</form>
</div>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No decisions linked to this meeting</div></div>
{% endfor %}
{% elif tab == 'processes' %}
<div class="empty-state"><div class="empty-state-text">Process management coming soon</div></div>
{% elif tab == 'contacts' %}
<div class="card mb-4">
<form action="/meetings/{{ item.id }}/contacts/add" method="post" class="flex gap-2 items-end" style="padding: 12px;">
<div class="form-group" style="flex:1; margin:0;">
<label class="form-label">Contact</label>
<select name="contact_id" class="form-select" required>
<option value="">Select contact...</option>
{% for c in all_contacts %}
<option value="{{ c.id }}">{{ c.first_name }} {{ c.last_name or '' }}</option>
{% endfor %}
</select>
</div>
<div class="form-group" style="flex:1; margin:0;">
<label class="form-label">Role</label>
<select name="role" class="form-select">
<option value="attendee">Attendee</option>
<option value="organizer">Organizer</option>
<option value="optional">Optional</option>
</select>
</div>
<button type="submit" class="btn btn-primary btn-sm">Add</button>
</form>
</div>
{% for c in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/contacts/{{ c.id }}">{{ c.first_name }} {{ c.last_name or '' }}</a></span>
{% if c.role %}<span class="row-tag">{{ c.role }}</span>{% endif %}
<span class="row-meta">{{ c.linked_at.strftime('%Y-%m-%d') if c.linked_at else '' }}</span>
<div class="row-actions">
<form action="/meetings/{{ item.id }}/contacts/{{ c.id }}/remove" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs" title="Remove">Remove</button>
</form>
</div>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No contacts linked to this meeting</div></div>
{% endfor %}
{% endif %}
{% endblock %}

102
templates/meeting_form.html Normal file
View File

@@ -0,0 +1,102 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">{{ page_title }}</h1>
</div>
<div class="card">
<form method="post" action="{{ '/meetings/' ~ item.id ~ '/edit' if item else '/meetings/create' }}">
<div class="form-grid">
<div class="form-group full-width">
<label class="form-label">Title *</label>
<input type="text" name="title" class="form-input" required
value="{{ item.title if item else '' }}">
</div>
<div class="form-group">
<label class="form-label">Date *</label>
<input type="date" name="meeting_date" class="form-input" required
value="{{ item.meeting_date if item else '' }}">
</div>
<div class="form-group">
<label class="form-label">Status</label>
<select name="status" class="form-select">
<option value="scheduled" {{ 'selected' if item and item.status == 'scheduled' }}>Scheduled</option>
<option value="completed" {{ 'selected' if item and item.status == 'completed' }}>Completed</option>
<option value="cancelled" {{ 'selected' if item and item.status == 'cancelled' }}>Cancelled</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Start Time</label>
<input type="datetime-local" name="start_at" class="form-input"
value="{{ item.start_at.strftime('%Y-%m-%dT%H:%M') if item and item.start_at else '' }}">
</div>
<div class="form-group">
<label class="form-label">End Time</label>
<input type="datetime-local" name="end_at" class="form-input"
value="{{ item.end_at.strftime('%Y-%m-%dT%H:%M') if item and item.end_at else '' }}">
</div>
<div class="form-group">
<label class="form-label">Location</label>
<input type="text" name="location" class="form-input" placeholder="Zoom, Google Meet, Room..."
value="{{ item.location if item and item.location else '' }}">
</div>
<div class="form-group">
<label class="form-label">Priority</label>
<select name="priority" class="form-select">
<option value="">None</option>
<option value="1" {{ 'selected' if item and item.priority == 1 }}>Critical</option>
<option value="2" {{ 'selected' if item and item.priority == 2 }}>High</option>
<option value="3" {{ 'selected' if item and item.priority == 3 }}>Normal</option>
<option value="4" {{ 'selected' if item and item.priority == 4 }}>Low</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Series (Parent Meeting)</label>
<select name="parent_id" class="form-select">
<option value="">None</option>
{% for m in parent_meetings %}
<option value="{{ m.id }}" {{ 'selected' if item and item.parent_id and item.parent_id|string == m.id|string }}>
{{ m.title }} ({{ m.meeting_date }})
</option>
{% endfor %}
</select>
</div>
<div class="form-group full-width">
<label class="form-label">Agenda</label>
<textarea name="agenda" class="form-textarea" rows="4">{{ item.agenda if item and item.agenda else '' }}</textarea>
</div>
{% if item %}
<div class="form-group full-width">
<label class="form-label">Transcript</label>
<textarea name="transcript" class="form-textarea" rows="6">{{ item.transcript if item and item.transcript else '' }}</textarea>
</div>
<div class="form-group full-width">
<label class="form-label">Meeting Notes</label>
<textarea name="notes_body" class="form-textarea" rows="6">{{ item.notes_body if item and item.notes_body else '' }}</textarea>
</div>
{% endif %}
<div class="form-group">
<label class="form-label">Tags</label>
<input type="text" name="tags" class="form-input" placeholder="tag1, tag2, ..."
value="{{ item.tags|join(', ') if item and item.tags else '' }}">
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save Changes' if item else 'Create Meeting' }}</button>
<a href="{{ '/meetings/' ~ item.id if item else '/meetings' }}" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
{% endblock %}

46
templates/meetings.html Normal file
View File

@@ -0,0 +1,46 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Meetings<span class="page-count">{{ items|length }}</span></h1>
<a href="/meetings/create" class="btn btn-primary">+ New Meeting</a>
</div>
<form class="filters-bar" method="get" action="/meetings">
<select name="status" class="filter-select" onchange="this.form.submit()">
<option value="">All Statuses</option>
<option value="scheduled" {{ 'selected' if current_status == 'scheduled' }}>Scheduled</option>
<option value="completed" {{ 'selected' if current_status == 'completed' }}>Completed</option>
<option value="cancelled" {{ 'selected' if current_status == 'cancelled' }}>Cancelled</option>
</select>
</form>
{% if items %}
<div class="card mt-3">
{% for item in items %}
<div class="list-row">
<span class="row-title"><a href="/meetings/{{ item.id }}">{{ item.title }}</a></span>
<span class="row-meta">{{ item.meeting_date }}</span>
{% if item.location %}
<span class="row-tag">{{ item.location }}</span>
{% endif %}
{% if item.action_count %}
<span class="row-meta">{{ item.action_count }} action{{ 's' if item.action_count != 1 }}</span>
{% endif %}
<span class="status-badge status-{{ item.status }}">{{ item.status }}</span>
<div class="row-actions">
<a href="/meetings/{{ item.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form action="/meetings/{{ item.id }}/delete" method="post" data-confirm="Delete this meeting?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Del</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state mt-3">
<div class="empty-state-icon">&#128197;</div>
<div class="empty-state-text">No meetings yet</div>
<a href="/meetings/create" class="btn btn-primary">Schedule First Meeting</a>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,26 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
{% if domain %}<span style="color:{{ domain.color }}">{{ domain.name }}</span><span class="sep">/</span>{% endif %}
{% if project %}<a href="/projects/{{ project.id }}">{{ project.name }}</a><span class="sep">/</span>{% endif %}
<span>{{ item.title }}</span>
</div>
<div class="detail-header">
<div class="flex items-center justify-between">
<h1 class="detail-title">{{ item.title }}</h1>
<div class="flex gap-2">
<a href="/notes/{{ item.id }}/edit" class="btn btn-secondary btn-sm">Edit</a>
<form action="/notes/{{ item.id }}/delete" method="post" data-confirm="Delete?" style="display:inline"><button class="btn btn-danger btn-sm">Delete</button></form>
</div>
</div>
<div class="detail-meta mt-2">
<span class="detail-meta-item">Updated {{ item.updated_at.strftime('%Y-%m-%d %H:%M') if item.updated_at else '' }}</span>
{% if item.tags %}{% for tag in item.tags %}<span class="row-tag">{{ tag }}</span>{% endfor %}{% endif %}
</div>
</div>
{% if item.body %}
<div class="card"><div class="detail-body" style="white-space:pre-wrap;font-family:var(--font-body)">{{ item.body }}</div></div>
{% else %}
<div class="card"><div class="text-muted" style="padding:20px">No content yet. <a href="/notes/{{ item.id }}/edit">Start writing</a></div></div>
{% endif %}
{% endblock %}

21
templates/note_form.html Normal file
View File

@@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb"><a href="/notes">Notes</a><span class="sep">/</span><span>{{ 'Edit' if item else 'New Note' }}</span></div>
<div class="page-header"><h1 class="page-title">{{ 'Edit Note' if item else 'New Note' }}</h1></div>
<div class="card">
<form method="post" action="{{ '/notes/' ~ item.id ~ '/edit' if item else '/notes/create' }}">
<div class="form-grid">
<div class="form-group full-width"><label class="form-label">Title *</label><input type="text" name="title" class="form-input" required value="{{ item.title if item else '' }}"></div>
<div class="form-group"><label class="form-label">Domain *</label>
<select name="domain_id" class="form-select" required>{% for d in domains %}<option value="{{ d.id }}" {{ 'selected' if (item and item.domain_id|string == d.id|string) or (not item and prefill_domain_id == d.id|string) }}>{{ d.name }}</option>{% endfor %}</select></div>
<div class="form-group"><label class="form-label">Project</label>
<select name="project_id" class="form-select"><option value="">-- None --</option>{% for p in projects %}<option value="{{ p.id }}" {{ 'selected' if (item and item.project_id and item.project_id|string == p.id|string) or (not item and prefill_project_id == p.id|string) }}>{{ p.name }}</option>{% endfor %}</select></div>
<div class="form-group full-width"><label class="form-label">Content</label><textarea name="body" class="form-textarea" rows="15" style="font-family:var(--font-mono);font-size:0.88rem">{{ item.body if item and item.body else '' }}</textarea></div>
<div class="form-group full-width"><label class="form-label">Tags</label><input type="text" name="tags" class="form-input" value="{{ item.tags|join(', ') if item and item.tags else '' }}"></div>
<input type="hidden" name="content_format" value="rich">
{% if prefill_task_id is defined and prefill_task_id %}<input type="hidden" name="task_id" value="{{ prefill_task_id }}">{% endif %}
{% if prefill_meeting_id is defined and prefill_meeting_id %}<input type="hidden" name="meeting_id" value="{{ prefill_meeting_id }}">{% endif %}
<div class="form-actions"><button type="submit" class="btn btn-primary">{{ 'Save' if item else 'Create' }}</button><a href="{{ '/notes/' ~ item.id if item else '/notes' }}" class="btn btn-secondary">Cancel</a></div>
</div>
</form></div>
{% endblock %}

25
templates/notes.html Normal file
View File

@@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Notes<span class="page-count">{{ items|length }}</span></h1>
<a href="/notes/create" class="btn btn-primary">+ New Note</a>
</div>
{% if items %}
<div class="card">
{% for item in items %}
<div class="list-row">
{% if item.domain_color %}<span class="row-domain-tag" style="background:{{ item.domain_color }}22;color:{{ item.domain_color }}">{{ item.domain_name }}</span>{% endif %}
<span class="row-title"><a href="/notes/{{ item.id }}">{{ item.title }}</a></span>
{% if item.project_name %}<span class="row-tag">{{ item.project_name }}</span>{% endif %}
<span class="row-meta">{{ item.updated_at.strftime('%Y-%m-%d') if item.updated_at else '' }}</span>
<div class="row-actions">
<a href="/notes/{{ item.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form action="/notes/{{ item.id }}/delete" method="post" data-confirm="Delete?" style="display:inline"><button class="btn btn-ghost btn-xs" style="color:var(--red)">Del</button></form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state"><div class="empty-state-icon">&#128196;</div><div class="empty-state-text">No notes yet</div><a href="/notes/create" class="btn btn-primary">Create Note</a></div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,133 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
<a href="/processes">Processes</a>
<span class="sep">/</span>
<a href="/processes/{{ run.process_id_ref }}">{{ run.process_name }}</a>
<span class="sep">/</span>
<span>{{ run.title }}</span>
</div>
<div class="detail-header">
<h1 class="detail-title">{{ run.title }}</h1>
<div class="flex gap-2">
{% if run.status != 'completed' %}
<form action="/processes/runs/{{ run.id }}/complete" method="post" data-confirm="Mark this run as complete?" style="display:inline">
<button type="submit" class="btn btn-primary btn-sm">Mark Complete</button>
</form>
{% endif %}
<form action="/processes/runs/{{ run.id }}/delete" method="post" data-confirm="Delete this run?" style="display:inline">
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
</form>
</div>
</div>
<div class="detail-meta mt-2">
<span class="status-badge status-{{ run.status }}">{{ run.status|replace('_', ' ') }}</span>
<span class="row-tag">{{ run.process_type }}</span>
<span class="row-tag">{{ run.task_generation|replace('_', ' ') }}</span>
{% if run.project_name %}<span class="detail-meta-item">{{ run.project_name }}</span>{% endif %}
{% if run.contact_first %}<span class="detail-meta-item">{{ run.contact_first }} {{ run.contact_last or '' }}</span>{% endif %}
{% if run.started_at %}<span class="detail-meta-item">Started {{ run.started_at.strftime('%Y-%m-%d') }}</span>{% endif %}
{% if run.completed_at %}<span class="detail-meta-item">Completed {{ run.completed_at.strftime('%Y-%m-%d') }}</span>{% endif %}
</div>
<!-- Progress Bar -->
<div class="card mt-3" style="padding: 16px;">
<div style="display: flex; align-items: center; gap: 12px;">
<span style="font-weight: 600; font-size: 0.9rem;">Progress</span>
<div style="flex: 1; height: 8px; background: var(--border); border-radius: 4px; overflow: hidden;">
<div style="width: {{ (completed_steps / total_steps * 100)|int if total_steps > 0 else 0 }}%; height: 100%; background: var(--green); border-radius: 4px; transition: width 0.3s;"></div>
</div>
<span style="font-weight: 600; font-size: 0.9rem;">{{ completed_steps }}/{{ total_steps }}</span>
</div>
</div>
<!-- Steps Checklist -->
<div class="card mt-3">
<div class="card-header">
<h3 class="card-title">Steps</h3>
</div>
{% for step in steps %}
<div class="list-row {{ 'completed' if step.status == 'completed' }}" style="align-items: flex-start;">
<div class="row-check">
{% if step.status == 'completed' %}
<form action="/processes/runs/{{ run.id }}/steps/{{ step.id }}/uncomplete" method="post" style="display:inline">
<input type="checkbox" id="step-{{ step.id }}" checked onchange="this.form.submit()">
<label for="step-{{ step.id }}"></label>
</form>
{% else %}
<form action="/processes/runs/{{ run.id }}/steps/{{ step.id }}/complete" method="post" style="display:inline" id="complete-form-{{ step.id }}">
<input type="checkbox" id="step-{{ step.id }}" onchange="this.form.submit()">
<label for="step-{{ step.id }}"></label>
</form>
{% endif %}
</div>
<div style="flex: 1;">
<div style="display: flex; align-items: center; gap: 8px;">
<span class="row-meta" style="min-width: 20px; font-weight: 600;">{{ loop.index }}</span>
<span class="row-title" style="{{ 'text-decoration: line-through; opacity: 0.6;' if step.status == 'completed' }}">{{ step.title }}</span>
</div>
{% if step.instructions %}
<div style="color: var(--muted); font-size: 0.82rem; margin: 4px 0 0 28px;">{{ step.instructions }}</div>
{% endif %}
{% if step.completed_at %}
<div style="color: var(--green); font-size: 0.78rem; margin: 4px 0 0 28px;">
Completed {{ step.completed_at.strftime('%Y-%m-%d %H:%M') }}
</div>
{% endif %}
{% if step.notes %}
<div style="color: var(--muted); font-size: 0.82rem; margin: 2px 0 0 28px; font-style: italic;">{{ step.notes }}</div>
{% endif %}
{% if step.status != 'completed' %}
<div style="margin: 6px 0 0 28px;">
<button type="button" class="btn btn-ghost btn-xs" onclick="toggleNotes('{{ step.id }}')">Add Notes</button>
<div id="notes-{{ step.id }}" style="display: none; margin-top: 4px;">
<form action="/processes/runs/{{ run.id }}/steps/{{ step.id }}/complete" method="post" style="display: flex; gap: 6px; align-items: flex-end;">
<input type="text" name="notes" class="form-input" placeholder="Completion notes..." style="flex: 1; height: 32px; font-size: 0.82rem;">
<button type="submit" class="btn btn-primary btn-sm">Complete with Notes</button>
</form>
</div>
</div>
{% endif %}
<!-- Show linked tasks for this step -->
{% if step_tasks.get(step.id|string) %}
<div style="margin: 6px 0 0 28px;">
{% for task in step_tasks[step.id|string] %}
<div style="display: inline-flex; align-items: center; gap: 4px; margin-right: 8px;">
<span class="status-badge status-{{ task.status }}" style="font-size: 0.72rem;">{{ task.status }}</span>
<a href="/tasks/{{ task.id }}" style="font-size: 0.82rem;">{{ task.title }}</a>
</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<!-- All Generated Tasks -->
{% if tasks %}
<div class="card mt-3">
<div class="card-header">
<h3 class="card-title">Generated Tasks<span class="page-count">{{ tasks|length }}</span></h3>
</div>
{% for task in tasks %}
<div class="list-row {{ 'completed' if task.status == 'done' }}">
<span class="priority-dot priority-{{ task.priority }}"></span>
<span class="row-title"><a href="/tasks/{{ task.id }}">{{ task.title }}</a></span>
{% if task.project_name %}<span class="row-tag">{{ task.project_name }}</span>{% endif %}
<span class="status-badge status-{{ task.status }}">{{ task.status|replace('_', ' ') }}</span>
</div>
{% endfor %}
</div>
{% endif %}
<script>
function toggleNotes(stepId) {
var el = document.getElementById('notes-' + stepId);
el.style.display = el.style.display === 'none' ? 'block' : 'none';
}
</script>
{% endblock %}

View File

@@ -0,0 +1,46 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">All Process Runs<span class="page-count">{{ items|length }}</span></h1>
<a href="/processes" class="btn btn-secondary">Back to Processes</a>
</div>
<form class="filters-bar" method="get" action="/processes/runs">
<select name="status" class="filter-select" onchange="this.form.submit()">
<option value="">All Statuses</option>
<option value="not_started" {{ 'selected' if current_status == 'not_started' }}>Not Started</option>
<option value="in_progress" {{ 'selected' if current_status == 'in_progress' }}>In Progress</option>
<option value="completed" {{ 'selected' if current_status == 'completed' }}>Completed</option>
</select>
</form>
{% if items %}
<div class="card mt-3">
{% for item in items %}
<div class="list-row">
<span class="row-title"><a href="/processes/runs/{{ item.id }}">{{ item.title }}</a></span>
<span class="row-tag">{{ item.process_name }}</span>
<span class="status-badge status-{{ item.status }}">{{ item.status|replace('_', ' ') }}</span>
{% if item.total_steps > 0 %}
<div class="row-meta" style="display: flex; align-items: center; gap: 6px;">
<div style="width: 60px; height: 4px; background: var(--border); border-radius: 2px; overflow: hidden;">
<div style="width: {{ (item.completed_steps / item.total_steps * 100)|int }}%; height: 100%; background: var(--green); border-radius: 2px;"></div>
</div>
<span>{{ item.completed_steps }}/{{ item.total_steps }}</span>
</div>
{% endif %}
{% if item.project_name %}
<span class="row-tag">{{ item.project_name }}</span>
{% endif %}
<span class="row-meta">{{ item.created_at.strftime('%Y-%m-%d') }}</span>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state mt-3">
<div class="empty-state-icon">&#9654;</div>
<div class="empty-state-text">No process runs yet</div>
<a href="/processes" class="btn btn-primary">Go to Processes</a>
</div>
{% endif %}
{% endblock %}

52
templates/processes.html Normal file
View File

@@ -0,0 +1,52 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Processes<span class="page-count">{{ items|length }}</span></h1>
<div class="flex gap-2">
<a href="/processes/runs" class="btn btn-secondary">All Runs</a>
<a href="/processes/create" class="btn btn-primary">+ New Process</a>
</div>
</div>
<form class="filters-bar" method="get" action="/processes">
<select name="status" class="filter-select" onchange="this.form.submit()">
<option value="">All Statuses</option>
<option value="draft" {{ 'selected' if current_status == 'draft' }}>Draft</option>
<option value="active" {{ 'selected' if current_status == 'active' }}>Active</option>
<option value="archived" {{ 'selected' if current_status == 'archived' }}>Archived</option>
</select>
<select name="process_type" class="filter-select" onchange="this.form.submit()">
<option value="">All Types</option>
<option value="workflow" {{ 'selected' if current_type == 'workflow' }}>Workflow</option>
<option value="checklist" {{ 'selected' if current_type == 'checklist' }}>Checklist</option>
</select>
</form>
{% if items %}
<div class="card mt-3">
{% for item in items %}
<div class="list-row">
<span class="row-title"><a href="/processes/{{ item.id }}">{{ item.name }}</a></span>
<span class="row-tag">{{ item.process_type }}</span>
<span class="status-badge status-{{ item.status }}">{{ item.status }}</span>
<span class="row-meta">{{ item.step_count }} step{{ 's' if item.step_count != 1 }}</span>
{% if item.category %}
<span class="row-tag">{{ item.category }}</span>
{% endif %}
<div class="row-actions">
<a href="/processes/{{ item.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form action="/processes/{{ item.id }}/delete" method="post" data-confirm="Delete this process?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Del</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state mt-3">
<div class="empty-state-icon">&#9881;</div>
<div class="empty-state-text">No processes yet</div>
<a href="/processes/create" class="btn btn-primary">Create First Process</a>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,183 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
<a href="/processes">Processes</a>
<span class="sep">/</span>
<span>{{ item.name }}</span>
</div>
<div class="detail-header">
<h1 class="detail-title">{{ item.name }}</h1>
<div class="flex gap-2">
<a href="/processes/{{ item.id }}/edit" class="btn btn-secondary btn-sm">Edit</a>
<form action="/processes/{{ item.id }}/delete" method="post" data-confirm="Delete this process?" style="display:inline">
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
</form>
</div>
</div>
<div class="detail-meta mt-2">
<span class="status-badge status-{{ item.status }}">{{ item.status }}</span>
<span class="row-tag">{{ item.process_type }}</span>
{% if item.category %}<span class="detail-meta-item">{{ item.category }}</span>{% endif %}
<span class="detail-meta-item">{{ steps|length }} step{{ 's' if steps|length != 1 }}</span>
<span class="detail-meta-item">Created {{ item.created_at.strftime('%Y-%m-%d') }}</span>
{% if item.tags %}
<div class="mt-1">{% for tag in item.tags %}<span class="row-tag">{{ tag }}</span>{% endfor %}</div>
{% endif %}
</div>
{% if item.description %}
<div class="card mt-3">
<div class="card-header"><h3 class="card-title">Description</h3></div>
<div class="detail-body" style="padding: 12px 16px;">{{ item.description }}</div>
</div>
{% endif %}
<!-- Steps -->
<div class="card mt-3">
<div class="card-header">
<h3 class="card-title">Steps<span class="page-count">{{ steps|length }}</span></h3>
</div>
{% for step in steps %}
<div class="list-row" style="align-items: flex-start;">
<span class="row-meta" style="min-width: 28px; text-align: center; font-weight: 600;">{{ loop.index }}</span>
<div style="flex: 1;">
<span class="row-title">{{ step.title }}</span>
{% if step.instructions %}
<div style="color: var(--muted); font-size: 0.82rem; margin-top: 2px;">{{ step.instructions[:120] }}{{ '...' if step.instructions|length > 120 }}</div>
{% endif %}
{% if step.expected_output %}
<div style="color: var(--muted); font-size: 0.82rem; margin-top: 2px;">Output: {{ step.expected_output[:80] }}</div>
{% endif %}
</div>
{% if step.estimated_days %}
<span class="row-meta">{{ step.estimated_days }}d</span>
{% endif %}
<div class="row-actions">
<button type="button" class="btn btn-ghost btn-xs" onclick="toggleEditStep('{{ step.id }}')">Edit</button>
<form action="/processes/{{ item.id }}/steps/{{ step.id }}/delete" method="post" data-confirm="Delete this step?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Del</button>
</form>
</div>
</div>
<!-- Inline edit form (hidden by default) -->
<div id="edit-step-{{ step.id }}" style="display: none; border-bottom: 1px solid var(--border); padding: 12px 16px; background: var(--surface2);">
<form action="/processes/{{ item.id }}/steps/{{ step.id }}/edit" method="post">
<div class="form-grid">
<div class="form-group full-width">
<label class="form-label">Title *</label>
<input type="text" name="title" class="form-input" value="{{ step.title }}" required>
</div>
<div class="form-group full-width">
<label class="form-label">Instructions</label>
<textarea name="instructions" class="form-textarea" rows="2">{{ step.instructions or '' }}</textarea>
</div>
<div class="form-group">
<label class="form-label">Expected Output</label>
<input type="text" name="expected_output" class="form-input" value="{{ step.expected_output or '' }}">
</div>
<div class="form-group">
<label class="form-label">Estimated Days</label>
<input type="number" name="estimated_days" class="form-input" min="0" value="{{ step.estimated_days or '' }}">
</div>
</div>
<div class="form-actions" style="margin-top: 8px;">
<button type="submit" class="btn btn-primary btn-sm">Save Step</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="toggleEditStep('{{ step.id }}')">Cancel</button>
</div>
</form>
</div>
{% endfor %}
<!-- Quick add step -->
<form class="quick-add" action="/processes/{{ item.id }}/steps/add" method="post" style="border-top: 1px solid var(--border);">
<input type="text" name="title" placeholder="Add a step..." required>
<button type="submit" class="btn btn-primary btn-sm">Add Step</button>
</form>
</div>
<!-- Runs -->
<div class="card mt-3">
<div class="card-header">
<h3 class="card-title">Runs<span class="page-count">{{ runs|length }}</span></h3>
</div>
{% for run in runs %}
<div class="list-row">
<span class="row-title"><a href="/processes/runs/{{ run.id }}">{{ run.title }}</a></span>
<span class="status-badge status-{{ run.status }}">{{ run.status|replace('_', ' ') }}</span>
{% if run.total_steps > 0 %}
<span class="row-meta">{{ run.completed_steps }}/{{ run.total_steps }} steps</span>
{% endif %}
{% if run.project_name %}
<span class="row-tag">{{ run.project_name }}</span>
{% endif %}
<span class="row-meta">{{ run.created_at.strftime('%Y-%m-%d') }}</span>
<div class="row-actions">
<form action="/processes/runs/{{ run.id }}/delete" method="post" data-confirm="Delete this run?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Del</button>
</form>
</div>
</div>
{% endfor %}
{% if not runs %}
<div style="padding: 16px; color: var(--muted); font-size: 0.85rem;">No runs yet</div>
{% endif %}
<!-- Start Run Form -->
{% if steps %}
<div style="border-top: 1px solid var(--border); padding: 12px 16px;">
<button type="button" class="btn btn-primary btn-sm" onclick="document.getElementById('start-run-form').style.display = document.getElementById('start-run-form').style.display === 'none' ? 'block' : 'none'">+ Start Run</button>
<div id="start-run-form" style="display: none; margin-top: 12px;">
<form action="/processes/{{ item.id }}/runs/start" method="post">
<div class="form-grid">
<div class="form-group full-width">
<label class="form-label">Run Title *</label>
<input type="text" name="title" class="form-input" required
value="{{ item.name }} - Run">
</div>
<div class="form-group">
<label class="form-label">Task Generation</label>
<select name="task_generation" class="form-select">
<option value="all_at_once">All at Once</option>
<option value="step_by_step">Step by Step</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Project</label>
<select name="project_id" class="form-select">
<option value="">None</option>
{% for p in projects %}
<option value="{{ p.id }}">{{ p.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Contact</label>
<select name="contact_id" class="form-select">
<option value="">None</option>
{% for c in contacts %}
<option value="{{ c.id }}">{{ c.first_name }} {{ c.last_name or '' }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-actions" style="margin-top: 8px;">
<button type="submit" class="btn btn-primary btn-sm">Start Run</button>
</div>
</form>
</div>
</div>
{% endif %}
</div>
<script>
function toggleEditStep(stepId) {
var el = document.getElementById('edit-step-' + stepId);
el.style.display = el.style.display === 'none' ? 'block' : 'none';
}
</script>
{% endblock %}

View File

@@ -0,0 +1,57 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">{{ page_title }}</h1>
</div>
<div class="card">
<form method="post" action="{{ '/processes/' ~ item.id ~ '/edit' if item else '/processes/create' }}">
<div class="form-grid">
<div class="form-group full-width">
<label class="form-label">Name *</label>
<input type="text" name="name" class="form-input" required
value="{{ item.name if item else '' }}">
</div>
<div class="form-group full-width">
<label class="form-label">Description</label>
<textarea name="description" class="form-textarea" rows="3">{{ item.description if item and item.description else '' }}</textarea>
</div>
<div class="form-group">
<label class="form-label">Type</label>
<select name="process_type" class="form-select">
<option value="checklist" {{ 'selected' if item and item.process_type == 'checklist' }}>Checklist</option>
<option value="workflow" {{ 'selected' if item and item.process_type == 'workflow' }}>Workflow</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Status</label>
<select name="status" class="form-select">
<option value="draft" {{ 'selected' if item and item.status == 'draft' }}>Draft</option>
<option value="active" {{ 'selected' if item and item.status == 'active' }}>Active</option>
<option value="archived" {{ 'selected' if item and item.status == 'archived' }}>Archived</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Category</label>
<input type="text" name="category" class="form-input" placeholder="e.g. Onboarding, Publishing..."
value="{{ item.category if item and item.category else '' }}">
</div>
<div class="form-group">
<label class="form-label">Tags</label>
<input type="text" name="tags" class="form-input" placeholder="tag1, tag2, ..."
value="{{ item.tags|join(', ') if item and item.tags else '' }}">
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save Changes' if item else 'Create Process' }}</button>
<a href="{{ '/processes/' ~ item.id if item else '/processes' }}" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,202 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
{% if domain %}<span style="color: {{ domain.color or '#4F6EF7' }}">{{ domain.name }}</span><span class="sep">/</span>{% endif %}
{% if area %}<span>{{ area.name }}</span><span class="sep">/</span>{% endif %}
<span>{{ item.name }}</span>
</div>
<div class="detail-header">
<div class="flex items-center justify-between">
<h1 class="detail-title">{{ item.name }}</h1>
<div class="flex gap-2">
<a href="/projects/{{ item.id }}/edit" class="btn btn-secondary btn-sm">Edit</a>
<form action="/projects/{{ item.id }}/delete" method="post" data-confirm="Delete this project?" style="display:inline">
<button class="btn btn-danger btn-sm">Delete</button>
</form>
</div>
</div>
<div class="detail-meta mt-2">
<span class="status-badge status-{{ item.status }}">{{ item.status|replace('_', ' ') }}</span>
<span class="detail-meta-item"><span class="priority-dot priority-{{ item.priority }}"></span> P{{ item.priority }}</span>
{% if item.target_date %}<span class="detail-meta-item">Target: {{ item.target_date }}</span>{% endif %}
</div>
<a href="/tasks/?project_id={{ item.id }}" class="stat-card-link" style="max-width: 300px; display: block; margin-top: 8px;">
<div class="progress-bar"><div class="progress-fill" style="width: {{ progress }}%"></div></div>
<div class="progress-text">{{ done_count }}/{{ task_count }} tasks complete ({{ progress }}%)</div>
</a>
</div>
{% if item.description %}
<div class="card mb-4"><div class="detail-body">{{ item.description }}</div></div>
{% endif %}
<!-- Tabs -->
<div class="tab-strip">
<a href="/projects/{{ item.id }}?tab=tasks" class="tab-item {{ 'active' if tab == 'tasks' }}">Tasks ({{ tasks|length }})</a>
<a href="/projects/{{ item.id }}?tab=notes" class="tab-item {{ 'active' if tab == 'notes' }}">Notes{% if counts.notes %} ({{ counts.notes }}){% endif %}</a>
<a href="/projects/{{ item.id }}?tab=links" class="tab-item {{ 'active' if tab == 'links' }}">Links{% if counts.links %} ({{ counts.links }}){% endif %}</a>
<a href="/projects/{{ item.id }}?tab=files" class="tab-item {{ 'active' if tab == 'files' }}">Files{% if counts.files %} ({{ counts.files }}){% endif %}</a>
<a href="/projects/{{ item.id }}?tab=lists" class="tab-item {{ 'active' if tab == 'lists' }}">Lists{% if counts.lists %} ({{ counts.lists }}){% endif %}</a>
<a href="/projects/{{ item.id }}?tab=decisions" class="tab-item {{ 'active' if tab == 'decisions' }}">Decisions{% if counts.decisions %} ({{ counts.decisions }}){% endif %}</a>
<a href="/projects/{{ item.id }}?tab=meetings" class="tab-item {{ 'active' if tab == 'meetings' }}">Meetings{% if counts.meetings %} ({{ counts.meetings }}){% endif %}</a>
<a href="/projects/{{ item.id }}?tab=processes" class="tab-item {{ 'active' if tab == 'processes' }}">Processes</a>
<a href="/projects/{{ item.id }}?tab=contacts" class="tab-item {{ 'active' if tab == 'contacts' }}">Contacts{% if counts.contacts %} ({{ counts.contacts }}){% endif %}</a>
</div>
{% if tab == 'tasks' %}
<form class="quick-add" action="/tasks/quick-add" method="post">
<input type="hidden" name="domain_id" value="{{ item.domain_id }}">
<input type="hidden" name="project_id" value="{{ item.id }}">
<input type="text" name="title" placeholder="Quick add task to this project..." required>
<button type="submit" class="btn btn-primary btn-sm">Add</button>
</form>
<a href="/tasks/create?domain_id={{ item.domain_id }}&project_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ Full Task Form</a>
{% for t in tasks %}
<div class="list-row {{ 'completed' if t.status == 'done' }}">
<div class="row-check">
<form action="/tasks/{{ t.id }}/toggle" method="post" style="display:inline">
<input type="checkbox" id="pt-{{ t.id }}" {{ 'checked' if t.status == 'done' }} onchange="this.form.submit()">
<label for="pt-{{ t.id }}"></label>
</form>
</div>
<span class="priority-dot priority-{{ t.priority }}"></span>
<span class="row-title"><a href="/tasks/{{ t.id }}">{{ t.title }}</a></span>
{% if t.due_date %}<span class="row-meta">{{ t.due_date }}</span>{% endif %}
<span class="status-badge status-{{ t.status }}">{{ t.status|replace('_', ' ') }}</span>
<div class="row-actions">
<a href="/tasks/{{ t.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
</div>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No tasks yet</div></div>
{% endfor %}
{% elif tab == 'notes' %}
<a href="/notes/create?domain_id={{ item.domain_id }}&project_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New Note</a>
{% for n in notes %}
<div class="list-row">
<span class="row-title"><a href="/notes/{{ n.id }}">{{ n.title }}</a></span>
<span class="row-meta">{{ n.updated_at.strftime('%Y-%m-%d') if n.updated_at else '' }}</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No notes yet</div></div>
{% endfor %}
{% elif tab == 'links' %}
<a href="/links/create?domain_id={{ item.domain_id }}&project_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New Link</a>
{% for l in links %}
<div class="list-row">
<span class="row-title"><a href="{{ l.url }}" target="_blank">{{ l.label }}</a></span>
<span class="row-meta">{{ l.url[:50] }}{% if l.url|length > 50 %}...{% endif %}</span>
<div class="row-actions">
<a href="/links/{{ l.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
</div>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No links yet</div></div>
{% endfor %}
{% elif tab == 'files' %}
<a href="/files/upload?context_type=project&context_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ Upload File</a>
{% for f in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/files/{{ f.id }}">{{ f.original_filename }}</a></span>
<span class="row-meta">{{ f.created_at.strftime('%Y-%m-%d') if f.created_at else '' }}</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No files attached to this project</div></div>
{% endfor %}
{% elif tab == 'lists' %}
<a href="/lists/create?domain_id={{ item.domain_id }}&project_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New List</a>
{% for l in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/lists/{{ l.id }}">{{ l.name }}</a></span>
<span class="row-meta">{{ l.item_count }} items</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No lists linked to this project</div></div>
{% endfor %}
{% elif tab == 'decisions' %}
<a href="/decisions/create?project_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New Decision</a>
{% for d in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/decisions/{{ d.id }}">{{ d.title }}</a></span>
<span class="status-badge status-{{ d.status }}">{{ d.status }}</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No decisions linked to this project</div></div>
{% endfor %}
{% elif tab == 'meetings' %}
<a href="/meetings/create" class="btn btn-ghost btn-sm mb-3">+ New Meeting</a>
<div class="card mb-4">
<form action="/projects/{{ item.id }}/meetings/add" method="post" class="flex gap-2 items-end" style="padding: 12px;">
<div class="form-group" style="flex:1; margin:0;">
<label class="form-label">Meeting</label>
<select name="meeting_id" class="form-select" required>
<option value="">Select meeting...</option>
{% for m in all_meetings %}
<option value="{{ m.id }}">{{ m.title }} ({{ m.meeting_date }})</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary btn-sm">Add</button>
</form>
</div>
{% for m in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/meetings/{{ m.id }}">{{ m.title }}</a></span>
<span class="row-meta">{{ m.meeting_date }}</span>
<span class="status-badge status-{{ m.status }}">{{ m.status }}</span>
<div class="row-actions">
<form action="/projects/{{ item.id }}/meetings/{{ m.id }}/remove" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs" title="Remove">Remove</button>
</form>
</div>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No meetings linked to this project</div></div>
{% endfor %}
{% elif tab == 'processes' %}
<div class="empty-state"><div class="empty-state-text">Process management coming soon</div></div>
{% elif tab == 'contacts' %}
<div class="card mb-4">
<form action="/projects/{{ item.id }}/contacts/add" method="post" class="flex gap-2 items-end" style="padding: 12px;">
<div class="form-group" style="flex:1; margin:0;">
<label class="form-label">Contact</label>
<select name="contact_id" class="form-select" required>
<option value="">Select contact...</option>
{% for c in all_contacts %}
<option value="{{ c.id }}">{{ c.first_name }} {{ c.last_name or '' }}</option>
{% endfor %}
</select>
</div>
<div class="form-group" style="flex:1; margin:0;">
<label class="form-label">Role</label>
<input type="text" name="role" class="form-input" placeholder="e.g. lead, contributor...">
</div>
<button type="submit" class="btn btn-primary btn-sm">Add</button>
</form>
</div>
{% for c in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/contacts/{{ c.id }}">{{ c.first_name }} {{ c.last_name or '' }}</a></span>
{% if c.role %}<span class="row-tag">{{ c.role }}</span>{% endif %}
<span class="row-meta">{{ c.linked_at.strftime('%Y-%m-%d') if c.linked_at else '' }}</span>
<div class="row-actions">
<form action="/projects/{{ item.id }}/contacts/{{ c.id }}/remove" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs" title="Remove">Remove</button>
</form>
</div>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No contacts linked to this project</div></div>
{% endfor %}
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,69 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb"><a href="/projects">Projects</a><span class="sep">/</span><span>{{ 'Edit' if item else 'New Project' }}</span></div>
<div class="page-header"><h1 class="page-title">{{ 'Edit Project' if item else 'New Project' }}</h1></div>
<div class="card">
<form method="post" action="{{ '/projects/' ~ item.id ~ '/edit' if item else '/projects/create' }}">
<div class="form-grid">
<div class="form-group full-width">
<label class="form-label">Name *</label>
<input type="text" name="name" class="form-input" required value="{{ item.name if item else '' }}">
</div>
<div class="form-group">
<label class="form-label">Domain *</label>
<select name="domain_id" class="form-select" required>
{% for d in domains %}
<option value="{{ d.id }}" {{ 'selected' if (item and item.domain_id|string == d.id|string) or (not item and prefill_domain_id == d.id|string) }}>{{ d.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Area</label>
<select name="area_id" class="form-select">
<option value="">-- None --</option>
{% for a in areas %}
<option value="{{ a.id }}" {{ 'selected' if (item and item.area_id and item.area_id|string == a.id|string) or (not item and prefill_area_id == a.id|string) }}>{{ a.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Status</label>
<select name="status" class="form-select">
{% for s in ['active', 'on_hold', 'completed', 'archived'] %}
<option value="{{ s }}" {{ 'selected' if item and item.status == s }}>{{ s|replace('_', ' ')|title }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Priority</label>
<select name="priority" class="form-select">
<option value="1" {{ 'selected' if item and item.priority == 1 }}>Critical</option>
<option value="2" {{ 'selected' if item and item.priority == 2 }}>High</option>
<option value="3" {{ 'selected' if (item and item.priority == 3) or not item }}>Normal</option>
<option value="4" {{ 'selected' if item and item.priority == 4 }}>Low</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Start Date</label>
<input type="date" name="start_date" class="form-input" value="{{ item.start_date if item and item.start_date else '' }}">
</div>
<div class="form-group">
<label class="form-label">Target Date</label>
<input type="date" name="target_date" class="form-input" value="{{ item.target_date if item and item.target_date else '' }}">
</div>
<div class="form-group full-width">
<label class="form-label">Description</label>
<textarea name="description" class="form-textarea">{{ item.description if item and item.description else '' }}</textarea>
</div>
<div class="form-group full-width">
<label class="form-label">Tags (comma-separated)</label>
<input type="text" name="tags" class="form-input" value="{{ item.tags|join(', ') if item and item.tags else '' }}">
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save Changes' if item else 'Create Project' }}</button>
<a href="{{ '/projects/' ~ item.id if item else '/projects' }}" class="btn btn-secondary">Cancel</a>
</div>
</div>
</form>
</div>
{% endblock %}

48
templates/projects.html Normal file
View File

@@ -0,0 +1,48 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Projects<span class="page-count">{{ items|length }}</span></h1>
<a href="/projects/create" class="btn btn-primary">+ New Project</a>
</div>
<form class="filters-bar" method="get" action="/projects">
<select name="domain_id" class="filter-select" onchange="this.form.submit()">
<option value="">All Domains</option>
{% for d in domains %}
<option value="{{ d.id }}" {{ 'selected' if current_domain_id == d.id|string }}>{{ d.name }}</option>
{% endfor %}
</select>
<select name="status" class="filter-select" onchange="this.form.submit()">
<option value="">All Statuses</option>
{% for s in ['active', 'on_hold', 'completed', 'archived'] %}
<option value="{{ s }}" {{ 'selected' if current_status == s }}>{{ s|replace('_', ' ')|title }}</option>
{% endfor %}
</select>
</form>
{% if items %}
<div class="card">
{% for item in items %}
<div class="list-row">
<span class="row-domain-tag" style="background: {{ item.domain_color or '#4F6EF7' }}22; color: {{ item.domain_color or '#4F6EF7' }}">{{ item.domain_name }}</span>
<span class="row-title"><a href="/projects/{{ item.id }}">{{ item.name }}</a></span>
{% if item.area_name %}<span class="row-meta">{{ item.area_name }}</span>{% endif %}
<span class="status-badge status-{{ item.status }}">{{ item.status|replace('_', ' ') }}</span>
<div style="width: 80px;">
<div class="progress-bar"><div class="progress-fill" style="width: {{ item.progress }}%"></div></div>
<div class="progress-text">{{ item.done_count }}/{{ item.task_count }}</div>
</div>
<div class="row-actions">
<a href="/projects/{{ item.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state">
<div class="empty-state-icon">&#128194;</div>
<div class="empty-state-text">No projects found</div>
<a href="/projects/create" class="btn btn-primary">Create First Project</a>
</div>
{% endif %}
{% endblock %}

37
templates/search.html Normal file
View File

@@ -0,0 +1,37 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Search</h1>
</div>
<form class="search-page-form" method="get" action="/search">
<input type="text" name="q" value="{{ query }}" class="form-input" placeholder="Search tasks, projects, notes, contacts..." autofocus style="max-width: 600px;">
<button type="submit" class="btn btn-primary">Search</button>
</form>
{% if query %}
<p class="text-muted mt-2">{{ results|length }} result{{ 's' if results|length != 1 }} for "{{ query }}"</p>
{% endif %}
{% if results %}
<div class="card mt-3">
{% for item in results %}
<div class="list-row">
<span class="search-type-badge search-type-{{ item.type }}">{{ item.type_label }}</span>
<span class="row-title"><a href="{{ item.url }}">{{ item.name }}</a></span>
{% if item.context %}
<span class="row-meta">{{ item.context }}</span>
{% endif %}
{% if item.status %}
<span class="status-badge status-{{ item.status }}">{{ item.status|replace('_', ' ') }}</span>
{% endif %}
</div>
{% endfor %}
</div>
{% elif query %}
<div class="empty-state mt-3">
<div class="empty-state-icon">&#128269;</div>
<div class="empty-state-text">No results found for "{{ query }}"</div>
</div>
{% endif %}
{% endblock %}

196
templates/task_detail.html Normal file
View File

@@ -0,0 +1,196 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
{% if domain %}<a href="/tasks?domain_id={{ item.domain_id }}">{{ domain.name }}</a><span class="sep">/</span>{% endif %}
{% if project %}<a href="/projects/{{ project.id }}">{{ project.name }}</a><span class="sep">/</span>{% endif %}
<span>{{ item.title }}</span>
</div>
<div class="detail-header">
<div class="flex items-center justify-between">
<h1 class="detail-title">{{ item.title }}</h1>
<div class="flex gap-2">
{% if item.status not in ['done', 'cancelled'] %}
{% if running_task_id and item.id|string == running_task_id %}
<form action="/time/stop" method="post" style="display:inline">
<button class="btn btn-sm timer-detail-btn timer-detail-stop" title="Stop timer">&#9632; Stop Timer</button>
</form>
{% else %}
<form action="/time/start" method="post" style="display:inline">
<input type="hidden" name="task_id" value="{{ item.id }}">
<button class="btn btn-sm timer-detail-btn timer-detail-play" title="Start timer">&#9654; Start Timer</button>
</form>
{% endif %}
{% endif %}
<a href="/tasks/{{ item.id }}/edit" class="btn btn-secondary btn-sm">Edit</a>
<form action="/tasks/{{ item.id }}/toggle" method="post" style="display:inline">
<button class="btn {{ 'btn-secondary' if item.status == 'done' else 'btn-primary' }} btn-sm">
{{ 'Reopen' if item.status == 'done' else 'Complete' }}
</button>
</form>
</div>
</div>
<div class="detail-meta mt-2">
<span class="status-badge status-{{ item.status }}">{{ item.status|replace('_', ' ') }}</span>
<span class="detail-meta-item"><span class="priority-dot priority-{{ item.priority }}"></span> P{{ item.priority }}</span>
{% if domain %}<span class="row-domain-tag" style="background: {{ domain.color or '#4F6EF7' }}22; color: {{ domain.color or '#4F6EF7' }}">{{ domain.name }}</span>{% endif %}
{% if project %}<span class="row-tag">{{ project.name }}</span>{% endif %}
{% if item.due_date %}<span class="detail-meta-item">Due: {{ item.due_date }}</span>{% endif %}
{% if item.context %}<span class="detail-meta-item">@{{ item.context }}</span>{% endif %}
{% if item.estimated_minutes %}<span class="detail-meta-item">~{{ item.estimated_minutes }}min</span>{% endif %}
{% if item.energy_required %}<span class="detail-meta-item">Energy: {{ item.energy_required }}</span>{% endif %}
</div>
</div>
{% if item.description %}
<div class="card mb-4">
<div class="detail-body">{{ item.description }}</div>
</div>
{% endif %}
{% if item.tags %}
<div class="flex gap-2 mb-4">
{% for tag in item.tags %}<span class="row-tag">{{ tag }}</span>{% endfor %}
</div>
{% endif %}
<!-- Tabs -->
<div class="tab-strip">
<a href="/tasks/{{ item.id }}?tab=overview" class="tab-item {{ 'active' if tab == 'overview' }}">Overview{% if counts.overview %} ({{ counts.overview }}){% endif %}</a>
<a href="/tasks/{{ item.id }}?tab=notes" class="tab-item {{ 'active' if tab == 'notes' }}">Notes{% if counts.notes %} ({{ counts.notes }}){% endif %}</a>
<a href="/tasks/{{ item.id }}?tab=links" class="tab-item {{ 'active' if tab == 'links' }}">Links{% if counts.links %} ({{ counts.links }}){% endif %}</a>
<a href="/tasks/{{ item.id }}?tab=files" class="tab-item {{ 'active' if tab == 'files' }}">Files{% if counts.files %} ({{ counts.files }}){% endif %}</a>
<a href="/tasks/{{ item.id }}?tab=lists" class="tab-item {{ 'active' if tab == 'lists' }}">Lists{% if counts.lists %} ({{ counts.lists }}){% endif %}</a>
<a href="/tasks/{{ item.id }}?tab=decisions" class="tab-item {{ 'active' if tab == 'decisions' }}">Decisions{% if counts.decisions %} ({{ counts.decisions }}){% endif %}</a>
<a href="/tasks/{{ item.id }}?tab=processes" class="tab-item {{ 'active' if tab == 'processes' }}">Processes</a>
<a href="/tasks/{{ item.id }}?tab=contacts" class="tab-item {{ 'active' if tab == 'contacts' }}">Contacts{% if counts.contacts %} ({{ counts.contacts }}){% endif %}</a>
</div>
{% if tab == 'overview' %}
{% if parent %}
<div class="card mb-4">
<div class="card-title text-sm">Parent Task</div>
<a href="/tasks/{{ parent.id }}">{{ parent.title }}</a>
</div>
{% endif %}
<div class="card">
<div class="card-header">
<h2 class="card-title">Subtasks<span class="page-count">{{ subtasks|length }}</span></h2>
<a href="/tasks/create?parent_id={{ item.id }}&domain_id={{ item.domain_id }}&project_id={{ item.project_id or '' }}" class="btn btn-ghost btn-sm">+ Add Subtask</a>
</div>
{% for sub in subtasks %}
<div class="list-row {{ 'completed' if sub.status == 'done' }}">
<div class="row-check">
<form action="/tasks/{{ sub.id }}/toggle" method="post" style="display:inline">
<input type="checkbox" id="sub-{{ sub.id }}" {{ 'checked' if sub.status == 'done' }} onchange="this.form.submit()">
<label for="sub-{{ sub.id }}"></label>
</form>
</div>
<span class="priority-dot priority-{{ sub.priority }}"></span>
<span class="row-title"><a href="/tasks/{{ sub.id }}">{{ sub.title }}</a></span>
<span class="status-badge status-{{ sub.status }}">{{ sub.status|replace('_', ' ') }}</span>
</div>
{% else %}
<div class="text-sm text-muted" style="padding: 12px;">No subtasks</div>
{% endfor %}
</div>
{% elif tab == 'notes' %}
<a href="/notes/create?task_id={{ item.id }}&domain_id={{ item.domain_id }}" class="btn btn-ghost btn-sm mb-3">+ New Note</a>
{% for n in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/notes/{{ n.id }}">{{ n.title }}</a></span>
<span class="row-meta">{{ n.updated_at.strftime('%Y-%m-%d') if n.updated_at else '' }}</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No notes linked to this task</div></div>
{% endfor %}
{% elif tab == 'links' %}
<a href="/links/create?task_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New Link</a>
{% for w in tab_data %}
<div class="list-row">
<span class="row-title"><a href="{{ w.url }}" target="_blank">{{ w.label }}</a></span>
<span class="row-meta">{{ w.url[:50] }}{% if w.url|length > 50 %}...{% endif %}</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No links linked to this task</div></div>
{% endfor %}
{% elif tab == 'files' %}
<a href="/files/upload?context_type=task&context_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ Upload File</a>
{% for f in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/files/{{ f.id }}">{{ f.original_filename }}</a></span>
<span class="row-meta">{{ f.created_at.strftime('%Y-%m-%d') if f.created_at else '' }}</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No files attached to this task</div></div>
{% endfor %}
{% elif tab == 'lists' %}
<a href="/lists/create?task_id={{ item.id }}&domain_id={{ item.domain_id }}" class="btn btn-ghost btn-sm mb-3">+ New List</a>
{% for l in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/lists/{{ l.id }}">{{ l.name }}</a></span>
<span class="row-meta">{{ l.item_count }} items</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No lists linked to this task</div></div>
{% endfor %}
{% elif tab == 'decisions' %}
<a href="/decisions/create?task_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New Decision</a>
{% for d in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/decisions/{{ d.id }}">{{ d.title }}</a></span>
<span class="status-badge status-{{ d.status }}">{{ d.status }}</span>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No decisions linked to this task</div></div>
{% endfor %}
{% elif tab == 'processes' %}
<div class="empty-state"><div class="empty-state-text">Process management coming soon</div></div>
{% elif tab == 'contacts' %}
<div class="card mb-4">
<form action="/tasks/{{ item.id }}/contacts/add" method="post" class="flex gap-2 items-end" style="padding: 12px;">
<div class="form-group" style="flex:1; margin:0;">
<label class="form-label">Contact</label>
<select name="contact_id" class="form-select" required>
<option value="">Select contact...</option>
{% for c in all_contacts %}
<option value="{{ c.id }}">{{ c.first_name }} {{ c.last_name or '' }}</option>
{% endfor %}
</select>
</div>
<div class="form-group" style="flex:1; margin:0;">
<label class="form-label">Role</label>
<input type="text" name="role" class="form-input" placeholder="e.g. reviewer, assignee...">
</div>
<button type="submit" class="btn btn-primary btn-sm">Add</button>
</form>
</div>
{% for c in tab_data %}
<div class="list-row">
<span class="row-title"><a href="/contacts/{{ c.id }}">{{ c.first_name }} {{ c.last_name or '' }}</a></span>
{% if c.role %}<span class="row-tag">{{ c.role }}</span>{% endif %}
<span class="row-meta">{{ c.linked_at.strftime('%Y-%m-%d') if c.linked_at else '' }}</span>
<div class="row-actions">
<form action="/tasks/{{ item.id }}/contacts/{{ c.id }}/remove" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs" title="Remove">Remove</button>
</form>
</div>
</div>
{% else %}
<div class="empty-state"><div class="empty-state-text">No contacts linked to this task</div></div>
{% endfor %}
{% endif %}
<div class="text-xs text-muted mt-4">
Created {{ item.created_at.strftime('%Y-%m-%d %H:%M') if item.created_at else '' }}
{% if item.completed_at %} | Completed {{ item.completed_at.strftime('%Y-%m-%d %H:%M') }}{% endif %}
</div>
{% endblock %}

119
templates/task_form.html Normal file
View File

@@ -0,0 +1,119 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
<a href="/tasks">Tasks</a>
<span class="sep">/</span>
<span>{{ 'Edit' if item else 'New Task' }}</span>
</div>
<div class="page-header">
<h1 class="page-title">{{ 'Edit Task' if item else 'New Task' }}</h1>
</div>
<div class="card">
<form method="post" action="{{ '/tasks/' ~ item.id ~ '/edit' if item else '/tasks/create' }}">
<div class="form-grid">
<div class="form-group full-width">
<label class="form-label">Title *</label>
<input type="text" name="title" class="form-input" required
value="{{ item.title if item else '' }}">
</div>
<div class="form-group">
<label class="form-label">Domain *</label>
<select name="domain_id" class="form-select" required>
{% for d in domains %}
<option value="{{ d.id }}"
{{ 'selected' if (item and item.domain_id|string == d.id|string) or (not item and prefill_domain_id == d.id|string) }}>
{{ d.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Project</label>
<select name="project_id" class="form-select">
<option value="">-- No Project --</option>
{% for p in projects %}
<option value="{{ p.id }}"
{{ 'selected' if (item and item.project_id and item.project_id|string == p.id|string) or (not item and prefill_project_id == p.id|string) }}>
{{ p.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Priority</label>
<select name="priority" class="form-select">
<option value="1" {{ 'selected' if item and item.priority == 1 }}>1 - Critical</option>
<option value="2" {{ 'selected' if item and item.priority == 2 }}>2 - High</option>
<option value="3" {{ 'selected' if (item and item.priority == 3) or not item }}>3 - Normal</option>
<option value="4" {{ 'selected' if item and item.priority == 4 }}>4 - Low</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Status</label>
<select name="status" class="form-select">
{% for s in ['open', 'in_progress', 'blocked', 'done', 'cancelled'] %}
<option value="{{ s }}" {{ 'selected' if item and item.status == s }}>{{ s|replace('_', ' ')|title }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Due Date</label>
<input type="date" name="due_date" class="form-input"
value="{{ item.due_date if item and item.due_date else '' }}">
</div>
<div class="form-group">
<label class="form-label">Context</label>
<select name="context" class="form-select">
<option value="">-- None --</option>
{% for ct in context_types %}
<option value="{{ ct.value }}"
{{ 'selected' if item and item.context == ct.value }}>{{ ct.label }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Estimated Minutes</label>
<input type="number" name="estimated_minutes" class="form-input" min="0"
value="{{ item.estimated_minutes if item and item.estimated_minutes else '' }}">
</div>
<div class="form-group">
<label class="form-label">Energy Required</label>
<select name="energy_required" class="form-select">
<option value="">-- None --</option>
<option value="high" {{ 'selected' if item and item.energy_required == 'high' }}>High</option>
<option value="medium" {{ 'selected' if item and item.energy_required == 'medium' }}>Medium</option>
<option value="low" {{ 'selected' if item and item.energy_required == 'low' }}>Low</option>
</select>
</div>
<div class="form-group full-width">
<label class="form-label">Description</label>
<textarea name="description" class="form-textarea">{{ item.description if item and item.description else '' }}</textarea>
</div>
<div class="form-group full-width">
<label class="form-label">Tags (comma-separated)</label>
<input type="text" name="tags" class="form-input"
value="{{ item.tags|join(', ') if item and item.tags else '' }}">
</div>
{% if prefill_parent_id %}
<input type="hidden" name="parent_id" value="{{ prefill_parent_id }}">
{% endif %}
<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save Changes' if item else 'Create Task' }}</button>
<a href="{{ '/tasks/' ~ item.id if item else '/tasks' }}" class="btn btn-secondary">Cancel</a>
</div>
</div>
</form>
</div>
{% endblock %}

105
templates/tasks.html Normal file
View File

@@ -0,0 +1,105 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">All Tasks<span class="page-count">{{ items|length }}</span></h1>
<a href="/tasks/create" class="btn btn-primary">+ New Task</a>
</div>
<!-- Quick Add -->
<form class="quick-add" action="/tasks/quick-add" method="post">
<input type="text" name="title" placeholder="Quick add task..." required>
<button type="submit" class="btn btn-primary btn-sm">Add</button>
</form>
<!-- Filters -->
<form class="filters-bar" method="get" action="/tasks">
<select name="status" class="filter-select" data-auto-submit onchange="this.form.submit()">
<option value="">All Statuses</option>
<option value="open" {{ 'selected' if current_status == 'open' }}>Open</option>
<option value="in_progress" {{ 'selected' if current_status == 'in_progress' }}>In Progress</option>
<option value="blocked" {{ 'selected' if current_status == 'blocked' }}>Blocked</option>
<option value="done" {{ 'selected' if current_status == 'done' }}>Done</option>
</select>
<select name="priority" class="filter-select" onchange="this.form.submit()">
<option value="">All Priorities</option>
<option value="1" {{ 'selected' if current_priority == '1' }}>Critical</option>
<option value="2" {{ 'selected' if current_priority == '2' }}>High</option>
<option value="3" {{ 'selected' if current_priority == '3' }}>Normal</option>
<option value="4" {{ 'selected' if current_priority == '4' }}>Low</option>
</select>
<select name="domain_id" class="filter-select" onchange="this.form.submit()">
<option value="">All Domains</option>
{% for d in domains %}
<option value="{{ d.id }}" {{ 'selected' if current_domain_id == d.id|string }}>{{ d.name }}</option>
{% endfor %}
</select>
<select name="project_id" class="filter-select" onchange="this.form.submit()">
<option value="">All Projects</option>
{% for p in projects %}
<option value="{{ p.id }}" {{ 'selected' if current_project_id == p.id|string }}>{{ p.name }}</option>
{% endfor %}
</select>
<select name="sort" class="filter-select" onchange="this.form.submit()">
<option value="sort_order" {{ 'selected' if current_sort == 'sort_order' }}>Manual Order</option>
<option value="priority" {{ 'selected' if current_sort == 'priority' }}>Priority</option>
<option value="due_date" {{ 'selected' if current_sort == 'due_date' }}>Due Date</option>
<option value="created_at" {{ 'selected' if current_sort == 'created_at' }}>Newest</option>
<option value="title" {{ 'selected' if current_sort == 'title' }}>Title</option>
</select>
</form>
<!-- Task List -->
{% if items %}
<div class="card">
{% for item in items %}
<div class="list-row {{ 'completed' if item.status in ['done', 'cancelled'] }} {{ 'timer-active' if running_task_id and item.id|string == running_task_id }}">
<div class="row-check">
<form action="/tasks/{{ item.id }}/toggle" method="post" style="display:inline">
<input type="checkbox" id="check-{{ item.id }}" {{ 'checked' if item.status == 'done' }}
onchange="this.form.submit()">
<label for="check-{{ item.id }}"></label>
</form>
</div>
{% if item.status not in ['done', 'cancelled'] %}
<div class="row-timer">
{% if running_task_id and item.id|string == running_task_id %}
<form action="/time/stop" method="post" style="display:inline">
<button type="submit" class="timer-btn timer-btn-stop" title="Stop timer">&#9632;</button>
</form>
{% else %}
<form action="/time/start" method="post" style="display:inline">
<input type="hidden" name="task_id" value="{{ item.id }}">
<button type="submit" class="timer-btn timer-btn-play" title="Start timer">&#9654;</button>
</form>
{% endif %}
</div>
{% endif %}
<span class="priority-dot priority-{{ item.priority }}"></span>
<span class="row-title"><a href="/tasks/{{ item.id }}">{{ item.title }}</a></span>
{% if item.project_name %}
<span class="row-tag">{{ item.project_name }}</span>
{% endif %}
{% if item.domain_name %}
<span class="row-domain-tag" style="background: {{ item.domain_color or '#4F6EF7' }}22; color: {{ item.domain_color or '#4F6EF7' }}">{{ item.domain_name }}</span>
{% endif %}
{% if item.due_date %}
<span class="row-meta {{ 'overdue' if item.due_date|string < now_date|default('9999') }}">{{ item.due_date }}</span>
{% endif %}
<span class="status-badge status-{{ item.status }}">{{ item.status|replace('_', ' ') }}</span>
<div class="row-actions">
<a href="/tasks/{{ item.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form action="/tasks/{{ item.id }}/delete" method="post" data-confirm="Delete this task?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Del</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state">
<div class="empty-state-icon">&#9745;</div>
<div class="empty-state-text">No tasks found</div>
<a href="/tasks/create" class="btn btn-primary">Create First Task</a>
</div>
{% endif %}
{% 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 %}

155
templates/time_entries.html Normal file
View File

@@ -0,0 +1,155 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<div>
<h1 class="page-title">Time Log</h1>
<div class="text-muted text-sm mt-1">
{{ entries | length }} entries
&middot; {{ (total_minutes / 60) | round(1) }}h total
(last {{ days }} days)
</div>
</div>
</div>
<!-- Running timer banner -->
{% if running %}
<div class="card mb-3" style="border-color: var(--green); background: var(--green-soft);">
<div style="display: flex; align-items: center; gap: 12px;">
<div style="width: 10px; height: 10px; border-radius: 50%; background: var(--green); animation: pulse 1.5s infinite;"></div>
<div style="flex: 1;">
<div style="font-weight: 600; color: var(--text);">
Timer running: {{ running.task_title }}
</div>
{% if running.project_name %}
<div class="text-muted text-sm">{{ running.project_name }}</div>
{% endif %}
</div>
<div style="font-size: 1.25rem; font-weight: 700; font-family: var(--font-mono); color: var(--green);"
id="running-banner-elapsed" data-start="{{ running.start_at.isoformat() }}">
--:--
</div>
<form method="POST" action="/time/stop">
<input type="hidden" name="entry_id" value="{{ running.id }}">
<button type="submit" class="btn btn-danger btn-sm">Stop</button>
</form>
</div>
</div>
{% endif %}
<!-- Filters -->
<div class="filters-bar">
<a href="/time?days=1" class="btn {{ 'btn-primary' if days == 1 else 'btn-secondary' }} btn-sm">Today</a>
<a href="/time?days=7" class="btn {{ 'btn-primary' if days == 7 else 'btn-secondary' }} btn-sm">7 Days</a>
<a href="/time?days=30" class="btn {{ 'btn-primary' if days == 30 else 'btn-secondary' }} btn-sm">30 Days</a>
<a href="/time?days=90" class="btn {{ 'btn-primary' if days == 90 else 'btn-secondary' }} btn-sm">90 Days</a>
</div>
<!-- Daily totals summary -->
{% if daily_totals %}
<div class="card mb-3">
<div class="card-title mb-2">Daily Summary</div>
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
{% for day, mins in daily_totals | dictsort(reverse=true) %}
<div style="text-align: center; padding: 8px 14px; background: var(--surface2); border-radius: var(--radius); min-width: 80px;">
<div style="font-size: 1.1rem; font-weight: 700;">{{ (mins / 60) | round(1) }}h</div>
<div class="text-muted text-xs">{{ day }}</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Time entries -->
{% if entries %}
<div class="card">
{% set current_date = namespace(value='') %}
{% for entry in entries %}
{% set entry_date = entry.start_at.strftime('%A, %B %-d') if entry.start_at else 'Unknown' %}
{% if entry_date != current_date.value %}
<div style="padding: 10px 12px 4px; font-size: 0.78rem; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; {% if not loop.first %}border-top: 1px solid var(--border); margin-top: 4px;{% endif %}">
{{ entry_date }}
</div>
{% set current_date.value = entry_date %}
{% endif %}
<div class="list-row">
{% if entry.end_at is none %}
<div style="width: 10px; height: 10px; border-radius: 50%; background: var(--green); animation: pulse 1.5s infinite; flex-shrink: 0;"></div>
{% endif %}
<div class="row-title">
<a href="/tasks/{{ entry.task_id }}">{{ entry.task_title }}</a>
</div>
{% if entry.project_name %}
<span class="row-tag">{{ entry.project_name }}</span>
{% endif %}
{% if entry.domain_name %}
<span class="row-domain-tag" style="background: {{ entry.domain_color or 'var(--accent)' }}20; color: {{ entry.domain_color or 'var(--accent)' }};">
{{ entry.domain_name }}
</span>
{% endif %}
<span style="font-family: var(--font-mono); font-size: 0.82rem; color: var(--text-secondary); min-width: 50px; text-align: right;">
{% if entry.end_at is none %}
<span style="color: var(--green);">running</span>
{% elif entry.duration_minutes %}
{% if entry.duration_minutes >= 60 %}
{{ (entry.duration_minutes / 60) | int }}h {{ entry.duration_minutes % 60 }}m
{% else %}
{{ entry.duration_minutes }}m
{% endif %}
{% else %}
--
{% endif %}
</span>
<span class="row-meta" style="min-width: 100px; text-align: right;">
{% if entry.start_at %}
{{ entry.start_at.strftime('%-I:%M %p') }}
{% if entry.end_at %}
- {{ entry.end_at.strftime('%-I:%M %p') }}
{% endif %}
{% endif %}
</span>
<div class="row-actions">
<form method="POST" action="/time/{{ entry.id }}/delete" data-confirm="Delete this time entry?">
<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 entries in the last {{ days }} days</div>
<div class="text-muted text-sm">Start a timer from any task to begin tracking time</div>
</div>
{% endif %}
<style>
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
</style>
<script>
// Update running banner elapsed time
(function() {
const el = document.getElementById('running-banner-elapsed');
if (!el) return;
const startAt = new Date(el.dataset.start);
function update() {
const now = new Date();
const secs = Math.floor((now - startAt) / 1000);
const h = Math.floor(secs / 3600);
const m = Math.floor((secs % 3600) / 60);
const s = secs % 60;
el.textContent = (h > 0 ? h + ':' : '') +
String(m).padStart(2, '0') + ':' +
String(s).padStart(2, '0');
}
update();
setInterval(update, 1000);
})();
</script>
{% endblock %}

54
templates/trash.html Normal file
View File

@@ -0,0 +1,54 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Trash<span class="page-count">{{ total_deleted }}</span></h1>
{% if total_deleted > 0 %}
<form action="/admin/trash/empty" method="post" data-confirm="Permanently delete ALL {{ total_deleted }} items? This cannot be undone." style="display:inline">
<button type="submit" class="btn btn-danger">Empty Trash</button>
</form>
{% endif %}
</div>
<!-- Type filter -->
<div class="filters-bar">
<a href="/admin/trash" class="btn {{ 'btn-primary' if not current_type else 'btn-secondary' }} btn-sm">
All ({{ total_deleted }})
</a>
{% for entity in trash_entities %}
{% set count = entity_counts.get(entity.table, 0) %}
{% if count > 0 %}
<a href="/admin/trash?entity_type={{ entity.table }}" class="btn {{ 'btn-primary' if current_type == entity.table else 'btn-secondary' }} btn-sm">
{{ entity.label }} ({{ count }})
</a>
{% endif %}
{% endfor %}
</div>
{% if deleted_items %}
<div class="card mt-3">
{% for item in deleted_items %}
<div class="list-row">
<span class="search-type-badge search-type-{{ item.table }}">{{ item.type_label }}</span>
<span class="row-title">{{ item.name }}</span>
{% if item.deleted_at %}
<span class="row-meta">Deleted {{ item.deleted_at.strftime('%Y-%m-%d %H:%M') if item.deleted_at else '' }}</span>
{% endif %}
<div class="row-actions" style="opacity: 1;">
<form action="/admin/trash/{{ item.table }}/{{ item.id }}/restore" method="post" style="display:inline">
<button type="submit" class="btn btn-secondary btn-xs">Restore</button>
</form>
<form action="/admin/trash/{{ item.table }}/{{ item.id }}/permanent-delete" method="post"
data-confirm="Permanently delete '{{ item.name }}'? This cannot be undone." style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Delete Forever</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state mt-3">
<div class="empty-state-icon">&#128465;</div>
<div class="empty-state-text">Trash is empty</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,32 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">{{ page_title }}</h1>
</div>
<div class="card">
<form method="post" action="/weblinks/folders/create">
<div class="form-grid">
<div class="form-group full-width">
<label class="form-label">Folder Name *</label>
<input type="text" name="name" class="form-input" required placeholder="Folder name...">
</div>
<div class="form-group">
<label class="form-label">Parent Folder</label>
<select name="parent_id" class="form-select">
<option value="">None (top-level)</option>
{% for f in parent_folders %}
<option value="{{ f.id }}">{{ f.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Create Folder</button>
<a href="/weblinks" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,52 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">{{ page_title }}</h1>
</div>
<div class="card">
<form method="post" action="{{ '/weblinks/' ~ item.id ~ '/edit' if item else '/weblinks/create' }}">
<div class="form-grid">
<div class="form-group full-width">
<label class="form-label">Label *</label>
<input type="text" name="label" class="form-input" required
value="{{ item.label if item else '' }}" placeholder="Display name...">
</div>
<div class="form-group full-width">
<label class="form-label">URL *</label>
<input type="url" name="url" class="form-input" required
value="{{ item.url if item else '' }}" placeholder="https://...">
</div>
<div class="form-group">
<label class="form-label">Folder</label>
<select name="folder_id" class="form-select">
<option value="">None</option>
{% for f in folders %}
<option value="{{ f.id }}" {{ 'selected' if prefill_folder_id == f.id|string }}>{{ f.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label class="form-label">Tags</label>
<input type="text" name="tags" class="form-input" placeholder="tag1, tag2, ..."
value="{{ item.tags|join(', ') if item and item.tags else '' }}">
</div>
<div class="form-group full-width">
<label class="form-label">Description</label>
<textarea name="description" class="form-textarea" rows="2">{{ item.description if item and item.description else '' }}</textarea>
</div>
</div>
{% if prefill_task_id is defined and prefill_task_id %}<input type="hidden" name="task_id" value="{{ prefill_task_id }}">{% endif %}
{% if prefill_meeting_id is defined and prefill_meeting_id %}<input type="hidden" name="meeting_id" value="{{ prefill_meeting_id }}">{% endif %}
<div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save Changes' if item else 'Add Link' }}</button>
<a href="{{ '/tasks/' ~ prefill_task_id ~ '?tab=links' if prefill_task_id is defined and prefill_task_id else '/weblinks' }}" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
{% endblock %}

105
templates/weblinks.html Normal file
View File

@@ -0,0 +1,105 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1 class="page-title">Bookmarks<span class="page-count">{{ items|length }}</span></h1>
<div class="flex gap-2">
<a href="/weblinks/folders/create" class="btn btn-secondary">+ New Folder</a>
<a href="/weblinks/create{{ '?folder_id=' ~ current_folder_id if current_folder_id }}" class="btn btn-primary">+ New Link</a>
</div>
</div>
<div class="weblinks-layout">
<!-- Folder sidebar -->
<div class="weblinks-folders">
<a href="/weblinks" class="weblink-folder-item {{ 'active' if not current_folder_id }}">
All Links
</a>
{% for folder in top_folders %}
<a href="/weblinks?folder_id={{ folder.id }}" class="weblink-folder-item {{ 'active' if current_folder_id == folder.id|string }}">
{{ folder.name }}
{% if folder.link_count %}<span class="badge" style="margin-left: auto;">{{ folder.link_count }}</span>{% endif %}
</a>
{% for child in child_folder_map.get(folder.id|string, []) %}
<a href="/weblinks?folder_id={{ child.id }}" class="weblink-folder-item {{ 'active' if current_folder_id == child.id|string }}" style="padding-left: 28px;">
{{ child.name }}
{% if child.link_count %}<span class="badge" style="margin-left: auto;">{{ child.link_count }}</span>{% endif %}
</a>
{% endfor %}
{% endfor %}
</div>
<!-- Links list -->
<div class="weblinks-content">
{% if current_folder %}
<div class="flex items-center justify-between mb-2">
<h2 style="font-size: 1rem; font-weight: 600;">{{ current_folder.name }}</h2>
<form action="/weblinks/folders/{{ current_folder.id }}/delete" method="post"
data-confirm="Delete folder '{{ current_folder.name }}'?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Delete Folder</button>
</form>
</div>
{% if available_links %}
<div class="card" style="margin-bottom: 12px;">
<form action="/weblinks/folders/{{ current_folder.id }}/add-link" method="post"
style="display: flex; gap: 8px; align-items: end; padding: 12px;">
<div class="form-group" style="flex: 1; margin: 0;">
<label class="form-label">Add existing link</label>
<select name="link_id" class="form-select" required>
<option value="">Select link...</option>
{% for l in available_links %}
<option value="{{ l.id }}">{{ l.label }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary btn-sm">Add</button>
</form>
</div>
{% endif %}
{% endif %}
{% if items %}
<div class="card">
{% for item in items %}
<div class="list-row">
<span class="row-title">
<a href="{{ item.url }}" target="_blank" rel="noopener">{{ item.label }}</a>
</span>
<span class="row-meta text-xs" style="max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
{{ item.url }}
</span>
{% if item.tags %}
{% for tag in item.tags %}
<span class="row-tag">{{ tag }}</span>
{% endfor %}
{% endif %}
<div class="row-actions">
{% if current_folder_id %}
<form action="/weblinks/folders/{{ current_folder_id }}/reorder" method="post" style="display:inline">
<input type="hidden" name="link_id" value="{{ item.id }}">
<input type="hidden" name="direction" value="up">
<button type="submit" class="btn btn-ghost btn-xs" title="Move up">&#9650;</button>
</form>
<form action="/weblinks/folders/{{ current_folder_id }}/reorder" method="post" style="display:inline">
<input type="hidden" name="link_id" value="{{ item.id }}">
<input type="hidden" name="direction" value="down">
<button type="submit" class="btn btn-ghost btn-xs" title="Move down">&#9660;</button>
</form>
{% endif %}
<a href="/weblinks/{{ item.id }}/edit" class="btn btn-ghost btn-xs">Edit</a>
<form action="/weblinks/{{ item.id }}/delete" method="post" data-confirm="Delete this link?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red)">Del</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state">
<div class="empty-state-icon">&#128279;</div>
<div class="empty-state-text">No links{{ ' in this folder' if current_folder }} yet</div>
<a href="/weblinks/create{{ '?folder_id=' ~ current_folder_id if current_folder_id }}" class="btn btn-primary">Add Link</a>
</div>
{% endif %}
</div>
</div>
{% endblock %}