feat: focus item detail page with inline note + checklist

Each standalone focus item now auto-creates a linked note and checklist.
Clicking a focus item opens a detail page with side-by-side note editor
(left) and checklist (right) with drag-to-reorder. Save & Return writes
the note and goes back to the focus list. Added focus_id FK to notes and
lists tables, made domain optional when creating from focus context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 20:02:27 +00:00
parent a2183af6e2
commit 6abef336c4
9 changed files with 365 additions and 17 deletions

View File

@@ -153,7 +153,7 @@ class BaseRepository:
nullable_fields = {
"description", "notes", "body", "area_id", "project_id",
"parent_id", "parent_item_id", "release_id", "due_date", "deadline", "tags",
"context", "folder_id", "meeting_id", "completed_at",
"context", "folder_id", "meeting_id", "focus_id", "completed_at",
"waiting_for_contact_id", "waiting_since", "color",
"rationale", "decided_at", "superseded_by_id",
"start_at", "end_at", "location", "agenda", "transcript", "notes_body",

View File

@@ -263,6 +263,158 @@ async def reorder_all_focus(
return RedirectResponse(url="/focus", status_code=303)
async def _ensure_focus_note_and_list(focus_id: str, item: dict, db: AsyncSession):
"""Find or create the single note + list attached to a focus item."""
note_repo = BaseRepository("notes", db)
list_repo = BaseRepository("lists", db)
# Find existing note
result = await db.execute(text(
"SELECT id FROM notes WHERE focus_id = :fid AND is_deleted = false LIMIT 1"
), {"fid": focus_id})
note_id = result.scalar()
if not note_id:
note = await note_repo.create({
"title": f'Notes: {item["title"]}',
"focus_id": focus_id,
"domain_id": item.get("domain_id"),
"project_id": item.get("project_id"),
"body": "",
"content_format": "rich",
})
note_id = note["id"]
# Find existing list
result = await db.execute(text(
"SELECT id FROM lists WHERE focus_id = :fid AND is_deleted = false LIMIT 1"
), {"fid": focus_id})
list_id = result.scalar()
if not list_id:
lst = await list_repo.create({
"name": f'List: {item["title"]}',
"focus_id": focus_id,
"domain_id": item.get("domain_id"),
"project_id": item.get("project_id"),
"list_type": "checklist",
})
list_id = lst["id"]
return str(note_id), str(list_id)
@router.get("/{focus_id}")
async def focus_detail(
focus_id: str, request: Request,
db: AsyncSession = Depends(get_db),
):
repo = BaseRepository("daily_focus", db)
item = await repo.get(focus_id)
if not item or item.get("task_id") or item.get("list_item_id"):
return RedirectResponse(url="/focus", status_code=303)
sidebar = await get_sidebar_data(db)
# Domain / project info
domain = None
if item.get("domain_id"):
result = await db.execute(text("SELECT name, color FROM domains WHERE id = :id"), {"id": str(item["domain_id"])})
row = result.first()
domain = dict(row._mapping) if row else None
project = None
if item.get("project_id"):
result = await db.execute(text("SELECT id, name FROM projects WHERE id = :id"), {"id": str(item["project_id"])})
row = result.first()
project = dict(row._mapping) if row else None
# Lists for convert-to-list-item
all_lists = await BaseRepository("lists", db).list()
# Ensure note + list exist
note_id, list_id = await _ensure_focus_note_and_list(focus_id, item, db)
# Load note
note = await BaseRepository("notes", db).get(note_id)
# Load list items
result = await db.execute(text("""
SELECT * FROM list_items
WHERE list_id = :lid AND is_deleted = false
ORDER BY sort_order, created_at
"""), {"lid": list_id})
list_items = [dict(r._mapping) for r in result]
return templates.TemplateResponse("focus_detail.html", {
"request": request, "sidebar": sidebar, "item": item,
"domain": domain, "project": project, "all_lists": all_lists,
"note": note, "list_id": list_id, "list_items": list_items,
"page_title": item.get("title", "Focus Item"), "active_nav": "focus",
})
@router.post("/{focus_id}/save-note")
async def save_focus_note(
focus_id: str, request: Request,
note_id: str = Form(...),
body: Optional[str] = Form(None),
db: AsyncSession = Depends(get_db),
):
note_repo = BaseRepository("notes", db)
await note_repo.update(note_id, {"body": body or ""})
return RedirectResponse(url="/focus", status_code=303)
@router.post("/{focus_id}/list-item/add")
async def add_focus_list_item(
focus_id: str, request: Request,
list_id: str = Form(...),
content: str = Form(...),
db: AsyncSession = Depends(get_db),
):
li_repo = BaseRepository("list_items", db)
await li_repo.create({"list_id": list_id, "content": content, "completed": False})
return RedirectResponse(url=f"/focus/{focus_id}", status_code=303)
@router.post("/{focus_id}/list-item/{item_id}/toggle")
async def toggle_focus_list_item(
focus_id: str, item_id: str, request: Request,
db: AsyncSession = Depends(get_db),
):
li_repo = BaseRepository("list_items", db)
item = await li_repo.get(item_id)
if item:
now = datetime.now(timezone.utc)
if item["completed"]:
await li_repo.update(item_id, {"completed": False, "completed_at": None})
else:
await li_repo.update(item_id, {"completed": True, "completed_at": now})
return RedirectResponse(url=f"/focus/{focus_id}", status_code=303)
@router.post("/{focus_id}/list-item/{item_id}/delete")
async def delete_focus_list_item(
focus_id: str, item_id: str, request: Request,
db: AsyncSession = Depends(get_db),
):
li_repo = BaseRepository("list_items", db)
await li_repo.soft_delete(item_id)
return RedirectResponse(url=f"/focus/{focus_id}", status_code=303)
@router.post("/{focus_id}/list-item/reorder-all")
async def reorder_focus_list_items(
focus_id: str, request: Request,
item_ids: str = Form(...),
db: AsyncSession = Depends(get_db),
):
repo = BaseRepository("list_items", db)
ids = [i.strip() for i in item_ids.split(",") if i.strip()]
if ids:
await repo.reorder(ids)
return RedirectResponse(url=f"/focus/{focus_id}", status_code=303)
@router.get("/{focus_id}/edit")
async def edit_focus_item(focus_id: str, request: Request, db: AsyncSession = Depends(get_db)):
repo = BaseRepository("daily_focus", db)
@@ -294,7 +446,7 @@ async def update_focus_item(
"domain_id": domain_id or None,
"project_id": project_id or None,
})
return RedirectResponse(url="/focus", status_code=303)
return RedirectResponse(url=f"/focus/{focus_id}", status_code=303)
@router.post("/{focus_id}/convert-to-task")
@@ -436,7 +588,8 @@ async def toggle_focus(focus_id: str, request: Request, db: AsyncSession = Depen
"UPDATE list_items SET completed = false, completed_at = NULL, updated_at = :now WHERE id = :id"
), {"id": item["list_item_id"], "now": now})
await db.commit()
return RedirectResponse(url="/focus", status_code=303)
referer = request.headers.get("referer", "/focus")
return RedirectResponse(url=referer, status_code=303)
@router.post("/{focus_id}/remove")

