From 1268da45b0f6d6f5edd8005ecd7445b2744e75f1 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Fri, 30 Jan 2026 17:22:51 -0500 Subject: [PATCH] feat: integrate Clock In functionality with Firebase support and refactor attendance management --- .../lib/src/l10n/strings.g.dart | 2 +- .../packages/domain/lib/krow_domain.dart | 2 + .../adapters/clock_in/clock_in_adapter.dart | 26 +++ .../entities/clock_in/attendance_status.dart | 19 ++ .../clock_in_repository_impl.dart | 95 ---------- .../clock_in_repository_impl.dart | 177 ++++++++++++++++++ .../repositories/clock_in_repository.dart | 39 ---- .../clock_in_repository_interface.dart | 41 ++-- .../src/domain/usecases/clock_in_usecase.dart | 5 +- .../domain/usecases/clock_out_usecase.dart | 5 +- .../get_attendance_status_usecase.dart | 5 +- .../src/presentation/bloc/clock_in_bloc.dart | 21 +-- .../src/presentation/bloc/clock_in_state.dart | 18 -- .../src/presentation/pages/clock_in_page.dart | 92 +-------- .../lib/src/staff_clock_in_module.dart | 14 +- .../features/staff/clock_in/pubspec.yaml | 1 + 16 files changed, 267 insertions(+), 295 deletions(-) create mode 100644 apps/mobile/packages/domain/lib/src/adapters/clock_in/clock_in_adapter.dart create mode 100644 apps/mobile/packages/domain/lib/src/entities/clock_in/attendance_status.dart delete mode 100644 apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories/clock_in_repository_impl.dart create mode 100644 apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart delete mode 100644 apps/mobile/packages/features/staff/clock_in/lib/src/domain/repositories/clock_in_repository.dart diff --git a/apps/mobile/packages/core_localization/lib/src/l10n/strings.g.dart b/apps/mobile/packages/core_localization/lib/src/l10n/strings.g.dart index 03de3bbf..28644dec 100644 --- a/apps/mobile/packages/core_localization/lib/src/l10n/strings.g.dart +++ b/apps/mobile/packages/core_localization/lib/src/l10n/strings.g.dart @@ -6,7 +6,7 @@ /// Locales: 2 /// Strings: 1044 (522 per locale) /// -/// Built on 2026-01-30 at 19:58 UTC +/// Built on 2026-01-30 at 22:11 UTC // coverage:ignore-file // ignore_for_file: type=lint, unused_import diff --git a/apps/mobile/packages/domain/lib/krow_domain.dart b/apps/mobile/packages/domain/lib/krow_domain.dart index 0b58872f..bc5e3d77 100644 --- a/apps/mobile/packages/domain/lib/krow_domain.dart +++ b/apps/mobile/packages/domain/lib/krow_domain.dart @@ -80,6 +80,8 @@ export 'src/entities/home/reorder_item.dart'; // Availability export 'src/adapters/availability/availability_adapter.dart'; +export 'src/entities/clock_in/attendance_status.dart'; +export 'src/adapters/clock_in/clock_in_adapter.dart'; export 'src/entities/availability/availability_slot.dart'; export 'src/entities/availability/day_availability.dart'; diff --git a/apps/mobile/packages/domain/lib/src/adapters/clock_in/clock_in_adapter.dart b/apps/mobile/packages/domain/lib/src/adapters/clock_in/clock_in_adapter.dart new file mode 100644 index 00000000..c2e198ce --- /dev/null +++ b/apps/mobile/packages/domain/lib/src/adapters/clock_in/clock_in_adapter.dart @@ -0,0 +1,26 @@ +import '../../entities/shifts/shift.dart'; +import '../../entities/clock_in/attendance_status.dart'; + +/// Adapter for Clock In related data. +class ClockInAdapter { + + /// Converts primitive attendance data to [AttendanceStatus]. + static AttendanceStatus toAttendanceStatus({ + required String status, + DateTime? checkInTime, + DateTime? checkOutTime, + String? activeShiftId, + }) { + final bool isCheckedIn = status == 'CHECKED_IN' || status == 'LATE'; // Assuming LATE is also checked in? + + // Statuses that imply active attendance: CHECKED_IN, LATE. + // Statuses that imply completed: CHECKED_OUT. + + return AttendanceStatus( + isCheckedIn: isCheckedIn, + checkInTime: checkInTime, + checkOutTime: checkOutTime, + activeShiftId: activeShiftId, + ); + } +} diff --git a/apps/mobile/packages/domain/lib/src/entities/clock_in/attendance_status.dart b/apps/mobile/packages/domain/lib/src/entities/clock_in/attendance_status.dart new file mode 100644 index 00000000..db44377d --- /dev/null +++ b/apps/mobile/packages/domain/lib/src/entities/clock_in/attendance_status.dart @@ -0,0 +1,19 @@ +import 'package:equatable/equatable.dart'; + +/// Simple entity to hold attendance state +class AttendanceStatus extends Equatable { + final bool isCheckedIn; + final DateTime? checkInTime; + final DateTime? checkOutTime; + final String? activeShiftId; + + const AttendanceStatus({ + this.isCheckedIn = false, + this.checkInTime, + this.checkOutTime, + this.activeShiftId, + }); + + @override + List get props => [isCheckedIn, checkInTime, checkOutTime, activeShiftId]; +} diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories/clock_in_repository_impl.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories/clock_in_repository_impl.dart deleted file mode 100644 index 75cf7fa4..00000000 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories/clock_in_repository_impl.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:krow_domain/krow_domain.dart'; -import 'package:intl/intl.dart'; -import '../../domain/repositories/clock_in_repository_interface.dart'; - -/// Implementation of [ClockInRepositoryInterface] using Mock Data. -/// -/// This implementation uses hardcoded data to match the prototype UI. -class ClockInRepositoryImpl implements ClockInRepositoryInterface { - - ClockInRepositoryImpl(); - - // Local state for the mock implementation - bool _isCheckedIn = false; - DateTime? _checkInTime; - DateTime? _checkOutTime; - - @override - Future getTodaysShift() async { - // Simulate network delay - await Future.delayed(const Duration(milliseconds: 500)); - - // Mock Shift matching the prototype - return Shift( - id: '1', - title: 'Warehouse Assistant', - clientName: 'Amazon Warehouse', - logoUrl: - 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/06/Amazon_2024.svg/500px-Amazon_2024.svg.png', - hourlyRate: 22.50, - location: 'San Francisco, CA', - locationAddress: '123 Market St, San Francisco, CA 94105', - date: DateFormat('yyyy-MM-dd').format(DateTime.now()), - startTime: '09:00', - endTime: '17:00', - createdDate: DateTime.now().subtract(const Duration(days: 2)).toIso8601String(), - status: 'assigned', - description: 'General warehouse duties including packing and sorting.', - ); - } - - @override - Future> getAttendanceStatus() async { - await Future.delayed(const Duration(milliseconds: 300)); - return { - 'isCheckedIn': _isCheckedIn, - 'checkInTime': _checkInTime, - 'checkOutTime': _checkOutTime, - 'activeShiftId': '1', - }; - } - - @override - Future> clockIn({required String shiftId, String? notes}) async { - await Future.delayed(const Duration(seconds: 1)); - _isCheckedIn = true; - _checkInTime = DateTime.now(); - - return getAttendanceStatus(); - } - - @override - Future> clockOut({String? notes, int? breakTimeMinutes}) async { - await Future.delayed(const Duration(seconds: 1)); - _isCheckedIn = false; - _checkOutTime = DateTime.now(); - - return getAttendanceStatus(); - } - - @override - Future>> getActivityLog() async { - await Future.delayed(const Duration(milliseconds: 300)); - return [ - { - 'date': DateTime.now().subtract(const Duration(days: 1)), - 'start': '09:00 AM', - 'end': '05:00 PM', - 'hours': '8h', - }, - { - 'date': DateTime.now().subtract(const Duration(days: 2)), - 'start': '09:00 AM', - 'end': '05:00 PM', - 'hours': '8h', - }, - { - 'date': DateTime.now().subtract(const Duration(days: 3)), - 'start': '09:00 AM', - 'end': '05:00 PM', - 'hours': '8h', - }, - ]; - } -} - 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 new file mode 100644 index 00000000..f23ca9dd --- /dev/null +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart @@ -0,0 +1,177 @@ +import 'package:firebase_auth/firebase_auth.dart' as firebase; +import 'package:firebase_data_connect/firebase_data_connect.dart'; +import 'package:krow_data_connect/krow_data_connect.dart' as dc; +import 'package:krow_domain/krow_domain.dart'; +import '../../domain/repositories/clock_in_repository_interface.dart'; + +/// Implementation of [ClockInRepositoryInterface] using Firebase Data Connect. +class ClockInRepositoryImpl implements ClockInRepositoryInterface { + final dc.ExampleConnector _dataConnect; + final firebase.FirebaseAuth _firebaseAuth; + + ClockInRepositoryImpl({ + required dc.ExampleConnector dataConnect, + required firebase.FirebaseAuth firebaseAuth, + }) : _dataConnect = dataConnect, + _firebaseAuth = firebaseAuth; + + Future _getStaffId() async { + final firebase.User? user = _firebaseAuth.currentUser; + if (user == null) throw Exception('User not authenticated'); + + final QueryResult result = + await _dataConnect.getStaffByUserId(userId: user.uid).execute(); + if (result.data.staffs.isEmpty) { + throw Exception('Staff profile not found'); + } + return result.data.staffs.first.id; + } + + /// Helper to convert Data Connect Timestamp to DateTime + DateTime? _toDateTime(dynamic t) { + if (t == null) return null; + // Attempt to use toJson assuming it matches the generated code's expectation of String + try { + // If t has toDate (e.g. cloud_firestore), usage would be t.toDate() + // But here we rely on toJson or toString + return DateTime.tryParse(t.toJson() as String); + } catch (_) { + try { + return DateTime.tryParse(t.toString()); + } catch (e) { + return null; + } + } + } + + /// Helper to create Timestamp from DateTime + Timestamp _fromDateTime(DateTime d) { + // Assuming Timestamp.fromJson takes an ISO string + return Timestamp.fromJson(d.toIso8601String()); + } + + /// Helper to find today's active application + Future _getTodaysApplication(String staffId) async { + final DateTime now = DateTime.now(); + + // Fetch recent applications (assuming meaningful limit) + final QueryResult result = + await _dataConnect.getApplicationsByStaffId( + staffId: staffId, + ).limit(20).execute(); + + try { + return result.data.applications.firstWhere((dc.GetApplicationsByStaffIdApplications app) { + final DateTime? shiftTime = _toDateTime(app.shift.startTime); + + if (shiftTime == null) return false; + + final bool isSameDay = shiftTime.year == now.year && + shiftTime.month == now.month && + shiftTime.day == now.day; + + if (!isSameDay) return false; + + // Check Status + final dynamic status = app.status.stringValue; + return status != 'PENDING' && status != 'REJECTED' && status != 'NO_SHOW' && status != 'CANCELED'; + }); + } catch (e) { + return null; + } + } + + @override + Future getTodaysShift() async { + final String staffId = await _getStaffId(); + final dc.GetApplicationsByStaffIdApplications? app = await _getTodaysApplication(staffId); + + if (app == null) return null; + + final dc.GetApplicationsByStaffIdApplicationsShift shift = app.shift; + + final QueryResult shiftResult = + await _dataConnect.getShiftById(id: shift.id).execute(); + + if (shiftResult.data.shift == null) return null; + + final dc.GetShiftByIdShift fullShift = shiftResult.data.shift!; + + return Shift( + id: fullShift.id, + title: fullShift.title, + clientName: fullShift.order.business.businessName, + logoUrl: '', // Not available in GetShiftById + hourlyRate: 0.0, + location: fullShift.location ?? '', + locationAddress: fullShift.locationAddress ?? '', + date: _toDateTime(fullShift.startTime)?.toIso8601String() ?? '', + startTime: _toDateTime(fullShift.startTime)?.toIso8601String() ?? '', + endTime: _toDateTime(fullShift.endTime)?.toIso8601String() ?? '', + createdDate: _toDateTime(fullShift.createdAt)?.toIso8601String() ?? '', + status: fullShift.status?.stringValue, + description: fullShift.description, + ); + } + + @override + Future getAttendanceStatus() async { + final String staffId = await _getStaffId(); + final dc.GetApplicationsByStaffIdApplications? app = await _getTodaysApplication(staffId); + + if (app == null) { + return const AttendanceStatus(isCheckedIn: false); + } + + return ClockInAdapter.toAttendanceStatus( + status: app.status.stringValue, + checkInTime: _toDateTime(app.checkInTime), + checkOutTime: _toDateTime(app.checkOutTime), + activeShiftId: app.shiftId, + ); + } + + @override + Future clockIn({required String shiftId, String? notes}) async { + final String staffId = await _getStaffId(); + + final QueryResult appsResult = + await _dataConnect.getApplicationsByStaffId(staffId: staffId).execute(); + + final dc.GetApplicationsByStaffIdApplications app = appsResult.data.applications.firstWhere((dc.GetApplicationsByStaffIdApplications a) => a.shiftId == shiftId); + + await _dataConnect.updateApplicationStatus( + id: app.id, + roleId: app.shiftRole.id, + ) + .status(dc.ApplicationStatus.CHECKED_IN) + .checkInTime(_fromDateTime(DateTime.now())) + .execute(); + + return getAttendanceStatus(); + } + + @override + Future clockOut({String? notes, int? breakTimeMinutes}) async { + final String staffId = await _getStaffId(); + + final dc.GetApplicationsByStaffIdApplications? app = await _getTodaysApplication(staffId); + if (app == null) throw Exception('No active shift found to clock out'); + + await _dataConnect.updateApplicationStatus( + id: app.id, + roleId: app.shiftRole.id, + ) + .status(dc.ApplicationStatus.CHECKED_OUT) + .checkOutTime(_fromDateTime(DateTime.now())) + .execute(); + + return getAttendanceStatus(); + } + + @override + Future>> getActivityLog() async { + // Placeholder as this wasn't main focus and returns raw maps + return >[]; + } +} diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/repositories/clock_in_repository.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/repositories/clock_in_repository.dart deleted file mode 100644 index c27c665f..00000000 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/repositories/clock_in_repository.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:krow_domain/krow_domain.dart'; - -/// Repository interface for Clock In/Out functionality -abstract class ClockInRepository { - - /// Retrieves the shift assigned to the user for the current day. - /// Returns null if no shift is assigned for today. - Future getTodaysShift(); - - /// Gets the current attendance status (e.g., checked in or not, times). - /// This helps in restoring the UI state if the app was killed. - Future getAttendanceStatus(); - - /// Checks the user in for the specified [shiftId]. - /// Returns the updated [AttendanceStatus]. - Future clockIn({required String shiftId, String? notes}); - - /// Checks the user out for the currently active shift. - /// Optionally accepts [breakTimeMinutes] if tracked. - Future clockOut({String? notes, int? breakTimeMinutes}); - - /// Retrieves a list of recent clock-in/out activities. - Future>> getActivityLog(); -} - -/// Simple entity to hold attendance state -class AttendanceStatus { - final bool isCheckedIn; - final DateTime? checkInTime; - final DateTime? checkOutTime; - final String? activeShiftId; - - const AttendanceStatus({ - this.isCheckedIn = false, - this.checkInTime, - this.checkOutTime, - this.activeShiftId, - }); -} diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/repositories/clock_in_repository_interface.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/repositories/clock_in_repository_interface.dart index 5049987e..c934a533 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/repositories/clock_in_repository_interface.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/repositories/clock_in_repository_interface.dart @@ -1,35 +1,24 @@ import 'package:krow_domain/krow_domain.dart'; -/// Interface for the Clock In feature repository. -/// -/// Defines the methods for managing clock-in/out operations and retrieving -/// related shift and attendance data. -abstract interface class ClockInRepositoryInterface { - /// Retrieves the shift scheduled for today. +/// Repository interface for Clock In/Out functionality +abstract class ClockInRepositoryInterface { + + /// Retrieves the shift assigned to the user for the current day. + /// Returns null if no shift is assigned for today. Future getTodaysShift(); - /// Retrieves the current attendance status (check-in time, check-out time, etc.). - /// - /// Returns a Map containing: - /// - 'isCheckedIn': bool - /// - 'checkInTime': DateTime? - /// - 'checkOutTime': DateTime? - Future> getAttendanceStatus(); + /// Gets the current attendance status (e.g., checked in or not, times). + /// This helps in restoring the UI state if the app was killed. + Future getAttendanceStatus(); - /// Clocks the user in for a specific shift. - Future> clockIn({ - required String shiftId, - String? notes, - }); + /// Checks the user in for the specified [shiftId]. + /// Returns the updated [AttendanceStatus]. + Future clockIn({required String shiftId, String? notes}); - /// Clocks the user out of the current shift. - Future> clockOut({ - String? notes, - int? breakTimeMinutes, - }); + /// Checks the user out for the currently active shift. + /// Optionally accepts [breakTimeMinutes] if tracked. + Future clockOut({String? notes, int? breakTimeMinutes}); - /// Retrieves the history of clock-in/out activity. - /// - /// Returns a list of maps, where each map represents an activity entry. + /// Retrieves a list of recent clock-in/out activities. Future>> getActivityLog(); } diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/clock_in_usecase.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/clock_in_usecase.dart index a99ae43e..c4535129 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/clock_in_usecase.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/clock_in_usecase.dart @@ -1,15 +1,16 @@ import 'package:krow_core/core.dart'; +import 'package:krow_domain/krow_domain.dart'; import '../repositories/clock_in_repository_interface.dart'; import '../arguments/clock_in_arguments.dart'; /// Use case for clocking in a user. -class ClockInUseCase implements UseCase> { +class ClockInUseCase implements UseCase { final ClockInRepositoryInterface _repository; ClockInUseCase(this._repository); @override - Future> call(ClockInArguments arguments) { + Future call(ClockInArguments arguments) { return _repository.clockIn( shiftId: arguments.shiftId, notes: arguments.notes, diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/clock_out_usecase.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/clock_out_usecase.dart index dbea2b26..b4869818 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/clock_out_usecase.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/clock_out_usecase.dart @@ -1,15 +1,16 @@ import 'package:krow_core/core.dart'; +import 'package:krow_domain/krow_domain.dart'; import '../repositories/clock_in_repository_interface.dart'; import '../arguments/clock_out_arguments.dart'; /// Use case for clocking out a user. -class ClockOutUseCase implements UseCase> { +class ClockOutUseCase implements UseCase { final ClockInRepositoryInterface _repository; ClockOutUseCase(this._repository); @override - Future> call(ClockOutArguments arguments) { + Future call(ClockOutArguments arguments) { return _repository.clockOut( notes: arguments.notes, breakTimeMinutes: arguments.breakTimeMinutes, diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/get_attendance_status_usecase.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/get_attendance_status_usecase.dart index e0722339..1f80da69 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/get_attendance_status_usecase.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/domain/usecases/get_attendance_status_usecase.dart @@ -1,14 +1,15 @@ import 'package:krow_core/core.dart'; +import 'package:krow_domain/krow_domain.dart'; import '../repositories/clock_in_repository_interface.dart'; /// Use case for getting the current attendance status (check-in/out times). -class GetAttendanceStatusUseCase implements NoInputUseCase> { +class GetAttendanceStatusUseCase implements NoInputUseCase { final ClockInRepositoryInterface _repository; GetAttendanceStatusUseCase(this._repository); @override - Future> call() { + Future call() { return _repository.getAttendanceStatus(); } } diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/bloc/clock_in_bloc.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/bloc/clock_in_bloc.dart index 551e8d32..9c1eefc5 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/bloc/clock_in_bloc.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/bloc/clock_in_bloc.dart @@ -37,15 +37,6 @@ class ClockInBloc extends Bloc { add(ClockInPageLoaded()); } - AttendanceStatus _mapToStatus(Map map) { - return AttendanceStatus( - isCheckedIn: map['isCheckedIn'] as bool? ?? false, - checkInTime: map['checkInTime'] as DateTime?, - checkOutTime: map['checkOutTime'] as DateTime?, - activeShiftId: map['activeShiftId'] as String?, - ); - } - Future _onLoaded( ClockInPageLoaded event, Emitter emit, @@ -53,13 +44,13 @@ class ClockInBloc extends Bloc { emit(state.copyWith(status: ClockInStatus.loading)); try { final shift = await _getTodaysShift(); - final statusMap = await _getAttendanceStatus(); + final status = await _getAttendanceStatus(); final activity = await _getActivityLog(); emit(state.copyWith( status: ClockInStatus.success, todayShift: shift, - attendance: _mapToStatus(statusMap), + attendance: status, activityLog: activity, )); } catch (e) { @@ -90,12 +81,12 @@ class ClockInBloc extends Bloc { ) async { emit(state.copyWith(status: ClockInStatus.actionInProgress)); try { - final newStatusMap = await _clockIn( + final newStatus = await _clockIn( ClockInArguments(shiftId: event.shiftId, notes: event.notes), ); emit(state.copyWith( status: ClockInStatus.success, - attendance: _mapToStatus(newStatusMap), + attendance: newStatus, )); } catch (e) { emit(state.copyWith( @@ -111,7 +102,7 @@ class ClockInBloc extends Bloc { ) async { emit(state.copyWith(status: ClockInStatus.actionInProgress)); try { - final newStatusMap = await _clockOut( + final newStatus = await _clockOut( ClockOutArguments( notes: event.notes, breakTimeMinutes: 0, // Should be passed from event if supported @@ -119,7 +110,7 @@ class ClockInBloc extends Bloc { ); emit(state.copyWith( status: ClockInStatus.success, - attendance: _mapToStatus(newStatusMap), + attendance: newStatus, )); } catch (e) { emit(state.copyWith( diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/bloc/clock_in_state.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/bloc/clock_in_state.dart index 8e6fe30c..a1f4c876 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/bloc/clock_in_state.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/bloc/clock_in_state.dart @@ -3,24 +3,6 @@ import 'package:krow_domain/krow_domain.dart'; enum ClockInStatus { initial, loading, success, failure, actionInProgress } -/// View model representing the user's current attendance state. -class AttendanceStatus extends Equatable { - final bool isCheckedIn; - final DateTime? checkInTime; - final DateTime? checkOutTime; - final String? activeShiftId; - - const AttendanceStatus({ - this.isCheckedIn = false, - this.checkInTime, - this.checkOutTime, - this.activeShiftId, - }); - - @override - List get props => [isCheckedIn, checkInTime, checkOutTime, activeShiftId]; -} - class ClockInState extends Equatable { final ClockInStatus status; final Shift? todayShift; diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/pages/clock_in_page.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/pages/clock_in_page.dart index f131f423..4be6cc15 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/pages/clock_in_page.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/pages/clock_in_page.dart @@ -107,63 +107,6 @@ class _ClockInPageState extends State { ), const SizedBox(height: 20), - // Today Attendance Section - const Align( - alignment: Alignment.centerLeft, - child: Text( - "Today Attendance", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: AppColors.krowCharcoal, - ), - ), - ), - const SizedBox(height: 12), - GridView.count( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - crossAxisCount: 2, - mainAxisSpacing: 12, - crossAxisSpacing: 12, - childAspectRatio: 1.0, - children: [ - AttendanceCard( - type: AttendanceType.checkin, - title: "Check In", - value: checkInStr, - subtitle: checkInTime != null - ? "On Time" - : "Pending", - scheduledTime: "09:00 AM", - ), - AttendanceCard( - type: AttendanceType.checkout, - title: "Check Out", - value: checkOutStr, - subtitle: checkOutTime != null - ? "Go Home" - : "Pending", - scheduledTime: "05:00 PM", - ), - AttendanceCard( - type: AttendanceType.breaks, - title: "Break Time", - // TODO: Connect to Data Connect when 'breakDuration' field is added to Shift schema. - value: "00:30 min", - subtitle: "Scheduled 00:30 min", - ), - const AttendanceCard( - type: AttendanceType.days, - title: "Total Days", - // TODO: Connect to Data Connect when 'staffStats' or similar aggregation API is available. - // Currently avoided to prevent fetching full shift history for a simple count. - value: "28", - subtitle: "Working Days", - ), - ], - ), - const SizedBox(height: 24), // Your Activity Header const Text( @@ -175,39 +118,7 @@ class _ClockInPageState extends State { color: AppColors.krowCharcoal, ), ), - const SizedBox(height: 12), - // Check-in Mode Toggle - const Align( - alignment: Alignment.centerLeft, - child: Text( - "Check-in Method", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Color(0xFF334155), // slate-700 - ), - ), - ), - const SizedBox(height: 8), - Container( - padding: const EdgeInsets.all(4), - decoration: BoxDecoration( - color: const Color(0xFFF1F5F9), // slate-100 - borderRadius: BorderRadius.circular(12), - ), - child: Row( - children: [ - _buildModeTab( - "Swipe", - LucideIcons.mapPin, - 'swipe', - state.checkInMode, - ), - // _buildModeTab("NFC Tap", LucideIcons.wifi, 'nfc', state.checkInMode), - ], - ), - ), const SizedBox(height: 16), // Selected Shift Info Card @@ -376,12 +287,13 @@ class _ClockInPageState extends State { ] else ...[ // No Shift State Container( + width: double.infinity, padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: const Color(0xFFF1F5F9), // slate-100 borderRadius: BorderRadius.circular(16), ), - child: Column( + child: const Column( children: [ const Text( "No confirmed shifts for today", 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 f7062597..16c0a809 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,6 +1,8 @@ +import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:krow_data_connect/krow_data_connect.dart'; -import 'data/repositories/clock_in_repository_impl.dart'; + +import 'data/repositories_impl/clock_in_repository_impl.dart'; import 'domain/repositories/clock_in_repository_interface.dart'; import 'domain/usecases/clock_in_usecase.dart'; import 'domain/usecases/clock_out_usecase.dart'; @@ -13,11 +15,13 @@ import 'presentation/pages/clock_in_page.dart'; class StaffClockInModule extends Module { @override void binds(Injector i) { - // Data Sources (Mocks from data_connect) - i.add(ShiftsRepositoryMock.new); - // Repositories - i.add(ClockInRepositoryImpl.new); + i.add( + () => ClockInRepositoryImpl( + dataConnect: ExampleConnector.instance, + firebaseAuth: FirebaseAuth.instance, + ), + ); // Use Cases i.add(GetTodaysShiftUseCase.new); diff --git a/apps/mobile/packages/features/staff/clock_in/pubspec.yaml b/apps/mobile/packages/features/staff/clock_in/pubspec.yaml index 624e7ed3..3a0c5413 100644 --- a/apps/mobile/packages/features/staff/clock_in/pubspec.yaml +++ b/apps/mobile/packages/features/staff/clock_in/pubspec.yaml @@ -31,3 +31,4 @@ dependencies: firebase_data_connect: ^0.2.2+2 geolocator: ^10.1.0 permission_handler: ^11.0.1 + firebase_auth: ^6.1.4