various enhancements for new tabs and bug fixes
This commit is contained in:
@@ -107,61 +107,118 @@ async def create_meeting(
|
||||
|
||||
|
||||
@router.get("/{meeting_id}")
|
||||
async def meeting_detail(meeting_id: str, request: Request, db: AsyncSession = Depends(get_db)):
|
||||
async def meeting_detail(
|
||||
meeting_id: str, request: Request,
|
||||
tab: str = "overview",
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
repo = BaseRepository("meetings", db)
|
||||
sidebar = await get_sidebar_data(db)
|
||||
item = await repo.get(meeting_id)
|
||||
if not item:
|
||||
return RedirectResponse(url="/meetings", status_code=303)
|
||||
|
||||
# Action items (tasks linked to this meeting)
|
||||
result = await db.execute(text("""
|
||||
SELECT t.*, mt.source,
|
||||
d.name as domain_name, d.color as domain_color,
|
||||
p.name as project_name
|
||||
FROM meeting_tasks mt
|
||||
JOIN tasks t ON mt.task_id = t.id
|
||||
LEFT JOIN domains d ON t.domain_id = d.id
|
||||
LEFT JOIN projects p ON t.project_id = p.id
|
||||
WHERE mt.meeting_id = :mid AND t.is_deleted = false
|
||||
ORDER BY t.sort_order, t.created_at
|
||||
"""), {"mid": meeting_id})
|
||||
action_items = [dict(r._mapping) for r in result]
|
||||
# Overview data (always needed for overview tab)
|
||||
action_items = []
|
||||
decisions = []
|
||||
domains = []
|
||||
tab_data = []
|
||||
all_contacts = []
|
||||
|
||||
# Notes linked to this meeting
|
||||
result = await db.execute(text("""
|
||||
SELECT * FROM notes
|
||||
WHERE meeting_id = :mid AND is_deleted = false
|
||||
ORDER BY created_at
|
||||
"""), {"mid": meeting_id})
|
||||
meeting_notes = [dict(r._mapping) for r in result]
|
||||
if tab == "overview":
|
||||
# Action items
|
||||
result = await db.execute(text("""
|
||||
SELECT t.*, mt.source,
|
||||
d.name as domain_name, d.color as domain_color,
|
||||
p.name as project_name
|
||||
FROM meeting_tasks mt
|
||||
JOIN tasks t ON mt.task_id = t.id
|
||||
LEFT JOIN domains d ON t.domain_id = d.id
|
||||
LEFT JOIN projects p ON t.project_id = p.id
|
||||
WHERE mt.meeting_id = :mid AND t.is_deleted = false
|
||||
ORDER BY t.sort_order, t.created_at
|
||||
"""), {"mid": meeting_id})
|
||||
action_items = [dict(r._mapping) for r in result]
|
||||
|
||||
# Decisions from this meeting
|
||||
result = await db.execute(text("""
|
||||
SELECT * FROM decisions
|
||||
WHERE meeting_id = :mid AND is_deleted = false
|
||||
ORDER BY created_at
|
||||
"""), {"mid": meeting_id})
|
||||
decisions = [dict(r._mapping) for r in result]
|
||||
# Decisions from this meeting
|
||||
result = await db.execute(text("""
|
||||
SELECT * FROM decisions
|
||||
WHERE meeting_id = :mid AND is_deleted = false
|
||||
ORDER BY created_at
|
||||
"""), {"mid": meeting_id})
|
||||
decisions = [dict(r._mapping) for r in result]
|
||||
|
||||
# Attendees
|
||||
result = await db.execute(text("""
|
||||
SELECT c.*, cm.role FROM contact_meetings cm
|
||||
JOIN contacts c ON cm.contact_id = c.id
|
||||
WHERE cm.meeting_id = :mid AND c.is_deleted = false
|
||||
ORDER BY c.first_name
|
||||
"""), {"mid": meeting_id})
|
||||
attendees = [dict(r._mapping) for r in result]
|
||||
# Domains for action item creation
|
||||
domains_repo = BaseRepository("domains", db)
|
||||
domains = await domains_repo.list()
|
||||
|
||||
# Domains for action item creation
|
||||
domains_repo = BaseRepository("domains", db)
|
||||
domains = await domains_repo.list()
|
||||
elif tab == "notes":
|
||||
result = await db.execute(text("""
|
||||
SELECT * FROM notes
|
||||
WHERE meeting_id = :mid AND is_deleted = false
|
||||
ORDER BY updated_at DESC
|
||||
"""), {"mid": meeting_id})
|
||||
tab_data = [dict(r._mapping) for r in result]
|
||||
|
||||
elif tab == "weblinks":
|
||||
result = await db.execute(text("""
|
||||
SELECT * FROM weblinks
|
||||
WHERE meeting_id = :mid AND is_deleted = false
|
||||
ORDER BY sort_order, label
|
||||
"""), {"mid": meeting_id})
|
||||
tab_data = [dict(r._mapping) for r in result]
|
||||
|
||||
elif tab == "files":
|
||||
result = await db.execute(text("""
|
||||
SELECT f.* FROM files f
|
||||
JOIN file_mappings fm ON fm.file_id = f.id
|
||||
WHERE fm.context_type = 'meeting' AND fm.context_id = :mid AND f.is_deleted = false
|
||||
ORDER BY f.created_at DESC
|
||||
"""), {"mid": meeting_id})
|
||||
tab_data = [dict(r._mapping) for r in result]
|
||||
|
||||
elif tab == "lists":
|
||||
result = await db.execute(text("""
|
||||
SELECT l.*,
|
||||
(SELECT count(*) FROM list_items li WHERE li.list_id = l.id AND li.is_deleted = false) as item_count
|
||||
FROM lists l
|
||||
WHERE l.meeting_id = :mid AND l.is_deleted = false
|
||||
ORDER BY l.sort_order, l.created_at DESC
|
||||
"""), {"mid": meeting_id})
|
||||
tab_data = [dict(r._mapping) for r in result]
|
||||
|
||||
elif tab == "contacts":
|
||||
result = await db.execute(text("""
|
||||
SELECT c.*, cm.role, cm.created_at as linked_at
|
||||
FROM contacts c
|
||||
JOIN contact_meetings cm ON cm.contact_id = c.id
|
||||
WHERE cm.meeting_id = :mid AND c.is_deleted = false
|
||||
ORDER BY c.first_name
|
||||
"""), {"mid": meeting_id})
|
||||
tab_data = [dict(r._mapping) for r in result]
|
||||
result = await db.execute(text("""
|
||||
SELECT id, first_name, last_name FROM contacts
|
||||
WHERE is_deleted = false ORDER BY first_name
|
||||
"""))
|
||||
all_contacts = [dict(r._mapping) for r in result]
|
||||
|
||||
# Tab counts
|
||||
counts = {}
|
||||
for count_tab, count_sql in [
|
||||
("notes", "SELECT count(*) FROM notes WHERE meeting_id = :mid AND is_deleted = false"),
|
||||
("weblinks", "SELECT count(*) FROM weblinks WHERE meeting_id = :mid AND is_deleted = false"),
|
||||
("files", "SELECT count(*) FROM files f JOIN file_mappings fm ON fm.file_id = f.id WHERE fm.context_type = 'meeting' AND fm.context_id = :mid AND f.is_deleted = false"),
|
||||
("lists", "SELECT count(*) FROM lists WHERE meeting_id = :mid AND is_deleted = false"),
|
||||
("contacts", "SELECT count(*) FROM contacts c JOIN contact_meetings cm ON cm.contact_id = c.id WHERE cm.meeting_id = :mid AND c.is_deleted = false"),
|
||||
]:
|
||||
result = await db.execute(text(count_sql), {"mid": meeting_id})
|
||||
counts[count_tab] = result.scalar() or 0
|
||||
|
||||
return templates.TemplateResponse("meeting_detail.html", {
|
||||
"request": request, "sidebar": sidebar, "item": item,
|
||||
"action_items": action_items, "meeting_notes": meeting_notes,
|
||||
"decisions": decisions, "attendees": attendees,
|
||||
"domains": domains,
|
||||
"action_items": action_items, "decisions": decisions,
|
||||
"domains": domains, "tab": tab, "tab_data": tab_data,
|
||||
"all_contacts": all_contacts, "counts": counts,
|
||||
"page_title": item["title"], "active_nav": "meetings",
|
||||
})
|
||||
|
||||
@@ -266,3 +323,30 @@ async def create_action_item(
|
||||
"""), {"mid": meeting_id, "tid": task["id"]})
|
||||
|
||||
return RedirectResponse(url=f"/meetings/{meeting_id}", status_code=303)
|
||||
|
||||
|
||||
# ---- Contact linking ----
|
||||
|
||||
@router.post("/{meeting_id}/contacts/add")
|
||||
async def add_contact(
|
||||
meeting_id: str,
|
||||
contact_id: str = Form(...),
|
||||
role: str = Form("attendee"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
await db.execute(text("""
|
||||
INSERT INTO contact_meetings (contact_id, meeting_id, role)
|
||||
VALUES (:cid, :mid, :role) ON CONFLICT DO NOTHING
|
||||
"""), {"cid": contact_id, "mid": meeting_id, "role": role if role and role.strip() else "attendee"})
|
||||
return RedirectResponse(url=f"/meetings/{meeting_id}?tab=contacts", status_code=303)
|
||||
|
||||
|
||||
@router.post("/{meeting_id}/contacts/{contact_id}/remove")
|
||||
async def remove_contact(
|
||||
meeting_id: str, contact_id: str,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
await db.execute(text(
|
||||
"DELETE FROM contact_meetings WHERE contact_id = :cid AND meeting_id = :mid"
|
||||
), {"cid": contact_id, "mid": meeting_id})
|
||||
return RedirectResponse(url=f"/meetings/{meeting_id}?tab=contacts", status_code=303)
|
||||
|
||||
Reference in New Issue
Block a user