various enhancements for new tabs and bug fixes
This commit is contained in:
@@ -131,6 +131,10 @@
|
||||
</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
|
||||
|
||||
@@ -77,9 +77,10 @@
|
||||
</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="{{ '/decisions/' ~ item.id if item else '/decisions' }}" class="btn btn-secondary">Cancel</a>
|
||||
<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>
|
||||
|
||||
@@ -5,6 +5,34 @@
|
||||
<span class="text-muted">{{ total }} open tasks classified by priority & 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">
|
||||
@@ -126,4 +154,31 @@
|
||||
<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 %}
|
||||
|
||||
@@ -37,9 +37,31 @@
|
||||
{% endif %}
|
||||
|
||||
<!-- Add task to focus -->
|
||||
{% if available_tasks %}
|
||||
<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>
|
||||
@@ -52,7 +74,36 @@
|
||||
<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>
|
||||
{% endif %}
|
||||
|
||||
<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
32
templates/history.html
Normal 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 %}
|
||||
@@ -95,4 +95,38 @@
|
||||
<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 %}
|
||||
|
||||
@@ -73,9 +73,11 @@
|
||||
</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="{{ '/lists/' ~ item.id if item else '/lists' }}" class="btn btn-secondary">Cancel</a>
|
||||
<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>
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<form class="filters-bar" method="get" action="/lists">
|
||||
<select name="domain_id" class="filter-select" onchange="this.form.submit()">
|
||||
<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" onchange="this.form.submit()">
|
||||
<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>
|
||||
@@ -57,4 +57,38 @@
|
||||
<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 %}
|
||||
|
||||
@@ -28,9 +28,21 @@
|
||||
{% 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=weblinks" class="tab-item {{ 'active' if tab == 'weblinks' }}">Weblinks{% if counts.weblinks %} ({{ counts.weblinks }}){% 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=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 mt-3">
|
||||
<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>
|
||||
@@ -38,7 +50,7 @@
|
||||
|
||||
<!-- Meeting Notes -->
|
||||
{% if item.notes_body %}
|
||||
<div class="card mt-3">
|
||||
<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>
|
||||
@@ -46,19 +58,17 @@
|
||||
|
||||
<!-- Transcript -->
|
||||
{% if item.transcript %}
|
||||
<div class="card mt-3">
|
||||
<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 mt-3">
|
||||
<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>
|
||||
|
||||
<!-- Quick add action item -->
|
||||
<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;">
|
||||
@@ -68,7 +78,6 @@
|
||||
</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">
|
||||
@@ -83,16 +92,14 @@
|
||||
{% 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 %}
|
||||
|
||||
{% if not action_items %}
|
||||
{% else %}
|
||||
<div style="padding: 16px; color: var(--muted); font-size: 0.85rem;">No action items yet</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Decisions -->
|
||||
{% if decisions %}
|
||||
<div class="card mt-3">
|
||||
<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">
|
||||
@@ -104,16 +111,89 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Attendees -->
|
||||
{% if attendees %}
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><h3 class="card-title">Attendees<span class="page-count">{{ attendees|length }}</span></h3></div>
|
||||
{% for att in attendees %}
|
||||
<div class="list-row">
|
||||
<span class="row-title"><a href="/contacts/{{ att.id }}">{{ att.first_name }} {{ att.last_name or '' }}</a></span>
|
||||
{% if att.role %}<span class="row-tag">{{ att.role }}</span>{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% 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 == 'weblinks' %}
|
||||
<a href="/weblinks/create?meeting_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New Weblink</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 weblinks 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 == '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 %}
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
<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>
|
||||
|
||||
@@ -34,8 +34,13 @@
|
||||
<!-- 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 ({{ notes|length }})</a>
|
||||
<a href="/projects/{{ item.id }}?tab=links" class="tab-item {{ 'active' if tab == 'links' }}">Links ({{ links|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=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' %}
|
||||
@@ -91,5 +96,75 @@
|
||||
{% 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 == '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 %}
|
||||
|
||||
@@ -54,6 +54,19 @@
|
||||
</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=weblinks" class="tab-item {{ 'active' if tab == 'weblinks' }}">Weblinks{% if counts.weblinks %} ({{ counts.weblinks }}){% 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>
|
||||
@@ -61,7 +74,6 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Subtasks -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">Subtasks<span class="page-count">{{ subtasks|length }}</span></h2>
|
||||
@@ -84,6 +96,99 @@
|
||||
{% 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 == 'weblinks' %}
|
||||
<a href="/weblinks/create?task_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New Weblink</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 weblinks 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 %}
|
||||
|
||||
@@ -41,9 +41,11 @@
|
||||
</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 Weblink' }}</button>
|
||||
<a href="/weblinks" class="btn btn-secondary">Cancel</a>
|
||||
<a href="{{ '/tasks/' ~ prefill_task_id ~ '?tab=weblinks' if prefill_task_id is defined and prefill_task_id else '/weblinks' }}" class="btn btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user