2026-01-29 23:07:33 +01:00
|
|
|
import logging
|
2026-01-31 23:16:24 +01:00
|
|
|
from fastapi import APIRouter, HTTPException, Query, Request
|
2026-01-29 23:07:33 +01:00
|
|
|
from fastapi.responses import HTMLResponse
|
|
|
|
|
from fastapi.templating import Jinja2Templates
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from app.core.database import execute_query
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
2026-01-31 23:16:24 +01:00
|
|
|
# Setup template directory - must be root "app" to allow extending shared/frontend/base.html
|
|
|
|
|
templates = Jinja2Templates(directory="app")
|
|
|
|
|
|
|
|
|
|
@router.get("/cases", response_class=HTMLResponse)
|
|
|
|
|
async def case_list(request: Request, status: str = Query(None), tag: str = Query(None), customer_id: int = Query(None)):
|
2026-01-29 23:07:33 +01:00
|
|
|
"""Display list of all cases."""
|
2026-01-31 23:16:24 +01:00
|
|
|
query = "SELECT * FROM sag_sager WHERE deleted_at IS NULL"
|
|
|
|
|
params = []
|
|
|
|
|
|
|
|
|
|
if status:
|
|
|
|
|
query += " AND status = %s"
|
|
|
|
|
params.append(status)
|
|
|
|
|
if customer_id:
|
|
|
|
|
query += " AND customer_id = %s"
|
|
|
|
|
params.append(customer_id)
|
|
|
|
|
|
|
|
|
|
query += " ORDER BY created_at DESC"
|
|
|
|
|
cases = execute_query(query, tuple(params))
|
|
|
|
|
|
|
|
|
|
# Fetch available statuses for filter dropdown
|
|
|
|
|
statuses_query = "SELECT DISTINCT status FROM sag_sager WHERE deleted_at IS NULL ORDER BY status"
|
|
|
|
|
statuses_result = execute_query(statuses_query)
|
|
|
|
|
statuses = [row["status"] for row in statuses_result] if statuses_result else []
|
|
|
|
|
|
|
|
|
|
# Fetch available tags for filter dropdown
|
|
|
|
|
tags_query = "SELECT DISTINCT tag_navn FROM sag_tags WHERE deleted_at IS NULL ORDER BY tag_navn"
|
|
|
|
|
tags_result = execute_query(tags_query)
|
|
|
|
|
all_tags = [row["tag_navn"] for row in tags_result] if tags_result else []
|
|
|
|
|
|
|
|
|
|
# Filter by tag if provided
|
|
|
|
|
if tag and cases:
|
|
|
|
|
case_ids = [case['id'] for case in cases]
|
|
|
|
|
tag_query = "SELECT sag_id FROM sag_tags WHERE tag_navn = %s AND deleted_at IS NULL"
|
|
|
|
|
tagged = execute_query(tag_query, (tag,))
|
|
|
|
|
tagged_ids = set(t['sag_id'] for t in tagged)
|
|
|
|
|
cases = [case for case in cases if case['id'] in tagged_ids]
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("modules/sag/templates/index.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"sager": cases,
|
|
|
|
|
"statuses": statuses,
|
|
|
|
|
"all_tags": all_tags,
|
|
|
|
|
"current_status": status,
|
|
|
|
|
"current_tag": tag
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
@router.get("/cases/new", response_class=HTMLResponse)
|
|
|
|
|
async def create_case_form_cases(request: Request):
|
|
|
|
|
"""Display create case form."""
|
|
|
|
|
return templates.TemplateResponse("modules/sag/templates/create.html", {
|
|
|
|
|
"request": request
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
@router.get("/cases/{case_id}", response_class=HTMLResponse)
|
|
|
|
|
async def case_details(request: Request, case_id: int):
|
2026-01-29 23:07:33 +01:00
|
|
|
"""Display case details."""
|
2026-01-31 23:16:24 +01:00
|
|
|
case_query = "SELECT * FROM sag_sager WHERE id = %s AND deleted_at IS NULL"
|
|
|
|
|
case_result = execute_query(case_query, (case_id,))
|
|
|
|
|
|
|
|
|
|
if not case_result:
|
|
|
|
|
return HTMLResponse(content="<h1>Case not found</h1>", status_code=404)
|
|
|
|
|
|
|
|
|
|
case = case_result[0]
|
|
|
|
|
|
|
|
|
|
# Fetch tags
|
|
|
|
|
tags_query = "SELECT * FROM sag_tags WHERE sag_id = %s AND deleted_at IS NULL ORDER BY created_at DESC"
|
|
|
|
|
tags = execute_query(tags_query, (case_id,))
|
|
|
|
|
|
|
|
|
|
# Fetch relations
|
|
|
|
|
relations_query = """
|
|
|
|
|
SELECT sr.*,
|
|
|
|
|
ss_kilde.titel AS kilde_titel,
|
|
|
|
|
ss_mål.titel AS mål_titel
|
|
|
|
|
FROM sag_relationer sr
|
|
|
|
|
JOIN sag_sager ss_kilde ON sr.kilde_sag_id = ss_kilde.id
|
|
|
|
|
JOIN sag_sager ss_mål ON sr.målsag_id = ss_mål.id
|
|
|
|
|
WHERE (sr.kilde_sag_id = %s OR sr.målsag_id = %s)
|
|
|
|
|
AND sr.deleted_at IS NULL
|
|
|
|
|
ORDER BY sr.created_at DESC
|
|
|
|
|
"""
|
|
|
|
|
relations = execute_query(relations_query, (case_id, case_id))
|
|
|
|
|
|
|
|
|
|
# Fetch linked contacts
|
|
|
|
|
contacts_query = """
|
|
|
|
|
SELECT sk.*, CONCAT(c.first_name, ' ', c.last_name) as contact_name, c.email as contact_email
|
|
|
|
|
FROM sag_kontakter sk
|
|
|
|
|
LEFT JOIN contacts c ON sk.contact_id = c.id
|
|
|
|
|
WHERE sk.sag_id = %s AND sk.deleted_at IS NULL
|
|
|
|
|
ORDER BY sk.created_at DESC
|
|
|
|
|
"""
|
|
|
|
|
contacts = execute_query(contacts_query, (case_id,))
|
|
|
|
|
|
|
|
|
|
# Fetch linked customers
|
|
|
|
|
customers_query = """
|
|
|
|
|
SELECT sk.*, c.name as customer_name, c.email as customer_email
|
|
|
|
|
FROM sag_kunder sk
|
|
|
|
|
LEFT JOIN customers c ON sk.customer_id = c.id
|
|
|
|
|
WHERE sk.sag_id = %s AND sk.deleted_at IS NULL
|
|
|
|
|
ORDER BY sk.created_at DESC
|
|
|
|
|
"""
|
|
|
|
|
customers = execute_query(customers_query, (case_id,))
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("modules/sag/templates/detail.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"case": case,
|
|
|
|
|
"tags": tags,
|
|
|
|
|
"relations": relations,
|
|
|
|
|
"contacts": contacts or [],
|
|
|
|
|
"customers": customers or []
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
@router.get("/cases/{case_id}/edit", response_class=HTMLResponse)
|
|
|
|
|
async def edit_case_form(request: Request, case_id: int):
|
|
|
|
|
"""Display edit case form."""
|
|
|
|
|
case_query = "SELECT * FROM sag_sager WHERE id = %s AND deleted_at IS NULL"
|
|
|
|
|
case_result = execute_query(case_query, (case_id,))
|
|
|
|
|
|
|
|
|
|
if not case_result:
|
|
|
|
|
return HTMLResponse(content="<h1>Case not found</h1>", status_code=404)
|
|
|
|
|
|
|
|
|
|
case = case_result[0]
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("modules/sag/templates/edit.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"case": case
|
|
|
|
|
})
|