View File

@@ -74,6 +74,7 @@ async def create_form(
project_id: Optional[str] = None,
task_id: Optional[str] = None,
meeting_id: Optional[str] = None,
focus_id: Optional[str] = None,
db: AsyncSession = Depends(get_db),
):
sidebar = await get_sidebar_data(db)
@@ -93,6 +94,7 @@ async def create_form(
"prefill_project_id": project_id or "",
"prefill_task_id": task_id or "",
"prefill_meeting_id": meeting_id or "",
"prefill_focus_id": focus_id or "",
})
@@ -100,11 +102,12 @@ async def create_form(
async def create_list(
request: Request,
name: str = Form(...),
domain_id: str = Form(...),
domain_id: Optional[str] = Form(None),
area_id: Optional[str] = Form(None),
project_id: Optional[str] = Form(None),
task_id: Optional[str] = Form(None),
meeting_id: Optional[str] = Form(None),
focus_id: Optional[str] = Form(None),
list_type: str = Form("checklist"),
description: Optional[str] = Form(None),
tags: Optional[str] = Form(None),
@@ -112,10 +115,12 @@ async def create_list(
):
repo = BaseRepository("lists", db)
data = {
"name": name, "domain_id": domain_id,
"name": name,
"list_type": list_type,
"description": description,
}
if domain_id and domain_id.strip():
data["domain_id"] = domain_id
if area_id and area_id.strip():
data["area_id"] = area_id
if project_id and project_id.strip():
@@ -124,6 +129,8 @@ async def create_list(
data["task_id"] = task_id
if meeting_id and meeting_id.strip():
data["meeting_id"] = meeting_id
if focus_id and focus_id.strip():
data["focus_id"] = focus_id
if tags and tags.strip():
data["tags"] = [t.strip() for t in tags.split(",") if t.strip()]
@@ -132,6 +139,8 @@ async def create_list(
return RedirectResponse(url=f"/tasks/{task_id}?tab=lists", status_code=303)
if meeting_id and meeting_id.strip():
return RedirectResponse(url=f"/meetings/{meeting_id}?tab=lists", status_code=303)
if focus_id and focus_id.strip():
return RedirectResponse(url=f"/focus/{focus_id}?tab=lists", status_code=303)
return RedirectResponse(url=f"/lists/{new_list['id']}", status_code=303)

View File

@@ -63,6 +63,7 @@ async def create_form(
project_id: Optional[str] = None,
task_id: Optional[str] = None,
meeting_id: Optional[str] = None,
focus_id: Optional[str] = None,
db: AsyncSession = Depends(get_db),
):
sidebar = await get_sidebar_data(db)
@@ -79,6 +80,7 @@ async def create_form(
"prefill_project_id": project_id or "",
"prefill_task_id": task_id or "",
"prefill_meeting_id": meeting_id or "",
"prefill_focus_id": focus_id or "",
})
@@ -86,10 +88,11 @@ async def create_form(
async def create_note(
request: Request,
title: str = Form(...),
domain_id: str = Form(...),
domain_id: Optional[str] = Form(None),
project_id: Optional[str] = Form(None),
task_id: Optional[str] = Form(None),
meeting_id: Optional[str] = Form(None),
focus_id: Optional[str] = Form(None),
body: Optional[str] = Form(None),
content_format: str = Form("rich"),
tags: Optional[str] = Form(None),
@@ -97,15 +100,19 @@ async def create_note(
):
repo = BaseRepository("notes", db)
data = {
"title": title, "domain_id": domain_id,
"title": title,
"body": body, "content_format": content_format,
}
if domain_id and domain_id.strip():
data["domain_id"] = domain_id
if project_id and project_id.strip():
data["project_id"] = project_id
if task_id and task_id.strip():
data["task_id"] = task_id
if meeting_id and meeting_id.strip():
data["meeting_id"] = meeting_id
if focus_id and focus_id.strip():
data["focus_id"] = focus_id
if tags and tags.strip():
data["tags"] = [t.strip() for t in tags.split(",") if t.strip()]
note = await repo.create(data)
@@ -113,6 +120,8 @@ async def create_note(
return RedirectResponse(url=f"/tasks/{task_id}?tab=notes", status_code=303)
if meeting_id and meeting_id.strip():
return RedirectResponse(url=f"/meetings/{meeting_id}?tab=notes", status_code=303)
if focus_id and focus_id.strip():
return RedirectResponse(url=f"/focus/{focus_id}?tab=notes", status_code=303)
return RedirectResponse(url=f"/notes/{note['id']}", status_code=303)

View File

@@ -52,7 +52,7 @@
</td>
<td style="padding:1px 3px;vertical-align:middle;color:var(--muted);font-size:0.78rem;white-space:nowrap;">{{ item.due_date or '' }}</td>
<td style="padding:1px 1px;vertical-align:middle;">{% if item.task_id %}<span class="priority-dot priority-{{ item.priority }}"></span>{% elif item.list_item_id %}<span style="color:var(--muted);font-size:0.85rem;">&#9776;</span>{% else %}<span style="color:var(--muted);font-size:0.85rem;">&#9679;</span>{% endif %}</td>
<td style="padding:1px 3px;vertical-align:middle;{{ 'text-decoration:line-through;' if item.completed }}">{% if item.task_id %}{% if item.task_title %}<a href="/tasks/{{ item.task_id }}" class="focus-title">{{ item.task_title }}</a>{% else %}<span style="color:var(--muted)">[Deleted]</span>{% endif %}{% elif item.list_item_id %}{% if item.list_item_content %}<a href="/lists/{{ item.list_item_list_id }}" class="focus-title">{{ item.list_item_content }}</a>{% else %}<span style="color:var(--muted)">[Deleted]</span>{% endif %}{% else %}<a href="/focus/{{ item.id }}/edit" class="focus-title">{{ item.title }}</a>{% endif %}</td>
<td style="padding:1px 3px;vertical-align:middle;{{ 'text-decoration:line-through;' if item.completed }}">{% if item.task_id %}{% if item.task_title %}<a href="/tasks/{{ item.task_id }}" class="focus-title">{{ item.task_title }}</a>{% else %}<span style="color:var(--muted)">[Deleted]</span>{% endif %}{% elif item.list_item_id %}{% if item.list_item_content %}<a href="/lists/{{ item.list_item_list_id }}" class="focus-title">{{ item.list_item_content }}</a>{% else %}<span style="color:var(--muted)">[Deleted]</span>{% endif %}{% else %}<a href="/focus/{{ item.id }}" class="focus-title">{{ item.title }}</a>{% endif %}</td>
<td style="padding:1px 3px;vertical-align:middle;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">{% if item.area_name %}<span class="row-tag">{{ item.area_name }}</span>{% endif %}</td>
<td style="padding:1px 3px;vertical-align:middle;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">{% if item.project_name %}<span class="row-tag" style="background:var(--accent-soft);color:var(--accent)">{{ item.project_name }}</span>{% elif item.list_item_id and item.list_name %}<span class="row-tag" style="background:var(--purple);color:#fff">{{ item.list_name }}</span>{% endif %}</td>
<td style="padding:1px 3px;vertical-align:middle;text-align:right;color:var(--muted);font-size:0.78rem;">{{ '~%smin'|format(item.estimated_minutes) if item.estimated_minutes else '' }}</td>

173
templates/focus_detail.html Normal file
View File

@@ -0,0 +1,173 @@
{% extends "base.html" %}
{% block content %}
<div class="breadcrumb">
<a href="/focus">Focus</a>
<span class="sep">/</span>
<span>{{ item.title }}</span>
</div>
<div class="detail-header">
<div class="flex items-center justify-between">
<h1 class="detail-title" style="{{ 'text-decoration:line-through;opacity:0.6;' if item.completed }}">{{ item.title }}</h1>
<div class="flex gap-2">
<a href="/focus/{{ item.id }}/edit" class="btn btn-secondary btn-sm">Edit</a>
<form action="/focus/{{ item.id }}/toggle" method="post" style="display:inline">
<button class="btn {{ 'btn-secondary' if item.completed else 'btn-primary' }} btn-sm">
{{ 'Reopen' if item.completed else 'Complete' }}
</button>
</form>
<form action="/focus/{{ item.id }}/remove" method="post" data-confirm="Delete this focus item?" style="display:inline">
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
</form>
</div>
</div>
<div class="detail-meta mt-2">
{% if item.completed %}<span class="status-badge status-done">completed</span>{% endif %}
{% 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 %}
</div>
</div>
<!-- Convert row -->
<div style="display:flex;flex-wrap:wrap;gap:8px;align-items:end;margin-bottom:16px;padding:8px 0;">
<span style="font-size:0.78rem;color:var(--muted);font-weight:600;">Convert to:</span>
<form action="/focus/{{ item.id }}/convert-to-task" method="post" data-confirm="Convert to task?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs">Task</button>
</form>
<form action="/focus/{{ item.id }}/convert-to-note" method="post" data-confirm="Convert to note?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs">Note</button>
</form>
<form action="/focus/{{ item.id }}/convert-to-link" method="post" data-confirm="Convert to link?" style="display:inline">
<button type="submit" class="btn btn-ghost btn-xs">Link</button>
</form>
<form action="/focus/{{ item.id }}/convert-to-list-item" method="post" data-confirm="Add to selected list?" style="display:inline-flex;gap:4px;align-items:end;">
<select name="list_id" class="form-select" style="min-width:140px;height:28px;font-size:12px;" required>
<option value="">Select list...</option>
{% for l in all_lists %}
<option value="{{ l.id }}">{{ l.name }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-ghost btn-xs">List Item</button>
</form>
</div>
<!-- Side-by-side: Note (left) + List (right) -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start;">
<!-- Note panel -->
<div class="card" style="padding:0;">
<div style="padding:8px 12px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;">
<span style="font-weight:600;font-size:0.82rem;color:var(--text);">Notes</span>
</div>
<form action="/focus/{{ item.id }}/save-note" method="post">
<input type="hidden" name="note_id" value="{{ note.id }}">
<textarea name="body" style="width:100%;min-height:320px;padding:10px 12px;border:none;background:transparent;color:var(--text);font-family:var(--font-mono);font-size:0.85rem;resize:vertical;outline:none;box-sizing:border-box;" placeholder="Start typing notes...">{{ note.body if note.body else '' }}</textarea>
<div style="padding:8px 12px;border-top:1px solid var(--border);display:flex;gap:8px;">
<button type="submit" class="btn btn-primary btn-sm">Save &amp; Return</button>
<a href="/focus" class="btn btn-ghost btn-sm">Back</a>
</div>
</form>
</div>
<!-- List panel -->
<div class="card" style="padding:0;">
<div style="padding:8px 12px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;">
<span style="font-weight:600;font-size:0.82rem;color:var(--text);">Checklist</span>
<span style="font-size:0.72rem;color:var(--muted);">{{ list_items|length }} item{{ 's' if list_items|length != 1 }}</span>
</div>
<!-- Add item -->
<form action="/focus/{{ item.id }}/list-item/add" method="post" style="display:flex;gap:6px;padding:8px 12px;border-bottom:1px solid var(--border);">
<input type="hidden" name="list_id" value="{{ list_id }}">
<input type="text" name="content" class="form-input" placeholder="Add item..." required style="flex:1;padding:5px 8px;font-size:0.82rem;">
<button type="submit" class="btn btn-primary btn-sm">+</button>
</form>
<!-- Items -->
<div id="focus-checklist" style="max-height:340px;overflow-y:auto;">
{% for li in list_items %}
<div class="list-row focus-cl-row" draggable="true" data-id="{{ li.id }}" style="padding:4px 8px;min-height:0;border-bottom:1px solid var(--border);{{ 'opacity:0.5;' if li.completed }}cursor:grab;">
<span class="reorder-grip" style="margin-right:4px;color:var(--muted);font-size:0.7rem;cursor:grab;">&#x2630;</span>
<form action="/focus/{{ item.id }}/list-item/{{ li.id }}/toggle" method="post" style="display:inline">
<div class="row-check" style="margin-right:6px;">
<input type="checkbox" id="li-{{ li.id }}" {{ 'checked' if li.completed }} onchange="this.form.submit()">
<label for="li-{{ li.id }}"></label>
</div>
</form>
<span style="flex:1;font-size:0.82rem;{{ 'text-decoration:line-through;' if li.completed }}">{{ li.content }}</span>
<form action="/focus/{{ item.id }}/list-item/{{ li.id }}/delete" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs" style="color:var(--red);padding:0 4px;" title="Remove">&times;</button>
</form>
</div>
{% else %}
<div style="padding:16px 12px;color:var(--muted);font-size:0.82rem;text-align:center;">No items yet</div>
{% endfor %}
</div>
<form id="cl-reorder-form" action="/focus/{{ item.id }}/list-item/reorder-all" method="post" style="display:none;">
<input type="hidden" name="item_ids" id="cl-reorder-ids">
</form>
</div>
</div>
<script>
(function() {
var container = document.getElementById('focus-checklist');
if (!container) return;
var dragRow = null;
container.querySelectorAll('.focus-cl-row').forEach(function(row) {
row.addEventListener('dragstart', function(e) {
dragRow = row;
row.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', row.dataset.id);
});
row.addEventListener('dragend', function() {
row.classList.remove('dragging');
container.querySelectorAll('.focus-cl-row.drag-over').forEach(function(el) {
el.classList.remove('drag-over');
});
if (dragRow) {
var allIds = [];
container.querySelectorAll('.focus-cl-row').forEach(function(r) {
allIds.push(r.dataset.id);
});
document.getElementById('cl-reorder-ids').value = allIds.join(',');
document.getElementById('cl-reorder-form').submit();
}
dragRow = null;
});
row.addEventListener('dragover', function(e) {
e.preventDefault();
if (!dragRow || row === dragRow) return;
e.dataTransfer.dropEffect = 'move';
container.querySelectorAll('.focus-cl-row.drag-over').forEach(function(el) {
el.classList.remove('drag-over');
});
row.classList.add('drag-over');
});
row.addEventListener('dragleave', function() {
row.classList.remove('drag-over');
});
row.addEventListener('drop', function(e) {
e.preventDefault();
if (!dragRow || row === dragRow) return;
row.classList.remove('drag-over');
var rows = Array.from(container.querySelectorAll('.focus-cl-row'));
var dragIdx = rows.indexOf(dragRow);
var targetIdx = rows.indexOf(row);
if (dragIdx < targetIdx) {
container.insertBefore(dragRow, row.nextSibling);
} else {
container.insertBefore(dragRow, row);
}
});
});
})();
</script>
{% endblock %}

View File

@@ -3,7 +3,9 @@
<div class="breadcrumb">
<a href="/focus">Focus</a>
<span class="sep">/</span>
<span>Edit Item</span>
<a href="/focus/{{ item.id }}">{{ item.title or 'Item' }}</a>
<span class="sep">/</span>
<span>Edit</span>
</div>
<div class="page-header">
@@ -45,7 +47,7 @@
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save Changes</button>
<a href="/focus" class="btn btn-secondary">Cancel</a>
<a href="/focus/{{ item.id }}" class="btn btn-secondary">Cancel</a>
</div>
</div>
</form>

View File

@@ -14,9 +14,9 @@
</div>
<div class="form-group">
<label class="form-label">Domain *</label>
<select name="domain_id" class="form-select" required>
<option value="">Select domain...</option>
<label class="form-label">Domain{{ ' *' if not (prefill_focus_id is defined and prefill_focus_id) }}</label>
<select name="domain_id" class="form-select" {{ 'required' if not (prefill_focus_id is defined and prefill_focus_id) }}>
<option value="">-- None --</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) }}>
@@ -75,9 +75,10 @@
{% 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 %}
{% if prefill_focus_id is defined and prefill_focus_id %}<input type="hidden" name="focus_id" value="{{ prefill_focus_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>
<a href="{{ '/focus/' ~ prefill_focus_id ~ '?tab=lists' if prefill_focus_id is defined and prefill_focus_id else ('/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>

View File

@@ -6,8 +6,8 @@
<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">Domain{{ ' *' if not (prefill_focus_id is defined and prefill_focus_id) }}</label>
<select name="domain_id" class="form-select" {{ 'required' if not (prefill_focus_id is defined and prefill_focus_id) }}><option value="">-- None --</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">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>
@@ -15,7 +15,8 @@
<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>
{% if prefill_focus_id is defined and prefill_focus_id %}<input type="hidden" name="focus_id" value="{{ prefill_focus_id }}">{% endif %}
<div class="form-actions"><button type="submit" class="btn btn-primary">{{ 'Save' if item else 'Create' }}</button><a href="{{ '/focus/' ~ prefill_focus_id ~ '?tab=notes' if prefill_focus_id is defined and prefill_focus_id else ('/notes/' ~ item.id if item else '/notes') }}" class="btn btn-secondary">Cancel</a></div>
</div>
</form></div>
{% endblock %}