"""Dashboard API endpoints.""" from flask import Blueprint, current_app from flask_jwt_extended import jwt_required from shopdb.extensions import db from shopdb.core.models import Asset, AssetType, AssetStatus from shopdb.utils.responses import success_response dashboard_bp = Blueprint('dashboard', __name__) # Map asset type name -> dashboard category label _TYPE_CATEGORY = { 'equipment': 'Equipment', 'computer': 'PC', 'printer': 'Printer', 'network_device': 'Network', } def _count_by_type(assettype): return db.session.query(Asset).join(AssetType).filter( Asset.isactive == True, AssetType.assettype == assettype ).count() @dashboard_bp.route('/summary', methods=['GET']) @dashboard_bp.route('', methods=['GET']) @jwt_required(optional=True) def get_dashboard(): """Get dashboard summary data (asset-based).""" equipment_count = _count_by_type('equipment') pc_count = _count_by_type('computer') network_count = _count_by_type('network_device') printer_count = _count_by_type('printer') total = equipment_count + pc_count + network_count + printer_count # Count by status status_counts = db.session.query( AssetStatus.status, db.func.count(Asset.assetid) ).outerjoin( Asset, db.and_(Asset.statusid == AssetStatus.statusid, Asset.isactive == True) ).group_by(AssetStatus.status).all() status_dict = {status: count for status, count in status_counts} # Recent assets recent = Asset.query.filter_by(isactive=True).order_by( Asset.createddate.desc() ).limit(10).all() return success_response({ # Fields expected by frontend 'totalmachines': total, 'totalequipment': equipment_count, 'totalpc': pc_count, 'totalnetwork': network_count, 'totalprinter': printer_count, 'activemachines': status_dict.get('In Use', 0), 'inrepair': status_dict.get('In Repair', 0), # Structured data 'counts': { 'equipment': equipment_count, 'pcs': pc_count, 'networkdevices': network_count, 'printers': printer_count, 'total': total }, 'bystatus': status_dict, 'recent': [ { 'assetid': a.assetid, 'assetnumber': a.assetnumber, 'assettype': a.assettype.assettype if a.assettype else None, 'createddate': a.createddate.isoformat() + 'Z' if a.createddate else None } for a in recent ] }) @dashboard_bp.route('/stats', methods=['GET']) @jwt_required(optional=True) def get_stats(): """Get detailed statistics by asset type.""" type_counts = db.session.query( AssetType.assettype, db.func.count(Asset.assetid) ).outerjoin( Asset, db.and_(Asset.assettypeid == AssetType.assettypeid, Asset.isactive == True) ).group_by(AssetType.assettypeid).all() return success_response({ 'bytype': [ { 'type': t, 'category': _TYPE_CATEGORY.get(t, t), 'count': count } for t, count in type_counts ] }) @dashboard_bp.route('/navigation', methods=['GET']) def get_navigation(): """Get navigation items from all loaded plugins.""" pm = current_app.extensions.get('plugin_manager') if not pm: return success_response([]) all_items = [] # Core navigation items (always present) all_items.extend([ {'name': 'Dashboard', 'icon': 'layout-dashboard', 'route': '/', 'position': 0}, {'name': 'Map', 'icon': 'map', 'route': '/map', 'position': 4}, ]) # Collect navigation items from all plugins for name, plugin in pm.get_all_plugins().items(): try: items = plugin.get_navigation_items() for item in items: item['plugin'] = name all_items.extend(items) except Exception: pass # Add core information section items all_items.extend([ {'name': 'Applications', 'icon': 'app-window', 'route': '/applications', 'position': 30, 'section': 'information'}, {'name': 'Knowledge Base', 'icon': 'book-open', 'route': '/knowledgebase', 'position': 35, 'section': 'information'}, {'name': 'Reports', 'icon': 'bar-chart-3', 'route': '/reports', 'position': 40, 'section': 'information'}, ]) # Sort by position all_items.sort(key=lambda x: x.get('position', 99)) return success_response(all_items) @dashboard_bp.route('/health', methods=['GET']) def health_check(): """Health check endpoint (no auth required).""" try: db.session.execute(db.text('SELECT 1')) db_status = 'healthy' except Exception as e: db_status = f'unhealthy: {str(e)}' return success_response({ 'status': 'ok' if db_status == 'healthy' else 'degraded', 'database': db_status, 'version': '1.0.0' })