fix: Backend added along with missing pages
Some checks failed
Velocity-OS Deployment Pipeline / lint (push) Has been cancelled
Velocity-OS Deployment Pipeline / build-and-push (agents) (push) Has been cancelled
Velocity-OS Deployment Pipeline / build-and-push (core) (push) Has been cancelled
Velocity-OS Deployment Pipeline / build-and-push (media-engine) (push) Has been cancelled
Velocity-OS Deployment Pipeline / build-and-push (webos) (push) Has been cancelled
Velocity-OS Deployment Pipeline / sign-images (agents) (push) Has been cancelled
Velocity-OS Deployment Pipeline / sign-images (core) (push) Has been cancelled
Velocity-OS Deployment Pipeline / sign-images (media-engine) (push) Has been cancelled
Velocity-OS Deployment Pipeline / sign-images (webos) (push) Has been cancelled
Velocity-OS Deployment Pipeline / notify-ingress (push) Has been cancelled

This commit is contained in:
2026-05-01 14:42:42 +05:30
parent effd19531a
commit 70ef8578d0
60 changed files with 25045 additions and 30 deletions

View File

@@ -0,0 +1,114 @@
from __future__ import annotations
from typing import Any
from fastapi import APIRouter, Depends, HTTPException, Request
from backend.auth.dependencies import UserPrincipal, get_current_user
from backend.crm.canonical_schema import ensure_canonical_crm_schema
router = APIRouter(dependencies=[Depends(get_current_user)])
async def _pool(request: Request):
await ensure_canonical_crm_schema(request.app)
pool = getattr(request.app.state, "db_pool", None)
if pool is None:
raise HTTPException(status_code=503, detail="Database unavailable.")
return pool
def _money_inr(value: Any) -> str:
amount = float(value or 0)
if amount >= 10_000_000:
return f"{amount / 10_000_000:.1f}Cr"
if amount >= 100_000:
return f"{amount / 100_000:.1f}L"
return f"{amount:,.0f}"
@router.get("/dashboard/morning-brief")
async def morning_brief(request: Request, user: UserPrincipal = Depends(get_current_user)) -> dict[str, Any]:
"""Command pillar briefing derived from canonical CRM data."""
pool = await _pool(request)
async with pool.acquire() as conn:
people_count = await conn.fetchval("SELECT COUNT(*) FROM crm_people")
active_leads = await conn.fetchval(
"""
SELECT COUNT(*)
FROM crm_leads
WHERE status NOT IN ('booked', 'lost', 'dormant')
"""
)
open_pipeline = await conn.fetchval(
"""
SELECT COALESCE(SUM(value), 0)
FROM crm_opportunities
WHERE stage NOT IN ('closed_won', 'closed_lost')
"""
)
avg_qd = await conn.fetchval("SELECT COALESCE(AVG(current_value), 0) FROM intel_qd_scores")
overdue_tasks = await conn.fetchval(
"""
SELECT COUNT(*)
FROM intel_reminders
WHERE status IN ('pending', 'confirmed') AND due_at < NOW()
"""
)
stages = await conn.fetch(
"""
SELECT status::text AS id, INITCAP(REPLACE(status::text, '_', ' ')) AS label, COUNT(*)::int AS count
FROM crm_leads
GROUP BY status
ORDER BY MIN(created_at) NULLS LAST, status::text
"""
)
actions = await conn.fetch(
"""
SELECT p.person_id::text, p.full_name, n.recommended_action, n.priority, n.rationale,
COALESCE(q.current_value, 0)::float AS qd_score
FROM read_next_best_action n
JOIN crm_people p ON p.person_id = n.person_id
LEFT JOIN LATERAL (
SELECT current_value
FROM intel_qd_scores
WHERE person_id = p.person_id
ORDER BY current_value DESC NULLS LAST
LIMIT 1
) q ON TRUE
ORDER BY
CASE n.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END,
qd_score DESC,
n.computed_at DESC NULLS LAST
LIMIT 3
"""
)
priority_cards = []
for row in actions:
urgency = (row["priority"] or "medium").lower()
if urgency not in {"high", "medium", "low"}:
urgency = "high" if urgency == "critical" else "medium"
priority_cards.append(
{
"id": row["person_id"],
"type": "follow_up",
"headline": row["recommended_action"] or "Follow up with client",
"sublabel": row["rationale"],
"personId": row["person_id"],
"personName": row["full_name"],
"cta": "Open client",
"urgency": urgency,
}
)
return {
"kpis": [
{"label": "Clients", "value": str(int(people_count or 0)), "sublabel": "canonical CRM records"},
{"label": "Active leads", "value": str(int(active_leads or 0)), "sublabel": "not booked/lost/dormant"},
{"label": "Open pipeline", "value": _money_inr(open_pipeline), "sublabel": f"avg QD {float(avg_qd or 0):.0f}"},
{"label": "Overdue follow-ups", "value": str(int(overdue_tasks or 0)), "deltaPositive": False},
],
"priorityCards": priority_cards,
"pipelineStages": [dict(row) for row in stages],
}