// ignore_for_file: deprecated_member_use import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:tasq/models/it_service_request.dart'; import 'package:tasq/models/it_service_request_assignment.dart'; import 'package:tasq/models/profile.dart'; import 'package:tasq/models/ticket.dart'; import 'package:tasq/models/task.dart'; import 'package:tasq/models/task_assignment.dart'; import 'package:tasq/models/ticket_message.dart'; import 'package:tasq/models/duty_schedule.dart'; import 'package:tasq/models/attendance_log.dart'; import 'package:tasq/models/live_position.dart'; import 'package:tasq/models/leave_of_absence.dart'; import 'package:tasq/models/pass_slip.dart'; import 'package:tasq/models/team.dart'; import 'package:tasq/models/team_member.dart'; import 'package:tasq/screens/dashboard/dashboard_screen.dart'; import 'package:tasq/utils/app_time.dart'; // underlying providers needed by the dashboard metrics logic import 'package:tasq/providers/it_service_request_provider.dart'; import 'package:tasq/providers/profile_provider.dart'; import 'package:tasq/providers/tickets_provider.dart'; import 'package:tasq/providers/tasks_provider.dart'; import 'package:tasq/providers/workforce_provider.dart'; // dutySchedulesProvider import 'package:tasq/providers/attendance_provider.dart'; import 'package:tasq/providers/whereabouts_provider.dart'; import 'package:tasq/providers/leave_provider.dart'; import 'package:tasq/providers/pass_slip_provider.dart'; import 'package:tasq/providers/teams_provider.dart'; void main() { group('dashboardMetricsProvider', () { test('service request in progress causes status to become On task', () async { AppTime.initialize(); final now = AppTime.now(); // create a single IT staff profile final profile = Profile( id: 'u1', fullName: 'Staff One', role: 'it_staff', avatarUrl: null, allowTracking: false, ); final service = ItServiceRequest( id: 'r1', services: ['wifi'], eventName: 'Event', status: ItServiceRequestStatus.inProgress, outsidePremiseAllowed: false, createdAt: now, updatedAt: now, ); final assignment = ItServiceRequestAssignment( id: 'a1', requestId: 'r1', userId: 'u1', createdAt: now, ); final container = ProviderContainer( overrides: [ profilesProvider.overrideWith((ref) => Stream.value([profile])), itServiceRequestsProvider.overrideWith( (ref) => Stream.value([service]), ), itServiceRequestAssignmentsProvider.overrideWith( (ref) => Stream.value([assignment]), ), // everything else can be empty streams so dashboard metrics doesn't crash ticketsProvider.overrideWith((ref) => Stream.value(const [])), tasksProvider.overrideWith((ref) => Stream.value(const [])), taskAssignmentsProvider.overrideWith( (ref) => Stream.value(const []), ), ticketMessagesAllProvider.overrideWith( (ref) => Stream.value(const []), ), dutySchedulesProvider.overrideWith( (ref) => Stream.value(const []), ), attendanceLogsProvider.overrideWith( (ref) => Stream.value(const []), ), livePositionsProvider.overrideWith( (ref) => Stream.value(const []), ), leavesProvider.overrideWith( (ref) => Stream.value(const []), ), passSlipsProvider.overrideWithProvider( StreamProvider>( (ref) => Stream.value(const []), ), ), teamsProvider.overrideWithProvider( StreamProvider>((ref) => Stream.value(const [])), ), teamMembersProvider.overrideWithProvider( StreamProvider>( (ref) => Stream.value(const []), ), ), ], ); // wait for the dashboard metrics provider to emit AsyncData final completer = Completer(); container.listen>(dashboardMetricsProvider, ( prev, next, ) { if (next is AsyncData) { completer.complete(next.value); } }, fireImmediately: true); final data = await completer.future; expect(data.staffRows.length, 1); expect(data.staffRows.first.status, 'On event'); }); test('service request not in progress does not affect status', () async { AppTime.initialize(); final now = AppTime.now(); final profile = Profile( id: 'u2', fullName: 'Staff Two', role: 'it_staff', avatarUrl: null, allowTracking: false, ); final service = ItServiceRequest( id: 'r2', services: ['wifi'], eventName: 'Event', status: ItServiceRequestStatus.scheduled, outsidePremiseAllowed: false, createdAt: now, updatedAt: now, ); final assignment = ItServiceRequestAssignment( id: 'a2', requestId: 'r2', userId: 'u2', createdAt: now, ); final container = ProviderContainer( overrides: [ profilesProvider.overrideWith((ref) => Stream.value([profile])), itServiceRequestsProvider.overrideWith( (ref) => Stream.value([service]), ), itServiceRequestAssignmentsProvider.overrideWith( (ref) => Stream.value([assignment]), ), ticketsProvider.overrideWith((ref) => Stream.value(const [])), tasksProvider.overrideWith((ref) => Stream.value(const [])), taskAssignmentsProvider.overrideWith( (ref) => Stream.value(const []), ), ticketMessagesAllProvider.overrideWith( (ref) => Stream.value(const []), ), dutySchedulesProvider.overrideWith( (ref) => Stream.value(const []), ), attendanceLogsProvider.overrideWith( (ref) => Stream.value(const []), ), livePositionsProvider.overrideWith( (ref) => Stream.value(const []), ), leavesProvider.overrideWith( (ref) => Stream.value(const []), ), passSlipsProvider.overrideWithProvider( StreamProvider>( (ref) => Stream.value(const []), ), ), teamsProvider.overrideWithProvider( StreamProvider>((ref) => Stream.value(const [])), ), teamMembersProvider.overrideWithProvider( StreamProvider>( (ref) => Stream.value(const []), ), ), ], ); final completer = Completer(); container.listen>(dashboardMetricsProvider, ( prev, next, ) { if (next is AsyncData) { completer.complete(next.value); } }, fireImmediately: true); final data = await completer.future; expect(data.staffRows.length, 1); // since user has no attendance or task data they should default to Off duty expect(data.staffRows.first.status.toLowerCase(), contains('off')); }); }); test( 'approved leave should force On leave status and not mark Off duty', () async { AppTime.initialize(); final now = AppTime.now(); final profile = Profile( id: 'u3', fullName: 'Staff Three', role: 'it_staff', avatarUrl: null, allowTracking: false, ); // create a schedule for today final schedule = DutySchedule( id: 's1', userId: profile.id, shiftType: 'am', startTime: DateTime(now.year, now.month, now.day, 8), endTime: DateTime(now.year, now.month, now.day, 16), status: 'scheduled', createdAt: now, checkInAt: null, checkInLocation: null, relieverIds: [], ); final leave = LeaveOfAbsence( id: 'l1', userId: profile.id, leaveType: 'sick_leave', justification: 'ill', startTime: now.subtract(const Duration(hours: 1)), endTime: now.add(const Duration(hours: 4)), status: 'approved', filedBy: profile.id, createdAt: now, ); final container = ProviderContainer( overrides: [ profilesProvider.overrideWith((ref) => Stream.value([profile])), dutySchedulesProvider.overrideWith((ref) => Stream.value([schedule])), leavesProvider.overrideWith((ref) => Stream.value([leave])), // stub the rest with empties ticketsProvider.overrideWith((ref) => Stream.value(const [])), tasksProvider.overrideWith((ref) => Stream.value(const [])), taskAssignmentsProvider.overrideWith( (ref) => Stream.value(const []), ), ticketMessagesAllProvider.overrideWith( (ref) => Stream.value(const []), ), attendanceLogsProvider.overrideWith( (ref) => Stream.value(const []), ), livePositionsProvider.overrideWith( (ref) => Stream.value(const []), ), passSlipsProvider.overrideWithProvider( StreamProvider>( (ref) => Stream.value(const []), ), ), teamsProvider.overrideWithProvider( StreamProvider>((ref) => Stream.value(const [])), ), teamMembersProvider.overrideWithProvider( StreamProvider>( (ref) => Stream.value(const []), ), ), itServiceRequestsProvider.overrideWith( (ref) => Stream.value(const []), ), itServiceRequestAssignmentsProvider.overrideWith( (ref) => Stream.value(const []), ), ], ); final completer = Completer(); container.listen>(dashboardMetricsProvider, ( prev, next, ) { if (next is AsyncData) { completer.complete(next.value); } }, fireImmediately: true); final data = await completer.future; expect(data.staffRows.length, 1); expect(data.staffRows.first.status, equals('On leave')); }, ); test('rejected leave should not affect schedule status', () async { AppTime.initialize(); final now = AppTime.now(); final profile = Profile( id: 'u4', fullName: 'Staff Four', role: 'it_staff', avatarUrl: null, allowTracking: false, ); final schedule = DutySchedule( id: 's2', userId: profile.id, shiftType: 'am', startTime: DateTime(now.year, now.month, now.day, 8), endTime: DateTime(now.year, now.month, now.day, 16), status: 'scheduled', createdAt: now, checkInAt: null, checkInLocation: null, relieverIds: [], ); final leave = LeaveOfAbsence( id: 'l2', userId: profile.id, leaveType: 'vacation_leave', justification: 'plans', startTime: now.subtract(const Duration(hours: 1)), endTime: now.add(const Duration(hours: 4)), status: 'rejected', filedBy: profile.id, createdAt: now, ); final container = ProviderContainer( overrides: [ profilesProvider.overrideWith((ref) => Stream.value([profile])), dutySchedulesProvider.overrideWith((ref) => Stream.value([schedule])), leavesProvider.overrideWith((ref) => Stream.value([leave])), ticketsProvider.overrideWith((ref) => Stream.value(const [])), tasksProvider.overrideWith((ref) => Stream.value(const [])), taskAssignmentsProvider.overrideWith( (ref) => Stream.value(const []), ), ticketMessagesAllProvider.overrideWith( (ref) => Stream.value(const []), ), attendanceLogsProvider.overrideWith( (ref) => Stream.value(const []), ), livePositionsProvider.overrideWith( (ref) => Stream.value(const []), ), passSlipsProvider.overrideWithProvider( StreamProvider>( (ref) => Stream.value(const []), ), ), teamsProvider.overrideWithProvider( StreamProvider>((ref) => Stream.value(const [])), ), teamMembersProvider.overrideWithProvider( StreamProvider>( (ref) => Stream.value(const []), ), ), itServiceRequestsProvider.overrideWith( (ref) => Stream.value(const []), ), itServiceRequestAssignmentsProvider.overrideWith( (ref) => Stream.value(const []), ), ], ); final completer = Completer(); container.listen>(dashboardMetricsProvider, ( prev, next, ) { if (next is AsyncData) { completer.complete(next.value); } }, fireImmediately: true); final data = await completer.future; expect(data.staffRows.length, 1); // since leave was rejected, status should not be 'On leave' nor 'Off duty' expect(data.staffRows.first.status, isNot('On leave')); expect(data.staffRows.first.status.toLowerCase(), isNot(contains('off'))); }); }