""" app/tenant/dashboard/routes.py Dashboard with Phase 3 KPI widgets scoped to active location. """ import logging from datetime import datetime, timezone, timedelta, date from decimal import Decimal from flask import Blueprint, render_template, g from flask_login import login_required from app.extensions import db from app.models.salon import ( Appointment, Transaction, Staff, CheckinQueue, StaffClocking, ) from app.decorators import require_role from sqlalchemy import func logger = logging.getLogger(__name__) dashboard_bp = Blueprint("dashboard", __name__) @dashboard_bp.route("/") @login_required @require_role("tenant_admin", "tenant_manager") def index(): today = date.today() day_start = datetime.combine(today, datetime.min.time()).replace(tzinfo=timezone.utc) day_end = day_start + timedelta(days=1) location_id = g.location.id if g.location else None # ── Today's revenue ────────────────────────────────────── revenue_row = db.session.query( func.sum(Transaction.total) ).filter_by( tenant_id=g.tenant.id, location_id=location_id ).filter( Transaction.created_at >= day_start, Transaction.created_at < day_end, Transaction.voided_at.is_(None), ).scalar() today_revenue = float(revenue_row or 0) # ── Today's appointments ───────────────────────────────── appt_counts = dict( db.session.query(Appointment.status, func.count(Appointment.id)) .filter_by(tenant_id=g.tenant.id, location_id=location_id) .filter( Appointment.start_time >= day_start, Appointment.start_time < day_end, ) .group_by(Appointment.status) .all() ) total_appts = sum(appt_counts.values()) completed_appts = appt_counts.get("completed", 0) pending_appts = appt_counts.get("pending", 0) + appt_counts.get("confirmed", 0) # ── Staff on shift ──────────────────────────────────────── staff_on_shift = StaffClocking.query.filter_by( tenant_id=g.tenant.id, location_id=location_id ).filter(StaffClocking.clocked_out_at.is_(None)).count() # ── Check-in queue ───────────────────────────────────────── queue_count = CheckinQueue.query.filter_by( tenant_id=g.tenant.id, location_id=location_id, status="waiting" ).count() # ── Upcoming appointments today (next 3) ────────────────── now = datetime.now(timezone.utc) upcoming = Appointment.query.filter_by( tenant_id=g.tenant.id, location_id=location_id ).filter( Appointment.start_time >= now, Appointment.start_time < day_end, Appointment.status.in_(["pending", "confirmed"]), ).order_by(Appointment.start_time).limit(5).all() kpis = { "today_revenue": today_revenue, "total_appointments": total_appts, "completed_appointments": completed_appts, "pending_appointments": pending_appts, "staff_on_shift": staff_on_shift, "queue_count": queue_count, } return render_template( "tenant/dashboard/index.html", tenant=g.tenant, location=g.location, kpis=kpis, upcoming_appointments=upcoming, today=today, )