import 'package:pocketbase/pocketbase.dart'; import '../../../core/api/pocketbase_client.dart'; import '../../../models/job.dart'; class ConnectionStats { const ConnectionStats({ required this.totalJobs, required this.byStatus, required this.byType, required this.totalRevenue, required this.pendingRevenue, required this.thisMonthJobs, required this.lastMonthJobs, required this.revisionCount, required this.recentJobs, }); final int totalJobs; final Map byStatus; // 'in_progress', 'sent', 'delivered', 'cancelled' final Map byType; // prosthetic_type -> count final double totalRevenue; final double pendingRevenue; final int thisMonthJobs; final int lastMonthJobs; final int revisionCount; final List recentJobs; int get deliveredJobs => byStatus['delivered'] ?? 0; int get activeJobs => (byStatus['in_progress'] ?? 0) + (byStatus['sent'] ?? 0); int get cancelledJobs => byStatus['cancelled'] ?? 0; double get revisionRate => totalJobs > 0 ? revisionCount / totalJobs * 100 : 0; double get completionRate => totalJobs > 0 ? deliveredJobs / totalJobs * 100 : 0; } class ConnectionStatsRepository { ConnectionStatsRepository._(); static final instance = ConnectionStatsRepository._(); PocketBase get _pb => PocketBaseClient.instance.pb; Future fetchStats({ required String labTenantId, required String clinicTenantId, }) async { final now = DateTime.now(); final thisMonthStart = DateTime(now.year, now.month, 1); final lastMonthStart = DateTime(now.year, now.month - 1, 1); final lastMonthEnd = thisMonthStart.subtract(const Duration(seconds: 1)); final filter = 'lab_tenant_id = "$labTenantId" && clinic_tenant_id = "$clinicTenantId"'; final results = await Future.wait([ _pb.collection('jobs').getList( filter: filter, perPage: 500, expand: 'clinic_tenant_id,lab_tenant_id', ), _pb.collection('finance_entries').getList( filter: 'tenant_id = "$labTenantId" && job_id.clinic_tenant_id = "$clinicTenantId"', perPage: 500, ), ]); final jobsResult = results[0]; final financeResult = results[1]; final allJobs = jobsResult.items .map((r) => Job.fromJson(r.toJson())) .toList(); // Status breakdown final byStatus = {}; final byType = {}; int thisMonthJobs = 0; int lastMonthJobs = 0; int revisionCount = 0; for (final job in allJobs) { // Status final s = job.status.value; byStatus[s] = (byStatus[s] ?? 0) + 1; // Type final t = job.prostheticType.value; byType[t] = (byType[t] ?? 0) + 1; // Monthly final created = job.dateCreated; if (created.isAfter(thisMonthStart)) thisMonthJobs++; else if (created.isAfter(lastMonthStart) && created.isBefore(lastMonthEnd)) lastMonthJobs++; // Revision if (job.status == JobStatus.inProgress && job.currentStep == null) revisionCount++; } // Finance double totalRevenue = 0; double pendingRevenue = 0; for (final r in financeResult.items) { final j = r.toJson(); final amount = (j['amount'] as num?)?.toDouble() ?? 0; totalRevenue += amount; if (j['status'] == 'pending') pendingRevenue += amount; } // Recent jobs (last 5 delivered or sent) final recent = allJobs .where((j) => j.status == JobStatus.delivered || j.status == JobStatus.sent) .toList() ..sort((a, b) => b.dateCreated.compareTo(a.dateCreated)); return ConnectionStats( totalJobs: allJobs.length, byStatus: byStatus, byType: byType, totalRevenue: totalRevenue, pendingRevenue: pendingRevenue, thisMonthJobs: thisMonthJobs, lastMonthJobs: lastMonthJobs, revisionCount: revisionCount, recentJobs: recent.take(5).toList(), ); } }