feat: enhanced capture queue with full conversion, batching, and filtering
- Convert to 7 entity types: task, note, project, list item, contact, decision, weblink - Each conversion has a dedicated form page with pre-filled fields and context selectors - Multi-line paste creates batch with shared import_batch_id and undo button - 3-tab filtering: Inbox (unprocessed), Processed (with conversion links), All - Context pre-fill: optional area/project selectors on capture form - Processed items show type badge and link to converted entity - Sidebar badge count for unprocessed items already working Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,28 +8,91 @@
|
||||
<div class="card mb-4">
|
||||
<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 items here... Each line becomes a separate capture item"></textarea>
|
||||
<textarea name="raw_text" class="form-textarea" rows="3" placeholder="Type or paste items here... Each line becomes a separate capture item" 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">Capture</button></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<a href="/capture?show=unprocessed" class="btn {{ 'btn-primary' if show != 'all' else 'btn-secondary' }} btn-sm">Unprocessed</a>
|
||||
<a href="/capture?show=all" class="btn {{ 'btn-primary' if show == 'all' else 'btn-secondary' }} btn-sm">All</a>
|
||||
<!-- 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 · {{ 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 not item.processed %}
|
||||
|
||||
{% if item.processed %}
|
||||
<div class="capture-actions">
|
||||
<form action="/capture/{{ item.id }}/to-task" method="post" class="flex gap-2">
|
||||
<select name="domain_id" class="filter-select" style="font-size:0.75rem;padding:3px 6px" required>
|
||||
{% for d in sidebar.domain_tree %}<option value="{{ d.id }}">{{ d.name }}</option>{% endfor %}
|
||||
</select>
|
||||
<button type="submit" class="btn btn-ghost btn-xs" style="color:var(--green)">To Task</button>
|
||||
</form>
|
||||
{% 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 →</a>
|
||||
{% elif item.converted_to_type == 'weblink' %}
|
||||
<a href="/weblinks" class="btn btn-ghost btn-xs">View →</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 →</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)">×</button></form>
|
||||
</div>
|
||||
@@ -37,6 +100,11 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="empty-state"><div class="empty-state-icon">📥</div><div class="empty-state-text">Capture queue is empty</div></div>
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">📥</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 %}
|
||||
|
||||
Reference in New Issue
Block a user