Initial Commit

This commit is contained in:
belviskhoremk
2026-05-12 00:34:21 +00:00
commit d2dc43b16f
57 changed files with 6056 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
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])