import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:superport/data/models/dashboard/equipment_status_distribution.dart'; import 'package:superport/data/models/dashboard/expiring_license.dart'; import 'package:superport/data/models/dashboard/overview_stats.dart'; import 'package:superport/data/models/dashboard/recent_activity.dart'; import 'package:superport/services/dashboard_service.dart'; import 'package:superport/screens/common/theme_tailwind.dart'; import 'package:superport/core/utils/debug_logger.dart'; // 대시보드(Overview) 화면의 상태 및 비즈니스 로직을 담답하는 컨트롤러 class OverviewController extends ChangeNotifier { final DashboardService _dashboardService = GetIt.instance(); // 상태 데이터 OverviewStats? _overviewStats; List _recentActivities = []; EquipmentStatusDistribution? _equipmentStatus; List _expiringLicenses = []; // 로딩 상태 bool _isLoadingStats = false; bool _isLoadingActivities = false; bool _isLoadingEquipmentStatus = false; bool _isLoadingLicenses = false; // 에러 상태 String? _statsError; String? _activitiesError; String? _equipmentStatusError; String? _licensesError; // Getters OverviewStats? get overviewStats => _overviewStats; List get recentActivities => _recentActivities; EquipmentStatusDistribution? get equipmentStatus => _equipmentStatus; List get expiringLicenses => _expiringLicenses; // 추가 getter int get totalCompanies => _overviewStats?.totalCompanies ?? 0; int get totalUsers => _overviewStats?.totalUsers ?? 0; bool get isLoading => _isLoadingStats || _isLoadingActivities || _isLoadingEquipmentStatus || _isLoadingLicenses; String? get error { return _statsError ?? _activitiesError ?? _equipmentStatusError ?? _licensesError; } OverviewController(); // 데이터 로드 Future loadData() async { try { await Future.wait([ _loadOverviewStats(), _loadRecentActivities(), _loadEquipmentStatus(), _loadExpiringLicenses(), ], eagerError: false); // 하나의 작업이 실패해도 다른 작업 계속 진행 } catch (e) { DebugLogger.logError('대시보드 데이터 로드 중 오류', error: e); // 개별 에러는 각 메서드에서 처리하므로 여기서는 로그만 남김 } } // 대시보드 데이터 로드 (loadData의 alias) Future loadDashboardData() async { await loadData(); } // 개별 데이터 로드 메서드 Future _loadOverviewStats() async { _isLoadingStats = true; _statsError = null; notifyListeners(); try { final result = await _dashboardService.getOverviewStats(); result.fold( (failure) { _statsError = failure.message; DebugLogger.logError('Overview 통계 로드 실패', error: failure.message); // 실패 시 기본값 설정 _overviewStats = OverviewStats( totalCompanies: 0, activeCompanies: 0, totalUsers: 0, activeUsers: 0, totalEquipment: 0, availableEquipment: 0, inUseEquipment: 0, maintenanceEquipment: 0, totalLicenses: 0, activeLicenses: 0, expiringLicensesCount: 0, expiredLicensesCount: 0, totalWarehouseLocations: 0, activeWarehouseLocations: 0, ); }, (stats) { _overviewStats = stats; }, ); } catch (e) { _statsError = '통계 데이터를 불러올 수 없습니다'; _overviewStats = OverviewStats( totalCompanies: 0, activeCompanies: 0, totalUsers: 0, activeUsers: 0, totalEquipment: 0, availableEquipment: 0, inUseEquipment: 0, maintenanceEquipment: 0, totalLicenses: 0, activeLicenses: 0, expiringLicensesCount: 0, expiredLicensesCount: 0, totalWarehouseLocations: 0, activeWarehouseLocations: 0, ); DebugLogger.logError('Overview 통계 로드 예외', error: e); } _isLoadingStats = false; notifyListeners(); } Future _loadRecentActivities() async { _isLoadingActivities = true; _activitiesError = null; notifyListeners(); try { final result = await _dashboardService.getRecentActivities(); result.fold( (failure) { _activitiesError = failure.message; _recentActivities = []; // 실패 시 빈 리스트 DebugLogger.logError('최근 활동 로드 실패', error: failure.message); }, (activities) { _recentActivities = activities ?? []; }, ); } catch (e) { _activitiesError = '최근 활동을 불러올 수 없습니다'; _recentActivities = []; DebugLogger.logError('최근 활동 로드 예외', error: e); } _isLoadingActivities = false; notifyListeners(); } Future _loadEquipmentStatus() async { _isLoadingEquipmentStatus = true; _equipmentStatusError = null; notifyListeners(); DebugLogger.log('장비 상태 분포 로드 시작', tag: 'DASHBOARD'); try { final result = await _dashboardService.getEquipmentStatusDistribution(); result.fold( (failure) { _equipmentStatusError = failure.message; DebugLogger.logError('장비 상태 분포 로드 실패', error: failure.message); // 실패 시 기본값 설정 _equipmentStatus = EquipmentStatusDistribution( available: 0, inUse: 0, maintenance: 0, disposed: 0, ); }, (status) { _equipmentStatus = status; DebugLogger.log('장비 상태 분포 로드 성공', tag: 'DASHBOARD', data: { 'available': status.available, 'inUse': status.inUse, 'maintenance': status.maintenance, 'disposed': status.disposed, }); }, ); } catch (e) { _equipmentStatusError = '장비 상태를 불러올 수 없습니다'; _equipmentStatus = EquipmentStatusDistribution( available: 0, inUse: 0, maintenance: 0, disposed: 0, ); DebugLogger.logError('장비 상태 로드 예외', error: e); } _isLoadingEquipmentStatus = false; notifyListeners(); } Future _loadExpiringLicenses() async { _isLoadingLicenses = true; _licensesError = null; notifyListeners(); try { final result = await _dashboardService.getExpiringLicenses(days: 30); result.fold( (failure) { _licensesError = failure.message; _expiringLicenses = []; // 실패 시 빈 리스트 DebugLogger.logError('만료 라이선스 로드 실패', error: failure.message); }, (licenses) { _expiringLicenses = licenses ?? []; }, ); } catch (e) { _licensesError = '라이선스 정보를 불러올 수 없습니다'; _expiringLicenses = []; DebugLogger.logError('만료 라이선스 로드 예외', error: e); } _isLoadingLicenses = false; notifyListeners(); } // 활동 타입별 아이콘과 색상 가져오기 IconData getActivityIcon(String activityType) { switch (activityType.toLowerCase()) { case 'equipment_in': case '장비 입고': return Icons.input; case 'equipment_out': case '장비 출고': return Icons.output; case 'user_create': case '사용자 추가': return Icons.person_add; case 'license_create': case '라이선스 등록': return Icons.vpn_key; default: return Icons.notifications; } } Color getActivityColor(String activityType) { switch (activityType.toLowerCase()) { case 'equipment_in': case '장비 입고': return AppThemeTailwind.success; case 'equipment_out': case '장비 출고': return AppThemeTailwind.warning; case 'user_create': case '사용자 추가': return AppThemeTailwind.primary; case 'license_create': case '라이선스 등록': return AppThemeTailwind.info; default: return AppThemeTailwind.muted; } } }