feat: return-to-project redirects from create/edit forms

When creating or editing items from a project detail tab, users now
return to that project's tab instead of the entity's own page.
Edit links pass from_project param; forms include hidden field.
Reassigning to a different project redirects to the new project.
Decisions/meetings create from project context inserts junction rows.
File uploads from project context redirect back to project files tab.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 18:28:15 +00:00
parent ba9c36e691
commit c7a07ed280
18 changed files with 125 additions and 27 deletions

View File

@@ -138,7 +138,7 @@ async def contact_detail(contact_id: str, request: Request, db: AsyncSession = D
@router.get("/{contact_id}/edit") @router.get("/{contact_id}/edit")
async def edit_form(contact_id: str, request: Request, db: AsyncSession = Depends(get_db)): async def edit_form(contact_id: str, request: Request, from_project: Optional[str] = None, db: AsyncSession = Depends(get_db)):
repo = BaseRepository("contacts", db) repo = BaseRepository("contacts", db)
sidebar = await get_sidebar_data(db) sidebar = await get_sidebar_data(db)
item = await repo.get(contact_id) item = await repo.get(contact_id)
@@ -159,6 +159,7 @@ async def edit_form(contact_id: str, request: Request, db: AsyncSession = Depend
"request": request, "sidebar": sidebar, "request": request, "sidebar": sidebar,
"page_title": "Edit Contact", "active_nav": "contacts", "page_title": "Edit Contact", "active_nav": "contacts",
"item": item, "all_links": all_links, "linked_links": linked_links, "item": item, "all_links": all_links, "linked_links": linked_links,
"from_project": from_project or "",
}) })
@@ -174,6 +175,7 @@ async def update_contact(
phone: Optional[str] = Form(None), phone: Optional[str] = Form(None),
notes: Optional[str] = Form(None), notes: Optional[str] = Form(None),
tags: Optional[str] = Form(None), tags: Optional[str] = Form(None),
from_project: Optional[str] = Form(None),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
repo = BaseRepository("contacts", db) repo = BaseRepository("contacts", db)
@@ -224,6 +226,8 @@ async def update_contact(
await db.commit() await db.commit()
if from_project and from_project.strip():
return RedirectResponse(url=f"/projects/{from_project}?tab=contacts", status_code=303)
return RedirectResponse(url=f"/contacts/{contact_id}", status_code=303) return RedirectResponse(url=f"/contacts/{contact_id}", status_code=303)

View File

@@ -57,6 +57,7 @@ async def create_form(
request: Request, request: Request,
meeting_id: Optional[str] = None, meeting_id: Optional[str] = None,
task_id: Optional[str] = None, task_id: Optional[str] = None,
project_id: Optional[str] = None,
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
sidebar = await get_sidebar_data(db) sidebar = await get_sidebar_data(db)
@@ -74,6 +75,7 @@ async def create_form(
"item": None, "item": None,
"prefill_meeting_id": meeting_id or "", "prefill_meeting_id": meeting_id or "",
"prefill_task_id": task_id or "", "prefill_task_id": task_id or "",
"prefill_project_id": project_id or "",
}) })
@@ -87,6 +89,7 @@ async def create_decision(
decided_at: Optional[str] = Form(None), decided_at: Optional[str] = Form(None),
meeting_id: Optional[str] = Form(None), meeting_id: Optional[str] = Form(None),
task_id: Optional[str] = Form(None), task_id: Optional[str] = Form(None),
project_id: Optional[str] = Form(None),
tags: Optional[str] = Form(None), tags: Optional[str] = Form(None),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
@@ -105,8 +108,18 @@ async def create_decision(
data["tags"] = [t.strip() for t in tags.split(",") if t.strip()] data["tags"] = [t.strip() for t in tags.split(",") if t.strip()]
decision = await repo.create(data) decision = await repo.create(data)
# Link to project if created from project context
if project_id and project_id.strip():
await db.execute(text("""
INSERT INTO decision_projects (decision_id, project_id)
VALUES (:did, :pid) ON CONFLICT DO NOTHING
"""), {"did": decision["id"], "pid": project_id})
if task_id and task_id.strip(): if task_id and task_id.strip():
return RedirectResponse(url=f"/tasks/{task_id}?tab=decisions", status_code=303) return RedirectResponse(url=f"/tasks/{task_id}?tab=decisions", status_code=303)
if project_id and project_id.strip():
return RedirectResponse(url=f"/projects/{project_id}?tab=decisions", status_code=303)
return RedirectResponse(url=f"/decisions/{decision['id']}", status_code=303) return RedirectResponse(url=f"/decisions/{decision['id']}", status_code=303)
@@ -152,7 +165,7 @@ async def decision_detail(decision_id: str, request: Request, db: AsyncSession =
@router.get("/{decision_id}/edit") @router.get("/{decision_id}/edit")
async def edit_form(decision_id: str, request: Request, db: AsyncSession = Depends(get_db)): async def edit_form(decision_id: str, request: Request, from_project: Optional[str] = None, db: AsyncSession = Depends(get_db)):
repo = BaseRepository("decisions", db) repo = BaseRepository("decisions", db)
sidebar = await get_sidebar_data(db) sidebar = await get_sidebar_data(db)
item = await repo.get(decision_id) item = await repo.get(decision_id)
@@ -178,6 +191,7 @@ async def edit_form(decision_id: str, request: Request, db: AsyncSession = Depen
"page_title": "Edit Decision", "active_nav": "decisions", "page_title": "Edit Decision", "active_nav": "decisions",
"item": item, "item": item,
"prefill_meeting_id": "", "prefill_meeting_id": "",
"from_project": from_project or "",
}) })
@@ -192,6 +206,7 @@ async def update_decision(
meeting_id: Optional[str] = Form(None), meeting_id: Optional[str] = Form(None),
superseded_by_id: Optional[str] = Form(None), superseded_by_id: Optional[str] = Form(None),
tags: Optional[str] = Form(None), tags: Optional[str] = Form(None),
from_project: Optional[str] = Form(None),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
repo = BaseRepository("decisions", db) repo = BaseRepository("decisions", db)
@@ -208,6 +223,9 @@ async def update_decision(
data["tags"] = None data["tags"] = None
await repo.update(decision_id, data) await repo.update(decision_id, data)
if from_project and from_project.strip():
return RedirectResponse(url=f"/projects/{from_project}?tab=decisions", status_code=303)
return RedirectResponse(url=f"/decisions/{decision_id}", status_code=303) return RedirectResponse(url=f"/decisions/{decision_id}", status_code=303)

View File

@@ -379,6 +379,12 @@ async def upload_file(
# Redirect back to context or file list # Redirect back to context or file list
if context_type and context_id: if context_type and context_id:
if context_type == "project":
return RedirectResponse(url=f"/projects/{context_id}?tab=files", status_code=303)
if context_type == "task":
return RedirectResponse(url=f"/tasks/{context_id}?tab=files", status_code=303)
if context_type == "meeting":
return RedirectResponse(url=f"/meetings/{context_id}?tab=files", status_code=303)
return RedirectResponse( return RedirectResponse(
url=f"/files?context_type={context_type}&context_id={context_id}", url=f"/files?context_type={context_type}&context_id={context_id}",
status_code=303, status_code=303,

View File

@@ -416,7 +416,7 @@ async def reorder_focus_list_items(
@router.get("/{focus_id}/edit") @router.get("/{focus_id}/edit")
async def edit_focus_item(focus_id: str, request: Request, db: AsyncSession = Depends(get_db)): async def edit_focus_item(focus_id: str, request: Request, from_project: Optional[str] = None, db: AsyncSession = Depends(get_db)):
repo = BaseRepository("daily_focus", db) repo = BaseRepository("daily_focus", db)
item = await repo.get(focus_id) item = await repo.get(focus_id)
if not item or item.get("task_id") or item.get("list_item_id"): if not item or item.get("task_id") or item.get("list_item_id"):
@@ -429,6 +429,7 @@ async def edit_focus_item(focus_id: str, request: Request, db: AsyncSession = De
"request": request, "sidebar": sidebar, "item": item, "request": request, "sidebar": sidebar, "item": item,
"domains": domains, "projects": projects, "lists": lists, "domains": domains, "projects": projects, "lists": lists,
"page_title": "Edit Focus Item", "active_nav": "focus", "page_title": "Edit Focus Item", "active_nav": "focus",
"from_project": from_project or "",
}) })
@@ -438,14 +439,21 @@ async def update_focus_item(
title: str = Form(...), title: str = Form(...),
domain_id: Optional[str] = Form(None), domain_id: Optional[str] = Form(None),
project_id: Optional[str] = Form(None), project_id: Optional[str] = Form(None),
from_project: Optional[str] = Form(None),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
repo = BaseRepository("daily_focus", db) repo = BaseRepository("daily_focus", db)
new_project = project_id if project_id and project_id.strip() else None
await repo.update(focus_id, { await repo.update(focus_id, {
"title": title.strip(), "title": title.strip(),
"domain_id": domain_id or None, "domain_id": domain_id or None,
"project_id": project_id or None, "project_id": new_project,
}) })
if from_project and from_project.strip():
if new_project and new_project != from_project:
return RedirectResponse(url=f"/projects/{new_project}?tab=focus", status_code=303)
return RedirectResponse(url=f"/projects/{from_project}?tab=focus", status_code=303)
return RedirectResponse(url=f"/focus/{focus_id}", status_code=303) return RedirectResponse(url=f"/focus/{focus_id}", status_code=303)

View File

@@ -122,7 +122,7 @@ async def create_link(
@router.get("/{link_id}/edit") @router.get("/{link_id}/edit")
async def edit_form(link_id: str, request: Request, db: AsyncSession = Depends(get_db)): async def edit_form(link_id: str, request: Request, from_project: Optional[str] = None, db: AsyncSession = Depends(get_db)):
repo = BaseRepository("links", db) repo = BaseRepository("links", db)
sidebar = await get_sidebar_data(db) sidebar = await get_sidebar_data(db)
item = await repo.get(link_id) item = await repo.get(link_id)
@@ -138,6 +138,7 @@ async def edit_form(link_id: str, request: Request, db: AsyncSession = Depends(g
"item": item, "item": item,
"prefill_domain_id": "", "prefill_project_id": "", "prefill_domain_id": "", "prefill_project_id": "",
"prefill_task_id": "", "prefill_meeting_id": "", "prefill_task_id": "", "prefill_meeting_id": "",
"from_project": from_project or "",
}) })
@@ -146,6 +147,7 @@ async def update_link(
link_id: str, label: str = Form(...), url: str = Form(...), link_id: str, label: str = Form(...), url: str = Form(...),
domain_id: Optional[str] = Form(None), project_id: Optional[str] = Form(None), domain_id: Optional[str] = Form(None), project_id: Optional[str] = Form(None),
description: Optional[str] = Form(None), tags: Optional[str] = Form(None), description: Optional[str] = Form(None), tags: Optional[str] = Form(None),
from_project: Optional[str] = Form(None),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
repo = BaseRepository("links", db) repo = BaseRepository("links", db)
@@ -160,6 +162,12 @@ async def update_link(
else: else:
data["tags"] = None data["tags"] = None
await repo.update(link_id, data) await repo.update(link_id, data)
new_project = data.get("project_id")
if from_project and from_project.strip():
if new_project and new_project != from_project:
return RedirectResponse(url=f"/projects/{new_project}?tab=links", status_code=303)
return RedirectResponse(url=f"/projects/{from_project}?tab=links", status_code=303)
return RedirectResponse(url="/links", status_code=303) return RedirectResponse(url="/links", status_code=303)

View File

@@ -141,6 +141,8 @@ async def create_list(
return RedirectResponse(url=f"/meetings/{meeting_id}?tab=lists", status_code=303) return RedirectResponse(url=f"/meetings/{meeting_id}?tab=lists", status_code=303)
if focus_id and focus_id.strip(): if focus_id and focus_id.strip():
return RedirectResponse(url=f"/focus/{focus_id}?tab=lists", status_code=303) return RedirectResponse(url=f"/focus/{focus_id}?tab=lists", status_code=303)
if project_id and project_id.strip():
return RedirectResponse(url=f"/projects/{project_id}?tab=lists", status_code=303)
return RedirectResponse(url=f"/lists/{new_list['id']}", status_code=303) return RedirectResponse(url=f"/lists/{new_list['id']}", status_code=303)
@@ -216,7 +218,7 @@ async def list_detail(list_id: str, request: Request, db: AsyncSession = Depends
@router.get("/{list_id}/edit") @router.get("/{list_id}/edit")
async def edit_form(list_id: str, request: Request, db: AsyncSession = Depends(get_db)): async def edit_form(list_id: str, request: Request, from_project: Optional[str] = None, db: AsyncSession = Depends(get_db)):
repo = BaseRepository("lists", db) repo = BaseRepository("lists", db)
sidebar = await get_sidebar_data(db) sidebar = await get_sidebar_data(db)
item = await repo.get(list_id) item = await repo.get(list_id)
@@ -236,6 +238,7 @@ async def edit_form(list_id: str, request: Request, db: AsyncSession = Depends(g
"page_title": "Edit List", "active_nav": "lists", "page_title": "Edit List", "active_nav": "lists",
"item": item, "item": item,
"prefill_domain_id": "", "prefill_project_id": "", "prefill_domain_id": "", "prefill_project_id": "",
"from_project": from_project or "",
}) })
@@ -249,6 +252,7 @@ async def update_list(
list_type: str = Form("checklist"), list_type: str = Form("checklist"),
description: Optional[str] = Form(None), description: Optional[str] = Form(None),
tags: Optional[str] = Form(None), tags: Optional[str] = Form(None),
from_project: Optional[str] = Form(None),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
repo = BaseRepository("lists", db) repo = BaseRepository("lists", db)
@@ -264,6 +268,12 @@ async def update_list(
data["tags"] = None data["tags"] = None
await repo.update(list_id, data) await repo.update(list_id, data)
new_project = data.get("project_id")
if from_project and from_project.strip():
if new_project and new_project != from_project:
return RedirectResponse(url=f"/projects/{new_project}?tab=lists", status_code=303)
return RedirectResponse(url=f"/projects/{from_project}?tab=lists", status_code=303)
return RedirectResponse(url=f"/lists/{list_id}", status_code=303) return RedirectResponse(url=f"/lists/{list_id}", status_code=303)

View File

@@ -51,7 +51,7 @@ async def list_meetings(
@router.get("/create") @router.get("/create")
async def create_form(request: Request, db: AsyncSession = Depends(get_db)): async def create_form(request: Request, project_id: Optional[str] = None, db: AsyncSession = Depends(get_db)):
sidebar = await get_sidebar_data(db) sidebar = await get_sidebar_data(db)
# Get contacts for attendee selection # Get contacts for attendee selection
contacts_repo = BaseRepository("contacts", db) contacts_repo = BaseRepository("contacts", db)
@@ -68,6 +68,7 @@ async def create_form(request: Request, db: AsyncSession = Depends(get_db)):
"contacts": contacts, "parent_meetings": parent_meetings, "contacts": contacts, "parent_meetings": parent_meetings,
"page_title": "New Meeting", "active_nav": "meetings", "page_title": "New Meeting", "active_nav": "meetings",
"item": None, "item": None,
"prefill_project_id": project_id or "",
}) })
@@ -84,6 +85,7 @@ async def create_meeting(
parent_id: Optional[str] = Form(None), parent_id: Optional[str] = Form(None),
agenda: Optional[str] = Form(None), agenda: Optional[str] = Form(None),
tags: Optional[str] = Form(None), tags: Optional[str] = Form(None),
project_id: Optional[str] = Form(None),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
repo = BaseRepository("meetings", db) repo = BaseRepository("meetings", db)
@@ -103,6 +105,14 @@ async def create_meeting(
data["tags"] = [t.strip() for t in tags.split(",") if t.strip()] data["tags"] = [t.strip() for t in tags.split(",") if t.strip()]
meeting = await repo.create(data) meeting = await repo.create(data)
# Link to project if created from project context
if project_id and project_id.strip():
await db.execute(text("""
INSERT INTO project_meetings (project_id, meeting_id)
VALUES (:pid, :mid) ON CONFLICT DO NOTHING
"""), {"pid": project_id, "mid": meeting["id"]})
return RedirectResponse(url=f"/projects/{project_id}?tab=meetings", status_code=303)
return RedirectResponse(url=f"/meetings/{meeting['id']}", status_code=303) return RedirectResponse(url=f"/meetings/{meeting['id']}", status_code=303)
@@ -253,7 +263,7 @@ async def meeting_detail(
@router.get("/{meeting_id}/edit") @router.get("/{meeting_id}/edit")
async def edit_form(meeting_id: str, request: Request, db: AsyncSession = Depends(get_db)): async def edit_form(meeting_id: str, request: Request, from_project: Optional[str] = None, db: AsyncSession = Depends(get_db)):
repo = BaseRepository("meetings", db) repo = BaseRepository("meetings", db)
sidebar = await get_sidebar_data(db) sidebar = await get_sidebar_data(db)
item = await repo.get(meeting_id) item = await repo.get(meeting_id)
@@ -273,6 +283,7 @@ async def edit_form(meeting_id: str, request: Request, db: AsyncSession = Depend
"contacts": contacts, "parent_meetings": parent_meetings, "contacts": contacts, "parent_meetings": parent_meetings,
"page_title": "Edit Meeting", "active_nav": "meetings", "page_title": "Edit Meeting", "active_nav": "meetings",
"item": item, "item": item,
"from_project": from_project or "",
}) })
@@ -291,6 +302,7 @@ async def update_meeting(
transcript: Optional[str] = Form(None), transcript: Optional[str] = Form(None),
notes_body: Optional[str] = Form(None), notes_body: Optional[str] = Form(None),
tags: Optional[str] = Form(None), tags: Optional[str] = Form(None),
from_project: Optional[str] = Form(None),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
repo = BaseRepository("meetings", db) repo = BaseRepository("meetings", db)
@@ -315,6 +327,9 @@ async def update_meeting(
data["tags"] = None data["tags"] = None
await repo.update(meeting_id, data) await repo.update(meeting_id, data)
if from_project and from_project.strip():
return RedirectResponse(url=f"/projects/{from_project}?tab=meetings", status_code=303)
return RedirectResponse(url=f"/meetings/{meeting_id}", status_code=303) return RedirectResponse(url=f"/meetings/{meeting_id}", status_code=303)

View File

@@ -122,6 +122,8 @@ async def create_note(
return RedirectResponse(url=f"/meetings/{meeting_id}?tab=notes", status_code=303) return RedirectResponse(url=f"/meetings/{meeting_id}?tab=notes", status_code=303)
if focus_id and focus_id.strip(): if focus_id and focus_id.strip():
return RedirectResponse(url=f"/focus/{focus_id}?tab=notes", status_code=303) return RedirectResponse(url=f"/focus/{focus_id}?tab=notes", status_code=303)
if project_id and project_id.strip():
return RedirectResponse(url=f"/projects/{project_id}?tab=notes", status_code=303)
return RedirectResponse(url=f"/notes/{note['id']}", status_code=303) return RedirectResponse(url=f"/notes/{note['id']}", status_code=303)
@@ -153,7 +155,7 @@ async def note_detail(note_id: str, request: Request, db: AsyncSession = Depends
@router.get("/{note_id}/edit") @router.get("/{note_id}/edit")
async def edit_form(note_id: str, request: Request, db: AsyncSession = Depends(get_db)): async def edit_form(note_id: str, request: Request, from_project: Optional[str] = None, db: AsyncSession = Depends(get_db)):
repo = BaseRepository("notes", db) repo = BaseRepository("notes", db)
sidebar = await get_sidebar_data(db) sidebar = await get_sidebar_data(db)
item = await repo.get(note_id) item = await repo.get(note_id)
@@ -168,6 +170,7 @@ async def edit_form(note_id: str, request: Request, db: AsyncSession = Depends(g
"domains": domains, "projects": projects, "domains": domains, "projects": projects,
"page_title": f"Edit Note", "active_nav": "notes", "page_title": f"Edit Note", "active_nav": "notes",
"item": item, "prefill_domain_id": "", "prefill_project_id": "", "item": item, "prefill_domain_id": "", "prefill_project_id": "",
"from_project": from_project or "",
}) })
@@ -180,6 +183,7 @@ async def update_note(
body: Optional[str] = Form(None), body: Optional[str] = Form(None),
content_format: str = Form("rich"), content_format: str = Form("rich"),
tags: Optional[str] = Form(None), tags: Optional[str] = Form(None),
from_project: Optional[str] = Form(None),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
repo = BaseRepository("notes", db) repo = BaseRepository("notes", db)
@@ -193,6 +197,12 @@ async def update_note(
else: else:
data["tags"] = None data["tags"] = None
await repo.update(note_id, data) await repo.update(note_id, data)
new_project = data.get("project_id")
if from_project and from_project.strip():
if new_project and new_project != from_project:
return RedirectResponse(url=f"/projects/{new_project}?tab=notes", status_code=303)
return RedirectResponse(url=f"/projects/{from_project}?tab=notes", status_code=303)
return RedirectResponse(url=f"/notes/{note_id}", status_code=303) return RedirectResponse(url=f"/notes/{note_id}", status_code=303)

View File

@@ -322,7 +322,7 @@ async def task_detail(
@router.get("/{task_id}/edit") @router.get("/{task_id}/edit")
async def edit_form(task_id: str, request: Request, db: AsyncSession = Depends(get_db)): async def edit_form(task_id: str, request: Request, from_project: Optional[str] = None, db: AsyncSession = Depends(get_db)):
repo = BaseRepository("tasks", db) repo = BaseRepository("tasks", db)
sidebar = await get_sidebar_data(db) sidebar = await get_sidebar_data(db)
item = await repo.get(task_id) item = await repo.get(task_id)
@@ -344,6 +344,7 @@ async def edit_form(task_id: str, request: Request, db: AsyncSession = Depends(g
"page_title": f"Edit Task", "active_nav": "tasks", "page_title": f"Edit Task", "active_nav": "tasks",
"item": item, "item": item,
"prefill_domain_id": "", "prefill_project_id": "", "prefill_parent_id": "", "prefill_domain_id": "", "prefill_project_id": "", "prefill_parent_id": "",
"from_project": from_project or "",
}) })
@@ -363,6 +364,7 @@ async def update_task(
tags: Optional[str] = Form(None), tags: Optional[str] = Form(None),
estimated_minutes: Optional[str] = Form(None), estimated_minutes: Optional[str] = Form(None),
energy_required: Optional[str] = Form(None), energy_required: Optional[str] = Form(None),
from_project: Optional[str] = Form(None),
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
): ):
repo = BaseRepository("tasks", db) repo = BaseRepository("tasks", db)
@@ -392,6 +394,13 @@ async def update_task(
data["completed_at"] = None data["completed_at"] = None
await repo.update(task_id, data) await repo.update(task_id, data)
# Redirect back to project context if applicable
new_project = data.get("project_id")
if from_project and from_project.strip():
if new_project and new_project != from_project:
return RedirectResponse(url=f"/projects/{new_project}?tab=tasks", status_code=303)
return RedirectResponse(url=f"/projects/{from_project}?tab=tasks", status_code=303)
return RedirectResponse(url=f"/tasks/{task_id}", status_code=303) return RedirectResponse(url=f"/tasks/{task_id}", status_code=303)

View File

@@ -46,7 +46,8 @@
</datalist> </datalist>
</div> </div>
<div class="form-actions"><button type="submit" class="btn btn-primary">{{ 'Save' if item else 'Create' }}</button><a href="{{ '/contacts/' ~ item.id if item else '/contacts' }}" class="btn btn-secondary">Cancel</a></div> {% if from_project is defined and from_project %}<input type="hidden" name="from_project" value="{{ from_project }}">{% endif %}
<div class="form-actions"><button type="submit" class="btn btn-primary">{{ 'Save' if item else 'Create' }}</button><a href="{{ '/projects/' ~ from_project ~ '?tab=contacts' if from_project is defined and from_project else ('/contacts/' ~ item.id if item else '/contacts') }}" class="btn btn-secondary">Cancel</a></div>
</div> </div>
</form></div> </form></div>

View File

@@ -78,9 +78,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_task_id is defined and prefill_task_id %}<input type="hidden" name="task_id" value="{{ prefill_task_id }}">{% endif %}
{% if prefill_project_id is defined and prefill_project_id %}<input type="hidden" name="project_id" value="{{ prefill_project_id }}">{% endif %}
{% if from_project is defined and from_project %}<input type="hidden" name="from_project" value="{{ from_project }}">{% endif %}
<div class="form-actions"> <div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save Changes' if item else 'Record Decision' }}</button> <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> <a href="{{ '/projects/' ~ from_project ~ '?tab=decisions' if from_project is defined and from_project else ('/projects/' ~ prefill_project_id ~ '?tab=decisions' if prefill_project_id is defined and prefill_project_id else ('/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> </div>
</form> </form>
</div> </div>

View File

@@ -45,9 +45,10 @@
</select> </select>
</div> </div>
{% if from_project is defined and from_project %}<input type="hidden" name="from_project" value="{{ from_project }}">{% endif %}
<div class="form-actions"> <div class="form-actions">
<button type="submit" class="btn btn-primary">Save Changes</button> <button type="submit" class="btn btn-primary">Save Changes</button>
<a href="/focus/{{ item.id }}" class="btn btn-secondary">Cancel</a> <a href="{{ '/projects/' ~ from_project ~ '?tab=focus' if from_project is defined and from_project else '/focus/' ~ item.id }}" class="btn btn-secondary">Cancel</a>
</div> </div>
</div> </div>
</form> </form>

View File

@@ -16,7 +16,8 @@
{% if prefill_task_id is defined and prefill_task_id %}<input type="hidden" name="task_id" value="{{ prefill_task_id }}">{% endif %} {% 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_meeting_id is defined and prefill_meeting_id %}<input type="hidden" name="meeting_id" value="{{ prefill_meeting_id }}">{% endif %}
{% if prefill_contact_id is defined and prefill_contact_id %}<input type="hidden" name="contact_id" value="{{ prefill_contact_id }}">{% endif %} {% if prefill_contact_id is defined and prefill_contact_id %}<input type="hidden" name="contact_id" value="{{ prefill_contact_id }}">{% endif %}
<div class="form-actions"><button type="submit" class="btn btn-primary">{{ 'Save' if item else 'Create' }}</button><a href="{{ '/contacts/' ~ prefill_contact_id if prefill_contact_id is defined and prefill_contact_id else ('/projects/' ~ prefill_project_id ~ '?tab=links' if prefill_project_id is defined and prefill_project_id else ('/tasks/' ~ prefill_task_id ~ '?tab=links' if prefill_task_id is defined and prefill_task_id else '/links')) }}" class="btn btn-secondary">Cancel</a></div> {% if from_project is defined and from_project %}<input type="hidden" name="from_project" value="{{ from_project }}">{% endif %}
<div class="form-actions"><button type="submit" class="btn btn-primary">{{ 'Save' if item else 'Create' }}</button><a href="{{ '/projects/' ~ from_project ~ '?tab=links' if from_project is defined and from_project else ('/contacts/' ~ prefill_contact_id if prefill_contact_id is defined and prefill_contact_id else ('/projects/' ~ prefill_project_id ~ '?tab=links' if prefill_project_id is defined and prefill_project_id else ('/tasks/' ~ prefill_task_id ~ '?tab=links' if prefill_task_id is defined and prefill_task_id else '/links'))) }}" class="btn btn-secondary">Cancel</a></div>
</div> </div>
</form></div> </form></div>
{% endblock %} {% endblock %}

View File

@@ -76,9 +76,10 @@
{% if prefill_task_id is defined and prefill_task_id %}<input type="hidden" name="task_id" value="{{ prefill_task_id }}">{% endif %} {% 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_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 %} {% if prefill_focus_id is defined and prefill_focus_id %}<input type="hidden" name="focus_id" value="{{ prefill_focus_id }}">{% endif %}
{% if from_project is defined and from_project %}<input type="hidden" name="from_project" value="{{ from_project }}">{% endif %}
<div class="form-actions"> <div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save Changes' if item else 'Create List' }}</button> <button type="submit" class="btn btn-primary">{{ 'Save Changes' if item else 'Create List' }}</button>
<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> <a href="{{ '/projects/' ~ from_project ~ '?tab=lists' if from_project is defined and from_project else ('/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 ('/projects/' ~ prefill_project_id ~ '?tab=lists' if prefill_project_id is defined and prefill_project_id and not item else ('/lists/' ~ item.id if item else '/lists')))) }}" class="btn btn-secondary">Cancel</a>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -93,9 +93,11 @@
</div> </div>
</div> </div>
{% if prefill_project_id is defined and prefill_project_id %}<input type="hidden" name="project_id" value="{{ prefill_project_id }}">{% endif %}
{% if from_project is defined and from_project %}<input type="hidden" name="from_project" value="{{ from_project }}">{% endif %}
<div class="form-actions"> <div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save Changes' if item else 'Create Meeting' }}</button> <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> <a href="{{ '/projects/' ~ from_project ~ '?tab=meetings' if from_project is defined and from_project else ('/projects/' ~ prefill_project_id ~ '?tab=meetings' if prefill_project_id is defined and prefill_project_id else ('/meetings/' ~ item.id if item else '/meetings')) }}" class="btn btn-secondary">Cancel</a>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -16,7 +16,8 @@
{% if prefill_task_id is defined and prefill_task_id %}<input type="hidden" name="task_id" value="{{ prefill_task_id }}">{% endif %} {% 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_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 %} {% 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> {% if from_project is defined and from_project %}<input type="hidden" name="from_project" value="{{ from_project }}">{% endif %}
<div class="form-actions"><button type="submit" class="btn btn-primary">{{ 'Save' if item else 'Create' }}</button><a href="{{ '/projects/' ~ from_project ~ '?tab=notes' if from_project is defined and from_project else ('/focus/' ~ prefill_focus_id ~ '?tab=notes' if prefill_focus_id is defined and prefill_focus_id else ('/projects/' ~ prefill_project_id ~ '?tab=notes' if prefill_project_id is defined and prefill_project_id and not item else ('/notes/' ~ item.id if item else '/notes'))) }}" class="btn btn-secondary">Cancel</a></div>
</div> </div>
</form></div> </form></div>
{% endblock %} {% endblock %}

View File

@@ -67,7 +67,7 @@
{% if t.due_date %}<span class="row-meta">{{ t.due_date }}</span>{% endif %} {% if t.due_date %}<span class="row-meta">{{ t.due_date }}</span>{% endif %}
<span class="status-badge status-{{ t.status }}">{{ t.status|replace('_', ' ') }}</span> <span class="status-badge status-{{ t.status }}">{{ t.status|replace('_', ' ') }}</span>
<div class="row-actions"> <div class="row-actions">
<a href="/tasks/{{ t.id }}/edit" class="btn btn-ghost btn-xs">Edit</a> <a href="/tasks/{{ t.id }}/edit?from_project={{ item.id }}" class="btn btn-ghost btn-xs">Edit</a>
<form action="/projects/{{ item.id }}/tasks/{{ t.id }}/unlink" method="post" style="display:inline"> <form action="/projects/{{ item.id }}/tasks/{{ t.id }}/unlink" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs">Unlink</button> <button class="btn btn-ghost btn-xs">Unlink</button>
</form> </form>
@@ -87,7 +87,7 @@
<span class="row-title"><a href="/notes/{{ n.id }}">{{ n.title }}</a></span> <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> <span class="row-meta">{{ n.updated_at.strftime('%Y-%m-%d') if n.updated_at else '' }}</span>
<div class="row-actions"> <div class="row-actions">
<a href="/notes/{{ n.id }}/edit" class="btn btn-ghost btn-xs">Edit</a> <a href="/notes/{{ n.id }}/edit?from_project={{ item.id }}" class="btn btn-ghost btn-xs">Edit</a>
<form action="/projects/{{ item.id }}/notes/{{ n.id }}/unlink" method="post" style="display:inline"> <form action="/projects/{{ item.id }}/notes/{{ n.id }}/unlink" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs">Unlink</button> <button class="btn btn-ghost btn-xs">Unlink</button>
</form> </form>
@@ -107,7 +107,7 @@
<span class="row-title"><a href="{{ l.url }}" target="_blank">{{ l.label }}</a></span> <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> <span class="row-meta">{{ l.url[:50] }}{% if l.url|length > 50 %}...{% endif %}</span>
<div class="row-actions"> <div class="row-actions">
<a href="/links/{{ l.id }}/edit" class="btn btn-ghost btn-xs">Edit</a> <a href="/links/{{ l.id }}/edit?from_project={{ item.id }}" class="btn btn-ghost btn-xs">Edit</a>
<form action="/projects/{{ item.id }}/links/{{ l.id }}/unlink" method="post" style="display:inline"> <form action="/projects/{{ item.id }}/links/{{ l.id }}/unlink" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs" title="Unlink from project">Unlink</button> <button class="btn btn-ghost btn-xs" title="Unlink from project">Unlink</button>
</form> </form>
@@ -146,7 +146,7 @@
<span class="row-title"><a href="/lists/{{ l.id }}">{{ l.name }}</a></span> <span class="row-title"><a href="/lists/{{ l.id }}">{{ l.name }}</a></span>
<span class="row-meta">{{ l.item_count }} items</span> <span class="row-meta">{{ l.item_count }} items</span>
<div class="row-actions"> <div class="row-actions">
<a href="/lists/{{ l.id }}/edit" class="btn btn-ghost btn-xs">Edit</a> <a href="/lists/{{ l.id }}/edit?from_project={{ item.id }}" class="btn btn-ghost btn-xs">Edit</a>
<form action="/projects/{{ item.id }}/lists/{{ l.id }}/unlink" method="post" style="display:inline"> <form action="/projects/{{ item.id }}/lists/{{ l.id }}/unlink" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs">Unlink</button> <button class="btn btn-ghost btn-xs">Unlink</button>
</form> </form>
@@ -166,7 +166,7 @@
<span class="row-title"><a href="/decisions/{{ d.id }}">{{ d.title }}</a></span> <span class="row-title"><a href="/decisions/{{ d.id }}">{{ d.title }}</a></span>
<span class="status-badge status-{{ d.status }}">{{ d.status }}</span> <span class="status-badge status-{{ d.status }}">{{ d.status }}</span>
<div class="row-actions"> <div class="row-actions">
<a href="/decisions/{{ d.id }}/edit" class="btn btn-ghost btn-xs">Edit</a> <a href="/decisions/{{ d.id }}/edit?from_project={{ item.id }}" class="btn btn-ghost btn-xs">Edit</a>
<form action="/projects/{{ item.id }}/decisions/{{ d.id }}/unlink" method="post" style="display:inline"> <form action="/projects/{{ item.id }}/decisions/{{ d.id }}/unlink" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs">Unlink</button> <button class="btn btn-ghost btn-xs">Unlink</button>
</form> </form>
@@ -180,7 +180,7 @@
{% endfor %} {% endfor %}
{% elif tab == 'meetings' %} {% elif tab == 'meetings' %}
<a href="/meetings/create" class="btn btn-ghost btn-sm mb-3">+ New Meeting</a> <a href="/meetings/create?project_id={{ item.id }}" class="btn btn-ghost btn-sm mb-3">+ New Meeting</a>
<div class="card mb-4"> <div class="card mb-4">
<form action="/projects/{{ item.id }}/meetings/add" method="post" class="flex gap-2 items-end" style="padding: 12px;"> <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;"> <div class="form-group" style="flex:1; margin:0;">
@@ -201,7 +201,7 @@
<span class="row-meta">{{ m.meeting_date }}</span> <span class="row-meta">{{ m.meeting_date }}</span>
<span class="status-badge status-{{ m.status }}">{{ m.status }}</span> <span class="status-badge status-{{ m.status }}">{{ m.status }}</span>
<div class="row-actions"> <div class="row-actions">
<a href="/meetings/{{ m.id }}/edit" class="btn btn-ghost btn-xs">Edit</a> <a href="/meetings/{{ m.id }}/edit?from_project={{ item.id }}" class="btn btn-ghost btn-xs">Edit</a>
<form action="/projects/{{ item.id }}/meetings/{{ m.id }}/remove" method="post" style="display:inline"> <form action="/projects/{{ item.id }}/meetings/{{ m.id }}/remove" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs">Unlink</button> <button class="btn btn-ghost btn-xs">Unlink</button>
</form> </form>
@@ -221,7 +221,7 @@
{% if f.domain_name %}<span class="row-tag" style="{% if f.domain_color %}border-color:{{ f.domain_color }};color:{{ f.domain_color }}{% endif %}">{{ f.domain_name }}</span>{% endif %} {% if f.domain_name %}<span class="row-tag" style="{% if f.domain_color %}border-color:{{ f.domain_color }};color:{{ f.domain_color }}{% endif %}">{{ f.domain_name }}</span>{% endif %}
<span class="row-meta">{{ f.created_at.strftime('%Y-%m-%d') if f.created_at else '' }}</span> <span class="row-meta">{{ f.created_at.strftime('%Y-%m-%d') if f.created_at else '' }}</span>
<div class="row-actions"> <div class="row-actions">
<a href="/focus/{{ f.id }}/edit" class="btn btn-ghost btn-xs">Edit</a> <a href="/focus/{{ f.id }}/edit?from_project={{ item.id }}" class="btn btn-ghost btn-xs">Edit</a>
<form action="/projects/{{ item.id }}/focus/{{ f.id }}/unlink" method="post" style="display:inline"> <form action="/projects/{{ item.id }}/focus/{{ f.id }}/unlink" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs">Unlink</button> <button class="btn btn-ghost btn-xs">Unlink</button>
</form> </form>
@@ -262,7 +262,7 @@
{% if c.role %}<span class="row-tag">{{ c.role }}</span>{% endif %} {% 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> <span class="row-meta">{{ c.linked_at.strftime('%Y-%m-%d') if c.linked_at else '' }}</span>
<div class="row-actions"> <div class="row-actions">
<a href="/contacts/{{ c.id }}/edit" class="btn btn-ghost btn-xs">Edit</a> <a href="/contacts/{{ c.id }}/edit?from_project={{ item.id }}" class="btn btn-ghost btn-xs">Edit</a>
<form action="/projects/{{ item.id }}/contacts/{{ c.id }}/remove" method="post" style="display:inline"> <form action="/projects/{{ item.id }}/contacts/{{ c.id }}/remove" method="post" style="display:inline">
<button class="btn btn-ghost btn-xs">Unlink</button> <button class="btn btn-ghost btn-xs">Unlink</button>
</form> </form>

View File

@@ -108,10 +108,11 @@
{% if prefill_parent_id %} {% if prefill_parent_id %}
<input type="hidden" name="parent_id" value="{{ prefill_parent_id }}"> <input type="hidden" name="parent_id" value="{{ prefill_parent_id }}">
{% endif %} {% endif %}
{% if from_project is defined and from_project %}<input type="hidden" name="from_project" value="{{ from_project }}">{% endif %}
<div class="form-actions"> <div class="form-actions">
<button type="submit" class="btn btn-primary">{{ 'Save Changes' if item else 'Create Task' }}</button> <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> <a href="{{ '/projects/' ~ from_project ~ '?tab=tasks' if from_project is defined and from_project else ('/tasks/' ~ item.id if item else '/tasks') }}" class="btn btn-secondary">Cancel</a>
</div> </div>
</div> </div>
</form> </form>