198 lines
8.9 KiB
Python
198 lines
8.9 KiB
Python
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Form, Request
|
|
from fastapi.responses import RedirectResponse
|
|
|
|
from app.core.db.common import CommonSessionLocal
|
|
from app.core.http_responses import ui_access_denied
|
|
from app.core.security.csrf import get_or_create_csrf_token, validate_csrf
|
|
from app.core.security.session_auth import get_current_user
|
|
from app.core.templating import templates
|
|
from app.modules.core.rbac.deps import get_user_permissions, get_user_roles
|
|
from app.modules.manager_dashboard.service import (
|
|
build_manager_dashboard_payload,
|
|
can_access_manager_dashboard,
|
|
get_manager_review_workspace,
|
|
get_next_manager_review_task_id,
|
|
)
|
|
from app.modules.services.execution import apply_task_review
|
|
from app.modules.alerts.workflow_escalations import update_workflow_escalation
|
|
|
|
router = APIRouter(prefix="/manager", tags=["manager-dashboard-v2-ui"])
|
|
|
|
VALID_TABS = {
|
|
"overview": "modules/manager_dashboard/templates/manager_dashboard/partials/overview.html",
|
|
"team-work": "modules/manager_dashboard/templates/manager_dashboard/partials/team_work.html",
|
|
"due-calendar": "modules/manager_dashboard/templates/manager_dashboard/partials/due_calendar.html",
|
|
"review-queue": "modules/manager_dashboard/templates/manager_dashboard/partials/review_queue.html",
|
|
"client-pending": "modules/manager_dashboard/templates/manager_dashboard/partials/client_pending.html",
|
|
"documents": "modules/manager_dashboard/templates/manager_dashboard/partials/documents.html",
|
|
"escalations": "modules/manager_dashboard/templates/manager_dashboard/partials/escalations.html",
|
|
"reports": "modules/manager_dashboard/templates/manager_dashboard/partials/reports.html",
|
|
"wizards": "modules/manager_dashboard/templates/manager_dashboard/partials/wizards.html",
|
|
}
|
|
|
|
|
|
def _ctx(request: Request, db, current_user, *, active_tab: str = "overview"):
|
|
payload = build_manager_dashboard_payload(db, request, current_user)
|
|
return {
|
|
"request": request,
|
|
"current_user": current_user,
|
|
"current_user_roles": get_user_roles(db, current_user.id),
|
|
"current_user_permissions": get_user_permissions(db, current_user.id),
|
|
"csrf_token": get_or_create_csrf_token(request),
|
|
"title": "Manager Dashboard",
|
|
"active_tab": active_tab,
|
|
**payload,
|
|
}
|
|
|
|
|
|
@router.get("/dashboard")
|
|
def dashboard(request: Request, tab: str = "overview"):
|
|
db = CommonSessionLocal()
|
|
try:
|
|
current_user = get_current_user(request, db=db)
|
|
if not current_user:
|
|
return RedirectResponse(url="/login", status_code=303)
|
|
if not can_access_manager_dashboard(db, current_user):
|
|
return ui_access_denied()
|
|
active_tab = tab if tab in VALID_TABS else "overview"
|
|
return templates.TemplateResponse(
|
|
"modules/manager_dashboard/templates/manager_dashboard/dashboard.html",
|
|
_ctx(request, db, current_user, active_tab=active_tab),
|
|
)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.get("/dashboard/tab/{tab_name}")
|
|
def dashboard_tab(request: Request, tab_name: str):
|
|
db = CommonSessionLocal()
|
|
try:
|
|
current_user = get_current_user(request, db=db)
|
|
if not current_user:
|
|
return RedirectResponse(url="/login", status_code=303)
|
|
if not can_access_manager_dashboard(db, current_user):
|
|
return ui_access_denied()
|
|
active_tab = tab_name if tab_name in VALID_TABS else "overview"
|
|
return templates.TemplateResponse(
|
|
VALID_TABS[active_tab],
|
|
_ctx(request, db, current_user, active_tab=active_tab),
|
|
)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.get("/reviews/engagements/{subscription_id}")
|
|
def manager_review_workspace(request: Request, subscription_id: int, task_id: int | None = None):
|
|
db = CommonSessionLocal()
|
|
try:
|
|
current_user = get_current_user(request, db=db)
|
|
if not current_user:
|
|
return RedirectResponse(url="/login", status_code=303)
|
|
if not can_access_manager_dashboard(db, current_user):
|
|
return ui_access_denied()
|
|
workspace = get_manager_review_workspace(
|
|
db, request, current_user,
|
|
subscription_id=subscription_id,
|
|
active_task_id=task_id,
|
|
)
|
|
if not workspace:
|
|
return RedirectResponse(url="/manager/dashboard?tab=review-queue", status_code=303)
|
|
return templates.TemplateResponse(
|
|
"modules/manager_dashboard/templates/manager_dashboard/manager_review_workspace.html",
|
|
{
|
|
"request": request,
|
|
"current_user": current_user,
|
|
"current_user_roles": get_user_roles(db, current_user.id),
|
|
"current_user_permissions": get_user_permissions(db, current_user.id),
|
|
"csrf_token": get_or_create_csrf_token(request),
|
|
"title": "Manager Engagement Review",
|
|
"workspace": workspace,
|
|
},
|
|
)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.post("/reviews/engagements/{subscription_id}/tasks/{task_id}")
|
|
def manager_review_task_submit(
|
|
request: Request,
|
|
subscription_id: int,
|
|
task_id: int,
|
|
decision: str = Form("reviewed"),
|
|
review_note: str = Form(""),
|
|
workflow_action: str = Form("save"),
|
|
csrf_token: str = Form(...),
|
|
):
|
|
validate_csrf(request, csrf_token)
|
|
db = CommonSessionLocal()
|
|
try:
|
|
current_user = get_current_user(request, db=db)
|
|
if not current_user:
|
|
return RedirectResponse(url="/login", status_code=303)
|
|
if not can_access_manager_dashboard(db, current_user):
|
|
return ui_access_denied()
|
|
workspace = get_manager_review_workspace(
|
|
db, request, current_user,
|
|
subscription_id=subscription_id,
|
|
active_task_id=task_id,
|
|
)
|
|
if not workspace:
|
|
return RedirectResponse(url="/manager/dashboard?tab=review-queue", status_code=303)
|
|
active = workspace.get("active_task") or {}
|
|
if int(active.get("id") or 0) != int(task_id):
|
|
return RedirectResponse(url=f"/manager/reviews/engagements/{subscription_id}?review_error=task", status_code=303)
|
|
if active.get("is_locked") or workspace.get("is_locked"):
|
|
return RedirectResponse(url=f"/manager/reviews/engagements/{subscription_id}?task_id={task_id}&review_error=locked", status_code=303)
|
|
if active.get("review_state") not in {"pending_review", "reviewed", "rework"}:
|
|
return RedirectResponse(url=f"/manager/reviews/engagements/{subscription_id}?task_id={task_id}&review_error=not_ready", status_code=303)
|
|
|
|
# Reload the ORM object from the scoped workspace query result.
|
|
from app.modules.services.models import ClientServiceTaskInstance
|
|
from sqlalchemy import select
|
|
task = db.execute(
|
|
select(ClientServiceTaskInstance).where(
|
|
ClientServiceTaskInstance.id == int(task_id),
|
|
ClientServiceTaskInstance.subscription_id == int(subscription_id),
|
|
)
|
|
).scalar_one_or_none()
|
|
if not task:
|
|
return RedirectResponse(url="/manager/dashboard?tab=review-queue", status_code=303)
|
|
try:
|
|
apply_task_review(
|
|
db,
|
|
task=task,
|
|
review_level="manager",
|
|
decision=decision,
|
|
note=review_note,
|
|
user_id=current_user.id,
|
|
)
|
|
db.commit()
|
|
except ValueError:
|
|
db.rollback()
|
|
return RedirectResponse(url=f"/manager/reviews/engagements/{subscription_id}?task_id={task_id}&review_error=validation", status_code=303)
|
|
|
|
if workflow_action == "save_next":
|
|
refreshed = get_manager_review_workspace(db, request, current_user, subscription_id=subscription_id)
|
|
next_id = get_next_manager_review_task_id(refreshed, task_id) if refreshed else None
|
|
if next_id:
|
|
return RedirectResponse(url=f"/manager/reviews/engagements/{subscription_id}?task_id={next_id}&reviewed=1", status_code=303)
|
|
return RedirectResponse(url=f"/manager/reviews/engagements/{subscription_id}?task_id={task_id}&reviewed=1", status_code=303)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.post("/escalations/{escalation_id}/action")
|
|
def manager_escalation_action(request: Request, escalation_id: int, action: str = Form(...), resolution_note: str = Form(""), csrf_token: str = Form(...)):
|
|
validate_csrf(request, csrf_token)
|
|
db=CommonSessionLocal()
|
|
try:
|
|
current_user=get_current_user(request, db=db)
|
|
if not current_user: return RedirectResponse(url="/login",status_code=303)
|
|
if not can_access_manager_dashboard(db,current_user): return ui_access_denied()
|
|
try: update_workflow_escalation(db, escalation_id=escalation_id, actor_user_id=current_user.id, action=action, resolution_note=resolution_note); db.commit()
|
|
except ValueError: db.rollback()
|
|
return RedirectResponse(url="/manager/dashboard?tab=escalations",status_code=303)
|
|
finally: db.close()
|