From 66859e4241ca804a5a2e766389c34d5baee94a7a Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Mon, 16 Feb 2026 16:00:27 -0500 Subject: [PATCH] feat(clock-in): Refactor ClockInRepositoryImpl to utilize DataConnectService and simplify dependency injection --- .../clock_in_repository_impl.dart | 308 ++++++++---------- .../lib/src/staff_clock_in_module.dart | 5 +- 2 files changed, 135 insertions(+), 178 deletions(-) diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart index 6caf9a50..ea0e990f 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart @@ -1,69 +1,17 @@ import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc; import 'package:krow_data_connect/krow_data_connect.dart' as dc; import 'package:krow_domain/krow_domain.dart'; -import 'package:krow_core/core.dart'; import '../../domain/repositories/clock_in_repository_interface.dart'; /// Implementation of [ClockInRepositoryInterface] using Firebase Data Connect. -class ClockInRepositoryImpl - with dc.DataErrorHandler - implements ClockInRepositoryInterface { +class ClockInRepositoryImpl implements ClockInRepositoryInterface { + ClockInRepositoryImpl() : _service = dc.DataConnectService.instance; - ClockInRepositoryImpl({ - required dc.ExampleConnector dataConnect, - }) : _dataConnect = dataConnect; - final dc.ExampleConnector _dataConnect; + final dc.DataConnectService _service; final Map _shiftToApplicationId = {}; String? _activeApplicationId; - Future _getStaffId() async { - final dc.StaffSession? session = dc.StaffSessionStore.instance.session; - final String? staffId = session?.staff?.id; - if (staffId != null && staffId.isNotEmpty) { - return staffId; - } - throw Exception('Staff session not found'); - } - - /// Helper to convert Data Connect fdc.Timestamp to DateTime - DateTime? _toDateTime(dynamic t) { - if (t == null) return null; - DateTime? dt; - if (t is DateTime) { - dt = t; - } else if (t is String) { - dt = DateTime.tryParse(t); - } else { - try { - if (t is fdc.Timestamp) { - dt = t.toDateTime(); - } - } catch (_) {} - - try { - if (dt == null && t.runtimeType.toString().contains('Timestamp')) { - dt = (t as dynamic).toDate(); - } - } catch (_) {} - - try { - dt ??= DateTime.tryParse(t.toString()); - } catch (_) {} - } - - if (dt != null) { - return DateTimeUtils.toDeviceTime(dt); - } - return null; - } - - /// Helper to create fdc.Timestamp from DateTime - fdc.Timestamp _fromDateTime(DateTime d) { - // Assuming fdc.Timestamp.fromJson takes an ISO string - return fdc.Timestamp.fromJson(d.toUtc().toIso8601String()); - } - ({fdc.Timestamp start, fdc.Timestamp end}) _utcDayRange(DateTime localDay) { final DateTime dayStartUtc = DateTime.utc( localDay.year, @@ -81,8 +29,8 @@ class ClockInRepositoryImpl 999, ); return ( - start: _fromDateTime(dayStartUtc), - end: _fromDateTime(dayEndUtc), + start: _service.toTimestamp(dayStartUtc), + end: _service.toTimestamp(dayEndUtc), ); } @@ -93,26 +41,29 @@ class ClockInRepositoryImpl final DateTime now = DateTime.now(); final ({fdc.Timestamp start, fdc.Timestamp end}) range = _utcDayRange(now); final fdc.QueryResult result = await executeProtected( - () => _dataConnect + dc.GetApplicationsByStaffIdVariables> result = await _service.run( + () => _service.connector .getApplicationsByStaffId(staffId: staffId) .dayStart(range.start) .dayEnd(range.end) .execute(), ); - final List apps = result.data.applications; + final List apps = + result.data.applications; if (apps.isEmpty) return const []; _shiftToApplicationId ..clear() - ..addEntries(apps.map((dc.GetApplicationsByStaffIdApplications app) => MapEntry(app.shiftId, app.id))); + ..addEntries(apps.map((dc.GetApplicationsByStaffIdApplications app) => + MapEntry(app.shiftId, app.id))); - apps.sort((dc.GetApplicationsByStaffIdApplications a, dc.GetApplicationsByStaffIdApplications b) { + apps.sort((dc.GetApplicationsByStaffIdApplications a, + dc.GetApplicationsByStaffIdApplications b) { final DateTime? aTime = - _toDateTime(a.shift.startTime) ?? _toDateTime(a.shift.date); + _service.toDateTime(a.shift.startTime) ?? _service.toDateTime(a.shift.date); final DateTime? bTime = - _toDateTime(b.shift.startTime) ?? _toDateTime(b.shift.date); + _service.toDateTime(b.shift.startTime) ?? _service.toDateTime(b.shift.date); if (aTime == null && bTime == null) return 0; if (aTime == null) return -1; if (bTime == null) return 1; @@ -124,118 +75,124 @@ class ClockInRepositoryImpl return apps; } - - @override Future> getTodaysShifts() async { - final String staffId = await _getStaffId(); - final List apps = - await _getTodaysApplications(staffId); - if (apps.isEmpty) return const []; + return _service.run(() async { + final String staffId = await _service.getStaffId(); + final List apps = + await _getTodaysApplications(staffId); + if (apps.isEmpty) return const []; - final List shifts = []; - for (final dc.GetApplicationsByStaffIdApplications app in apps) { - final dc.GetApplicationsByStaffIdApplicationsShift shift = app.shift; - final DateTime? startDt = _toDateTime(app.shiftRole.startTime); - final DateTime? endDt = _toDateTime(app.shiftRole.endTime); - final DateTime? createdDt = _toDateTime(app.createdAt); + final List shifts = []; + for (final dc.GetApplicationsByStaffIdApplications app in apps) { + final dc.GetApplicationsByStaffIdApplicationsShift shift = app.shift; + final DateTime? startDt = _service.toDateTime(app.shiftRole.startTime); + final DateTime? endDt = _service.toDateTime(app.shiftRole.endTime); + final DateTime? createdDt = _service.toDateTime(app.createdAt); - final String roleName = app.shiftRole.role.name; - final String orderName = - (shift.order.eventName ?? '').trim().isNotEmpty - ? shift.order.eventName! - : shift.order.business.businessName; - final String title = '$roleName - $orderName'; - shifts.add( - Shift( - id: shift.id, - title: title, - clientName: shift.order.business.businessName, - logoUrl: shift.order.business.companyLogoUrl ?? '', - hourlyRate: app.shiftRole.role.costPerHour, - location: shift.location ?? '', - locationAddress: shift.order.teamHub.hubName, - date: startDt?.toIso8601String() ?? '', - startTime: startDt?.toIso8601String() ?? '', - endTime: endDt?.toIso8601String() ?? '', - createdDate: createdDt?.toIso8601String() ?? '', - status: shift.status?.stringValue, - description: shift.description, - latitude: shift.latitude, - longitude: shift.longitude, - ), - ); - } + final String roleName = app.shiftRole.role.name; + final String orderName = + (shift.order.eventName ?? '').trim().isNotEmpty + ? shift.order.eventName! + : shift.order.business.businessName; + final String title = '$roleName - $orderName'; + shifts.add( + Shift( + id: shift.id, + title: title, + clientName: shift.order.business.businessName, + logoUrl: shift.order.business.companyLogoUrl ?? '', + hourlyRate: app.shiftRole.role.costPerHour, + location: shift.location ?? '', + locationAddress: shift.order.teamHub.hubName, + date: startDt?.toIso8601String() ?? '', + startTime: startDt?.toIso8601String() ?? '', + endTime: endDt?.toIso8601String() ?? '', + createdDate: createdDt?.toIso8601String() ?? '', + status: shift.status?.stringValue, + description: shift.description, + latitude: shift.latitude, + longitude: shift.longitude, + ), + ); + } - return shifts; + return shifts; + }); } @override Future getAttendanceStatus() async { - final String staffId = await _getStaffId(); - final List apps = - await _getTodaysApplications(staffId); - if (apps.isEmpty) { - return const AttendanceStatus(isCheckedIn: false); - } + return _service.run(() async { + final String staffId = await _service.getStaffId(); + final List apps = + await _getTodaysApplications(staffId); + if (apps.isEmpty) { + return const AttendanceStatus(isCheckedIn: false); + } - dc.GetApplicationsByStaffIdApplications? activeApp; - for (final dc.GetApplicationsByStaffIdApplications app in apps) { - if (app.checkInTime != null && app.checkOutTime == null) { - if (activeApp == null) { - activeApp = app; - } else { - final DateTime? current = _toDateTime(activeApp.checkInTime); - final DateTime? next = _toDateTime(app.checkInTime); - if (current == null || (next != null && next.isAfter(current))) { + dc.GetApplicationsByStaffIdApplications? activeApp; + for (final dc.GetApplicationsByStaffIdApplications app in apps) { + if (app.checkInTime != null && app.checkOutTime == null) { + if (activeApp == null) { activeApp = app; + } else { + final DateTime? current = _service.toDateTime(activeApp.checkInTime); + final DateTime? next = _service.toDateTime(app.checkInTime); + if (current == null || (next != null && next.isAfter(current))) { + activeApp = app; + } } } } - } - if (activeApp == null) { - _activeApplicationId = null; - return const AttendanceStatus(isCheckedIn: false); - } + if (activeApp == null) { + _activeApplicationId = null; + return const AttendanceStatus(isCheckedIn: false); + } - _activeApplicationId = activeApp.id; + _activeApplicationId = activeApp.id; - return AttendanceStatus( - isCheckedIn: true, - checkInTime: _toDateTime(activeApp.checkInTime), - checkOutTime: _toDateTime(activeApp.checkOutTime), - activeShiftId: activeApp.shiftId, - activeApplicationId: activeApp.id, - ); + return AttendanceStatus( + isCheckedIn: true, + checkInTime: _service.toDateTime(activeApp.checkInTime), + checkOutTime: _service.toDateTime(activeApp.checkOutTime), + activeShiftId: activeApp.shiftId, + activeApplicationId: activeApp.id, + ); + }); } @override Future clockIn({required String shiftId, String? notes}) async { - final String staffId = await _getStaffId(); + return _service.run(() async { + final String staffId = await _service.getStaffId(); - final String? cachedAppId = _shiftToApplicationId[shiftId]; - dc.GetApplicationsByStaffIdApplications? app; - if (cachedAppId != null) { - try { - final List apps = await _getTodaysApplications(staffId); - app = apps.firstWhere((dc.GetApplicationsByStaffIdApplications a) => a.id == cachedAppId); - } catch (_) {} - } - app ??= (await _getTodaysApplications(staffId)) - .firstWhere((dc.GetApplicationsByStaffIdApplications a) => a.shiftId == shiftId); + final String? cachedAppId = _shiftToApplicationId[shiftId]; + dc.GetApplicationsByStaffIdApplications? app; + if (cachedAppId != null) { + try { + final List apps = + await _getTodaysApplications(staffId); + app = apps.firstWhere( + (dc.GetApplicationsByStaffIdApplications a) => a.id == cachedAppId); + } catch (_) {} + } + app ??= (await _getTodaysApplications(staffId)).firstWhere( + (dc.GetApplicationsByStaffIdApplications a) => a.shiftId == shiftId); - final fdc.Timestamp checkInTs = _fromDateTime(DateTime.now()); + final fdc.Timestamp checkInTs = _service.toTimestamp(DateTime.now()); - await executeProtected(() => _dataConnect - .updateApplicationStatus( - id: app!.id, - ) - .checkInTime(checkInTs) - .execute()); - _activeApplicationId = app.id; + await _service.run(() => _service.connector + .updateApplicationStatus( + id: app!.id, + ) + .checkInTime(checkInTs) + .execute()); + _activeApplicationId = app.id; - return getAttendanceStatus(); + return getAttendanceStatus(); + }); } @override @@ -244,32 +201,35 @@ class ClockInRepositoryImpl int? breakTimeMinutes, String? applicationId, }) async { - await _getStaffId(); // Validate session - + return _service.run(() async { + await _service.getStaffId(); // Validate session - final String? targetAppId = applicationId ?? _activeApplicationId; - if (targetAppId == null || targetAppId.isEmpty) { - throw Exception('No active application id for checkout'); - } - final fdc.QueryResult appResult = await executeProtected(() => _dataConnect - .getApplicationById(id: targetAppId) - .execute()); - final dc.GetApplicationByIdApplication? app = appResult.data.application; + final String? targetAppId = applicationId ?? _activeApplicationId; + if (targetAppId == null || targetAppId.isEmpty) { + throw Exception('No active application id for checkout'); + } + final fdc.QueryResult appResult = + await _service.run(() => _service.connector + .getApplicationById(id: targetAppId) + .execute()); + final dc.GetApplicationByIdApplication? app = appResult.data.application; - if (app == null) { - throw Exception('Application not found for checkout'); - } - if (app.checkInTime == null || app.checkOutTime != null) { - throw Exception('No active shift found to clock out'); - } + if (app == null) { + throw Exception('Application not found for checkout'); + } + if (app.checkInTime == null || app.checkOutTime != null) { + throw Exception('No active shift found to clock out'); + } - await executeProtected(() => _dataConnect - .updateApplicationStatus( - id: targetAppId, - ) - .checkOutTime(_fromDateTime(DateTime.now())) - .execute()); + await _service.run(() => _service.connector + .updateApplicationStatus( + id: targetAppId, + ) + .checkOutTime(_service.toTimestamp(DateTime.now())) + .execute()); - return getAttendanceStatus(); + return getAttendanceStatus(); + }); } } diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/staff_clock_in_module.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/staff_clock_in_module.dart index 37164a81..ffd19c01 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/staff_clock_in_module.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/staff_clock_in_module.dart @@ -1,7 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:krow_core/core.dart'; -import 'package:krow_data_connect/krow_data_connect.dart'; import 'data/repositories_impl/clock_in_repository_impl.dart'; import 'domain/repositories/clock_in_repository_interface.dart'; @@ -16,9 +15,7 @@ class StaffClockInModule extends Module { @override void binds(Injector i) { // Repositories - i.add( - () => ClockInRepositoryImpl(dataConnect: ExampleConnector.instance), - ); + i.add(ClockInRepositoryImpl.new); // Use Cases i.add(GetTodaysShiftUseCase.new);