mirror of
http://88.130.71.182:3000/BlitTech/badoHair_be.git
synced 2026-06-13 12:12:56 +00:00
115 lines
4.5 KiB
Python
115 lines
4.5 KiB
Python
from fastapi import APIRouter, Depends, Query
|
|
import asyncpg
|
|
|
|
from app.core.responses import ok
|
|
from app.dependencies import get_db, require_admin
|
|
|
|
router = APIRouter(prefix="/stats", tags=["Admin — Dashboard"])
|
|
|
|
|
|
@router.get("/overview")
|
|
async def overview(
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
# Revenue
|
|
revenue_today = float(await db.fetchval(
|
|
"SELECT COALESCE(SUM(total_amount),0) FROM orders WHERE status IN ('paid','processing','shipped','delivered') AND DATE(created_at)=CURRENT_DATE"
|
|
) or 0)
|
|
revenue_week = float(await db.fetchval(
|
|
"SELECT COALESCE(SUM(total_amount),0) FROM orders WHERE status IN ('paid','processing','shipped','delivered') AND created_at>=date_trunc('week',now())"
|
|
) or 0)
|
|
revenue_month = float(await db.fetchval(
|
|
"SELECT COALESCE(SUM(total_amount),0) FROM orders WHERE status IN ('paid','processing','shipped','delivered') AND created_at>=date_trunc('month',now())"
|
|
) or 0)
|
|
|
|
# Booking counts — what the frontend dashboard shows
|
|
orders_pending = int(await db.fetchval("SELECT COUNT(*) FROM orders WHERE status='pending'") or 0)
|
|
bookings_pending = int(await db.fetchval("SELECT COUNT(*) FROM bookings WHERE status='pending'") or 0)
|
|
bookings_confirmed = int(await db.fetchval(
|
|
"SELECT COUNT(*) FROM bookings b JOIN time_slots ts ON ts.id=b.slot_id WHERE b.status='confirmed' AND ts.date>=CURRENT_DATE"
|
|
) or 0)
|
|
|
|
# Products
|
|
products_count = int(await db.fetchval("SELECT COUNT(*) FROM products WHERE is_hidden=false") or 0)
|
|
catalog_value = float(await db.fetchval(
|
|
"SELECT COALESCE(SUM(price),0) FROM products WHERE is_hidden=false"
|
|
) or 0)
|
|
low_stock_count = int(await db.fetchval(
|
|
"SELECT COUNT(*) FROM products WHERE stock_quantity<=5 AND is_hidden=false"
|
|
) or 0)
|
|
|
|
new_customers_month = int(await db.fetchval(
|
|
"SELECT COUNT(*) FROM profiles WHERE role='client' AND created_at>=date_trunc('month',now())"
|
|
) or 0)
|
|
|
|
return ok({
|
|
# Revenue stats
|
|
"revenue_today": revenue_today,
|
|
"revenue_week": revenue_week,
|
|
"revenue_month": revenue_month,
|
|
# Booking stats (matches frontend dashboard cards)
|
|
"orders_pending": orders_pending,
|
|
"bookings_pending": bookings_pending,
|
|
"bookings_confirmed": bookings_confirmed,
|
|
"bookings_upcoming": bookings_confirmed,
|
|
# Product stats (matches frontend dashboard cards)
|
|
"products_count": products_count,
|
|
"catalog_value": catalog_value,
|
|
"low_stock_count": low_stock_count,
|
|
# Customer stats
|
|
"new_customers_month": new_customers_month,
|
|
})
|
|
|
|
|
|
@router.get("/revenue")
|
|
async def revenue(
|
|
period: str = Query("month", pattern="^(today|week|month|year)$"),
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
trunc = {"today": "day", "week": "week", "month": "month", "year": "year"}[period]
|
|
rows = await db.fetch(
|
|
f"""
|
|
SELECT date_trunc('{trunc}', created_at) AS period,
|
|
COALESCE(SUM(total_amount), 0) AS revenue,
|
|
COUNT(*) AS orders_count
|
|
FROM orders
|
|
WHERE status IN ('paid','processing','shipped','delivered')
|
|
AND created_at >= date_trunc('{trunc}', now()) - interval '12 {trunc}s'
|
|
GROUP BY 1 ORDER BY 1 DESC
|
|
""",
|
|
)
|
|
booking_rows = await db.fetch(
|
|
f"""
|
|
SELECT date_trunc('{trunc}', b.created_at) AS period,
|
|
COALESCE(SUM(b.amount_paid), 0) AS revenue,
|
|
COUNT(*) AS bookings_count
|
|
FROM bookings b
|
|
WHERE b.status IN ('confirmed','completed')
|
|
AND b.created_at >= date_trunc('{trunc}', now()) - interval '12 {trunc}s'
|
|
GROUP BY 1 ORDER BY 1 DESC
|
|
""",
|
|
)
|
|
return ok({"orders": [dict(r) for r in rows], "bookings": [dict(r) for r in booking_rows]})
|
|
|
|
|
|
@router.get("/activity", tags=["Admin — Dashboard"])
|
|
async def activity(
|
|
limit: int = Query(20, ge=1, le=100),
|
|
db: asyncpg.Connection = Depends(get_db),
|
|
_: dict = Depends(require_admin),
|
|
):
|
|
rows = await db.fetch(
|
|
"""
|
|
SELECT al.id, p.full_name AS actor_name, al.action,
|
|
al.entity_type, al.entity_id, al.metadata, al.created_at
|
|
FROM activity_log al
|
|
LEFT JOIN profiles p ON p.id = al.actor_id
|
|
ORDER BY al.created_at DESC
|
|
LIMIT $1
|
|
""",
|
|
limit,
|
|
)
|
|
return ok([dict(r) for r in rows])
|