diff --git a/apps/mobile/apps/client/android/app/build.gradle.kts b/apps/mobile/apps/client/android/app/build.gradle.kts index f169e26c..323e6fd0 100644 --- a/apps/mobile/apps/client/android/app/build.gradle.kts +++ b/apps/mobile/apps/client/android/app/build.gradle.kts @@ -43,7 +43,6 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId = "com.krowwithus.client" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. @@ -51,18 +50,17 @@ android { targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName - - manifestPlaceholders["GOOGLE_MAPS_API_KEY"] = dartEnvironmentVariables["GOOGLE_MAPS_API_KEY"] ?: "" + manifestPlaceholders["GOOGLE_MAPS_API_KEY"] = dartEnvironmentVariables["GOOGLE_MAPS_API_KEY"] ?: "" } signingConfigs { create("release") { if (System.getenv()["CI"] == "true") { // CodeMagic CI environment - storeFile = file(System.getenv()["CM_KEYSTORE_PATH_CLIENT"] ?: "") - storePassword = System.getenv()["CM_KEYSTORE_PASSWORD_CLIENT"] - keyAlias = System.getenv()["CM_KEY_ALIAS_CLIENT"] - keyPassword = System.getenv()["CM_KEY_PASSWORD_CLIENT"] + storeFile = file(System.getenv()["CM_KEYSTORE_PATH"] ?: "") + storePassword = System.getenv()["CM_KEYSTORE_PASSWORD"] + keyAlias = System.getenv()["CM_KEY_ALIAS"] + keyPassword = System.getenv()["CM_KEY_PASSWORD"] } else { // Local development environment keyAlias = keystoreProperties["keyAlias"] as String? diff --git a/apps/mobile/apps/client/lib/src/widgets/session_listener.dart b/apps/mobile/apps/client/lib/src/widgets/session_listener.dart index cbae1627..707d5cf7 100644 --- a/apps/mobile/apps/client/lib/src/widgets/session_listener.dart +++ b/apps/mobile/apps/client/lib/src/widgets/session_listener.dart @@ -104,7 +104,7 @@ class _SessionListenerState extends State { actions: [ TextButton( onPressed: () { - Navigator.of(context).pop(); + Modular.to.popSafe(); _proceedToLogin(); }, child: const Text('Log In'), @@ -134,7 +134,7 @@ class _SessionListenerState extends State { ), TextButton( onPressed: () { - Navigator.of(context).pop(); + Modular.to.popSafe();; _proceedToLogin(); }, child: const Text('Log Out'), diff --git a/apps/mobile/apps/staff/android/app/build.gradle.kts b/apps/mobile/apps/staff/android/app/build.gradle.kts index 9e3968be..0f7dd24a 100644 --- a/apps/mobile/apps/staff/android/app/build.gradle.kts +++ b/apps/mobile/apps/staff/android/app/build.gradle.kts @@ -43,7 +43,6 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId = "com.krowwithus.staff" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. @@ -59,10 +58,10 @@ android { create("release") { if (System.getenv()["CI"] == "true") { // CodeMagic CI environment - storeFile = file(System.getenv()["CM_KEYSTORE_PATH_STAFF"] ?: "") - storePassword = System.getenv()["CM_KEYSTORE_PASSWORD_STAFF"] - keyAlias = System.getenv()["CM_KEY_ALIAS_STAFF"] - keyPassword = System.getenv()["CM_KEY_PASSWORD_STAFF"] + storeFile = file(System.getenv()["CM_KEYSTORE_PATH"] ?: "") + storePassword = System.getenv()["CM_KEYSTORE_PASSWORD"] + keyAlias = System.getenv()["CM_KEY_ALIAS"] + keyPassword = System.getenv()["CM_KEY_PASSWORD"] } else { // Local development environment keyAlias = keystoreProperties["keyAlias"] as String? diff --git a/apps/mobile/apps/staff/android/app/google-services.json_back b/apps/mobile/apps/staff/android/app/google-services.json_back deleted file mode 100644 index f4d57e10..00000000 --- a/apps/mobile/apps/staff/android/app/google-services.json_back +++ /dev/null @@ -1,162 +0,0 @@ -{ - "project_info": { - "project_number": "933560802882", - "project_id": "krow-workforce-dev", - "storage_bucket": "krow-workforce-dev.firebasestorage.app" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:933560802882:android:edcddb83ea4bbb517757db", - "android_client_info": { - "package_name": "com.krow.app.business.dev" - } - }, - "oauth_client": [ - { - "client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyDBYhflhK6DThKnS7RM-9raKdvyKzLUjY4" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com", - "client_type": 3 - }, - { - "client_id": "933560802882-dppsapp5i3lsfrlm1mhob2s21peofg1t.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "com.krow.app.staff.dev" - } - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "1:933560802882:android:d49b8c0f4d19e95e7757db", - "android_client_info": { - "package_name": "com.krow.app.staff.dev" - } - }, - "oauth_client": [ - { - "client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyDBYhflhK6DThKnS7RM-9raKdvyKzLUjY4" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com", - "client_type": 3 - }, - { - "client_id": "933560802882-dppsapp5i3lsfrlm1mhob2s21peofg1t.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "com.krow.app.staff.dev" - } - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "1:933560802882:android:da13569105659ead7757db", - "android_client_info": { - "package_name": "com.krowwithus.client" - } - }, - "oauth_client": [ - { - "client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyDBYhflhK6DThKnS7RM-9raKdvyKzLUjY4" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com", - "client_type": 3 - }, - { - "client_id": "933560802882-dppsapp5i3lsfrlm1mhob2s21peofg1t.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "com.krow.app.staff.dev" - } - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "1:933560802882:android:1ae05d85c865f77c7757db", - "android_client_info": { - "package_name": "com.krowwithus.staff" - } - }, - "oauth_client": [ - { - "client_id": "933560802882-ikdfv3o5f47g36qqgvfq55o4m19n7gk4.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "com.krowwithus.staff", - "certificate_hash": "ac917ae8470ab29f1107c773c6017ff5ea5d102d" - } - }, - { - "client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyDBYhflhK6DThKnS7RM-9raKdvyKzLUjY4" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com", - "client_type": 3 - }, - { - "client_id": "933560802882-dppsapp5i3lsfrlm1mhob2s21peofg1t.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "com.krow.app.staff.dev" - } - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/apps/mobile/apps/staff/lib/src/widgets/session_listener.dart b/apps/mobile/apps/staff/lib/src/widgets/session_listener.dart index fa830a35..47d9fdd0 100644 --- a/apps/mobile/apps/staff/lib/src/widgets/session_listener.dart +++ b/apps/mobile/apps/staff/lib/src/widgets/session_listener.dart @@ -104,7 +104,7 @@ class _SessionListenerState extends State { actions: [ TextButton( onPressed: () { - Navigator.of(context).pop(); + Modular.to.popSafe();; _proceedToLogin(); }, child: const Text('Log In'), @@ -134,7 +134,7 @@ class _SessionListenerState extends State { ), TextButton( onPressed: () { - Navigator.of(context).pop(); + Modular.to.popSafe();; _proceedToLogin(); }, child: const Text('Log Out'), diff --git a/apps/mobile/packages/core_localization/lib/src/data/repositories_impl/locale_repository_impl.dart b/apps/mobile/packages/core_localization/lib/src/data/repositories_impl/locale_repository_impl.dart index 861f579f..be8f1e24 100644 --- a/apps/mobile/packages/core_localization/lib/src/data/repositories_impl/locale_repository_impl.dart +++ b/apps/mobile/packages/core_localization/lib/src/data/repositories_impl/locale_repository_impl.dart @@ -1,4 +1,5 @@ import 'dart:ui'; + import 'package:core_localization/src/l10n/strings.g.dart'; import '../../domain/repositories/locale_repository_interface.dart'; @@ -31,12 +32,10 @@ class LocaleRepositoryImpl implements LocaleRepositoryInterface { return getDefaultLocale(); } + /// We can hardcode this to english based on customer requirements, + /// but in a more dynamic app this should be the device locale or a fallback to english. @override Locale getDefaultLocale() { - final Locale deviceLocale = AppLocaleUtils.findDeviceLocale().flutterLocale; - if (getSupportedLocales().contains(deviceLocale)) { - return deviceLocale; - } return const Locale('en'); } diff --git a/apps/mobile/packages/data_connect/lib/src/connectors/staff/data/repositories/staff_connector_repository_impl.dart b/apps/mobile/packages/data_connect/lib/src/connectors/staff/data/repositories/staff_connector_repository_impl.dart index e5f0f4d5..770f1d68 100644 --- a/apps/mobile/packages/data_connect/lib/src/connectors/staff/data/repositories/staff_connector_repository_impl.dart +++ b/apps/mobile/packages/data_connect/lib/src/connectors/staff/data/repositories/staff_connector_repository_impl.dart @@ -20,7 +20,6 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { @override Future getProfileCompletion() async { - return true; return _service.run(() async { final String staffId = await _service.getStaffId(); diff --git a/apps/mobile/packages/domain/lib/src/entities/financial/staff_payment.dart b/apps/mobile/packages/domain/lib/src/entities/financial/staff_payment.dart index d6126de8..75cd8d8e 100644 --- a/apps/mobile/packages/domain/lib/src/entities/financial/staff_payment.dart +++ b/apps/mobile/packages/domain/lib/src/entities/financial/staff_payment.dart @@ -28,6 +28,12 @@ class StaffPayment extends Equatable { required this.amount, required this.status, this.paidAt, + this.shiftTitle, + this.shiftLocation, + this.locationAddress, + this.hoursWorked, + this.hourlyRate, + this.workedTime, }); /// Unique identifier. final String id; @@ -47,6 +53,24 @@ class StaffPayment extends Equatable { /// When the payment was successfully processed. final DateTime? paidAt; + /// Title of the shift worked. + final String? shiftTitle; + + /// Location/hub name of the shift. + final String? shiftLocation; + + /// Address of the shift location. + final String? locationAddress; + + /// Number of hours worked. + final double? hoursWorked; + + /// Hourly rate for the shift. + final double? hourlyRate; + + /// Work session duration or status. + final String? workedTime; + @override - List get props => [id, staffId, assignmentId, amount, status, paidAt]; + List get props => [id, staffId, assignmentId, amount, status, paidAt, shiftTitle, shiftLocation, locationAddress, hoursWorked, hourlyRate, workedTime]; } \ No newline at end of file diff --git a/apps/mobile/packages/features/client/client_coverage/lib/src/presentation/pages/coverage_page.dart b/apps/mobile/packages/features/client/client_coverage/lib/src/presentation/pages/coverage_page.dart index 697fc13d..7d3bf602 100644 --- a/apps/mobile/packages/features/client/client_coverage/lib/src/presentation/pages/coverage_page.dart +++ b/apps/mobile/packages/features/client/client_coverage/lib/src/presentation/pages/coverage_page.dart @@ -1,14 +1,13 @@ +import 'package:core_localization/core_localization.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:intl/intl.dart'; -import 'package:krow_core/core.dart'; -import 'package:core_localization/core_localization.dart'; + import '../blocs/coverage_bloc.dart'; import '../blocs/coverage_event.dart'; import '../blocs/coverage_state.dart'; - import '../widgets/coverage_calendar_selector.dart'; import '../widgets/coverage_quick_stats.dart'; import '../widgets/coverage_shift_list.dart'; @@ -78,21 +77,6 @@ class _CoveragePageState extends State { pinned: true, expandedHeight: 300.0, backgroundColor: UiColors.primary, - leading: IconButton( - onPressed: () => Modular.to.toClientHome(), - icon: Container( - padding: const EdgeInsets.all(UiConstants.space2), - decoration: BoxDecoration( - color: UiColors.primaryForeground.withOpacity(0.2), - shape: BoxShape.circle, - ), - child: const Icon( - UiIcons.arrowLeft, - color: UiColors.primaryForeground, - size: UiConstants.space4, - ), - ), - ), title: AnimatedSwitcher( duration: const Duration(milliseconds: 200), child: Text( diff --git a/apps/mobile/packages/features/client/client_coverage/lib/src/presentation/widgets/coverage_shift_list.dart b/apps/mobile/packages/features/client/client_coverage/lib/src/presentation/widgets/coverage_shift_list.dart index e675719b..c1bedeed 100644 --- a/apps/mobile/packages/features/client/client_coverage/lib/src/presentation/widgets/coverage_shift_list.dart +++ b/apps/mobile/packages/features/client/client_coverage/lib/src/presentation/widgets/coverage_shift_list.dart @@ -506,21 +506,21 @@ class _WorkerRow extends StatelessWidget { ), ), ), - if (worker.status == CoverageWorkerStatus.checkedIn) - UiButton.primary( - text: context.t.client_coverage.worker_row.verify, - size: UiButtonSize.small, - onPressed: () { - UiSnackbar.show( - context, - message: - context.t.client_coverage.worker_row.verified_message( - name: worker.name, - ), - type: UiSnackbarType.success, - ); - }, - ), + // if (worker.status == CoverageWorkerStatus.checkedIn) + // UiButton.primary( + // text: context.t.client_coverage.worker_row.verify, + // size: UiButtonSize.small, + // onPressed: () { + // UiSnackbar.show( + // context, + // message: + // context.t.client_coverage.worker_row.verified_message( + // name: worker.name, + // ), + // type: UiSnackbarType.success, + // ); + // }, + // ), ], ), ], diff --git a/apps/mobile/packages/features/client/orders/analyze.txt b/apps/mobile/packages/features/client/orders/analyze.txt deleted file mode 100644 index 28d6d1d5..00000000 Binary files a/apps/mobile/packages/features/client/orders/analyze.txt and /dev/null differ diff --git a/apps/mobile/packages/features/client/orders/analyze_output.txt b/apps/mobile/packages/features/client/orders/analyze_output.txt deleted file mode 100644 index 4c48dc48..00000000 Binary files a/apps/mobile/packages/features/client/orders/analyze_output.txt and /dev/null differ diff --git a/apps/mobile/packages/features/client/orders/view_orders/lib/src/presentation/widgets/view_order_card.dart b/apps/mobile/packages/features/client/orders/view_orders/lib/src/presentation/widgets/view_order_card.dart index 689ec491..ca085684 100644 --- a/apps/mobile/packages/features/client/orders/view_orders/lib/src/presentation/widgets/view_order_card.dart +++ b/apps/mobile/packages/features/client/orders/view_orders/lib/src/presentation/widgets/view_order_card.dart @@ -161,14 +161,7 @@ class _ViewOrderCardState extends State { decoration: BoxDecoration( color: UiColors.white, borderRadius: UiConstants.radiusLg, - border: Border.all(color: UiColors.border), - boxShadow: [ - BoxShadow( - color: UiColors.black.withValues(alpha: 0.04), - blurRadius: 8, - offset: const Offset(0, 2), - ), - ], + border: Border.all(color: UiColors.border, width: 0.5), ), child: Column( children: [ @@ -249,9 +242,12 @@ class _ViewOrderCardState extends State { size: 14, color: UiColors.iconSecondary, ), - Text( - order.eventName, - style: UiTypography.headline5m.textSecondary, + Expanded( + child: Text( + order.eventName, + style: UiTypography.headline5m.textSecondary, + overflow: TextOverflow.ellipsis, + ), ), ], ), @@ -313,7 +309,8 @@ class _ViewOrderCardState extends State { Expanded( child: Text( order.hubManagerName!, - style: UiTypography.footnote2r.textSecondary, + style: + UiTypography.footnote2r.textSecondary, maxLines: 1, overflow: TextOverflow.ellipsis, ), @@ -335,7 +332,8 @@ class _ViewOrderCardState extends State { bgColor: UiColors.primary.withValues(alpha: 0.08), onTap: () => _openEditSheet(order: order), ), - if (_canEditOrder(order)) const SizedBox(width: UiConstants.space2), + if (_canEditOrder(order)) + const SizedBox(width: UiConstants.space2), if (order.confirmedApps.isNotEmpty) _buildHeaderIconButton( icon: _expanded diff --git a/apps/mobile/packages/features/client/reports/lib/src/presentation/blocs/daily_ops/daily_ops_bloc.dart b/apps/mobile/packages/features/client/reports/lib/src/presentation/blocs/daily_ops/daily_ops_bloc.dart index 943553bb..06f54dcb 100644 --- a/apps/mobile/packages/features/client/reports/lib/src/presentation/blocs/daily_ops/daily_ops_bloc.dart +++ b/apps/mobile/packages/features/client/reports/lib/src/presentation/blocs/daily_ops/daily_ops_bloc.dart @@ -1,5 +1,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:krow_domain/src/entities/reports/daily_ops_report.dart'; +import 'package:krow_domain/krow_domain.dart'; + import '../../../domain/repositories/reports_repository.dart'; import 'daily_ops_event.dart'; import 'daily_ops_state.dart'; diff --git a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/coverage_report_page.dart b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/coverage_report_page.dart index ca7c9f5e..54ba368b 100644 --- a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/coverage_report_page.dart +++ b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/coverage_report_page.dart @@ -1,14 +1,14 @@ -// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs -import 'package:client_reports/src/presentation/blocs/coverage/coverage_bloc.dart'; +import 'package:client_reports/src/presentation/blocs/coverage/coverage_bloc.dart'; import 'package:client_reports/src/presentation/blocs/coverage/coverage_event.dart'; import 'package:client_reports/src/presentation/blocs/coverage/coverage_state.dart'; -import 'package:krow_domain/krow_domain.dart'; import 'package:core_localization/core_localization.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:intl/intl.dart'; +import 'package:krow_core/core.dart'; +import 'package:krow_domain/krow_domain.dart'; class CoverageReportPage extends StatefulWidget { const CoverageReportPage({super.key}); @@ -23,11 +23,10 @@ class _CoverageReportPageState extends State { @override Widget build(BuildContext context) { - return BlocProvider( + return BlocProvider( create: (BuildContext context) => Modular.get() ..add(LoadCoverageReport(startDate: _startDate, endDate: _endDate)), child: Scaffold( - backgroundColor: UiColors.bgMenu, body: BlocBuilder( builder: (BuildContext context, CoverageState state) { if (state is CoverageLoading) { @@ -64,7 +63,7 @@ class _CoverageReportPageState extends State { Row( children: [ GestureDetector( - onTap: () => Navigator.of(context).pop(), + onTap: () => Modular.to.popSafe(), child: Container( width: 40, height: 40, diff --git a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/daily_ops_report_page.dart b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/daily_ops_report_page.dart index 07ede38c..15e4765f 100644 --- a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/daily_ops_report_page.dart +++ b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/daily_ops_report_page.dart @@ -1,5 +1,4 @@ -// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs, implementation_imports -import 'package:client_reports/src/presentation/blocs/daily_ops/daily_ops_bloc.dart'; +import 'package:client_reports/src/presentation/blocs/daily_ops/daily_ops_bloc.dart'; import 'package:client_reports/src/presentation/blocs/daily_ops/daily_ops_event.dart'; import 'package:client_reports/src/presentation/blocs/daily_ops/daily_ops_state.dart'; import 'package:core_localization/core_localization.dart'; @@ -8,7 +7,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:intl/intl.dart'; -import 'package:krow_domain/src/entities/reports/daily_ops_report.dart'; +import 'package:krow_core/core.dart'; +import 'package:krow_domain/krow_domain.dart'; class DailyOpsReportPage extends StatefulWidget { const DailyOpsReportPage({super.key}); @@ -50,11 +50,10 @@ class _DailyOpsReportPageState extends State { @override Widget build(BuildContext context) { - return BlocProvider( + return BlocProvider( create: (BuildContext context) => Modular.get() ..add(LoadDailyOpsReport(date: _selectedDate)), child: Scaffold( - backgroundColor: UiColors.bgMenu, body: BlocBuilder( builder: (BuildContext context, DailyOpsState state) { if (state is DailyOpsLoading) { @@ -94,7 +93,7 @@ class _DailyOpsReportPageState extends State { Row( children: [ GestureDetector( - onTap: () => Navigator.of(context).pop(), + onTap: () => Modular.to.popSafe(), child: Container( width: 40, height: 40, @@ -245,6 +244,7 @@ class _DailyOpsReportPageState extends State { crossAxisCount: 2, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: 1.2, @@ -318,7 +318,7 @@ class _DailyOpsReportPageState extends State { ], ), - const SizedBox(height: 8), + const SizedBox(height: UiConstants.space8), Text( context.t.client_reports.daily_ops_report .all_shifts_title @@ -398,14 +398,8 @@ class _OpsStatCard extends StatelessWidget { padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: UiColors.white, - borderRadius: BorderRadius.circular(12), - boxShadow: [ - BoxShadow( - color: UiColors.black.withOpacity(0.06), - blurRadius: 4, - offset: const Offset(0, 2), - ), - ], + borderRadius: BorderRadius.circular(UiConstants.radiusBase), + border: Border.all(color: UiColors.border, width: 0.5), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -440,7 +434,8 @@ class _OpsStatCard extends StatelessWidget { color: UiColors.textPrimary, ), ), - const SizedBox(height: 6), + + //UiChip(label: subValue), // Colored pill badge (matches prototype) Container( padding: const EdgeInsets.symmetric( @@ -449,12 +444,12 @@ class _OpsStatCard extends StatelessWidget { ), decoration: BoxDecoration( color: color.withOpacity(0.12), - borderRadius: BorderRadius.circular(20), + borderRadius: BorderRadius.circular(4), ), child: Text( subValue, style: TextStyle( - fontSize: 10, + fontSize: 12, fontWeight: FontWeight.bold, color: color, ), diff --git a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/forecast_report_page.dart b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/forecast_report_page.dart index 553ca240..a0479a67 100644 --- a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/forecast_report_page.dart +++ b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/forecast_report_page.dart @@ -2,6 +2,7 @@ import 'package:client_reports/src/presentation/blocs/forecast/forecast_bloc.dart'; import 'package:client_reports/src/presentation/blocs/forecast/forecast_event.dart'; import 'package:client_reports/src/presentation/blocs/forecast/forecast_state.dart'; +import 'package:krow_core/core.dart'; import 'package:krow_domain/krow_domain.dart'; import 'package:core_localization/core_localization.dart'; import 'package:design_system/design_system.dart'; @@ -28,7 +29,6 @@ class _ForecastReportPageState extends State { create: (BuildContext context) => Modular.get() ..add(LoadForecastReport(startDate: _startDate, endDate: _endDate)), child: Scaffold( - backgroundColor: UiColors.bgMenu, body: BlocBuilder( builder: (BuildContext context, ForecastState state) { if (state is ForecastLoading) { @@ -86,7 +86,7 @@ class _ForecastReportPageState extends State { (ForecastWeek week) => _WeeklyBreakdownItem(week: week), ), - const SizedBox(height: 40), + const SizedBox(height: UiConstants.space24), ], ), ), @@ -124,7 +124,7 @@ class _ForecastReportPageState extends State { Row( children: [ GestureDetector( - onTap: () => Navigator.of(context).pop(), + onTap: () => Modular.to.popSafe(), child: Container( width: 40, height: 40, @@ -184,6 +184,7 @@ class _ForecastReportPageState extends State { final TranslationsClientReportsForecastReportEn t = context.t.client_reports.forecast_report; return GridView.count( crossAxisCount: 2, + padding: EdgeInsets.zero, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), mainAxisSpacing: 12, diff --git a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/no_show_report_page.dart b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/no_show_report_page.dart index 17410784..7cf962d2 100644 --- a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/no_show_report_page.dart +++ b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/no_show_report_page.dart @@ -1,4 +1,5 @@ // ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs +import 'package:krow_core/core.dart'; import 'package:krow_domain/krow_domain.dart'; import 'package:client_reports/src/presentation/blocs/no_show/no_show_bloc.dart'; import 'package:client_reports/src/presentation/blocs/no_show/no_show_event.dart'; @@ -27,7 +28,6 @@ class _NoShowReportPageState extends State { create: (BuildContext context) => Modular.get() ..add(LoadNoShowReport(startDate: _startDate, endDate: _endDate)), child: Scaffold( - backgroundColor: UiColors.bgMenu, body: BlocBuilder( builder: (BuildContext context, NoShowState state) { if (state is NoShowLoading) { @@ -67,7 +67,7 @@ class _NoShowReportPageState extends State { Row( children: [ GestureDetector( - onTap: () => Navigator.of(context).pop(), + onTap: () => Modular.to.popSafe(), child: Container( width: 40, height: 40, diff --git a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/performance_report_page.dart b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/performance_report_page.dart index 3593b5fa..ccfd5169 100644 --- a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/performance_report_page.dart +++ b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/performance_report_page.dart @@ -1,5 +1,4 @@ -// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs, implementation_imports -import 'package:client_reports/src/presentation/blocs/performance/performance_bloc.dart'; +import 'package:client_reports/src/presentation/blocs/performance/performance_bloc.dart'; import 'package:client_reports/src/presentation/blocs/performance/performance_event.dart'; import 'package:client_reports/src/presentation/blocs/performance/performance_state.dart'; import 'package:core_localization/core_localization.dart'; @@ -7,7 +6,8 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_modular/flutter_modular.dart'; -import 'package:krow_domain/src/entities/reports/performance_report.dart'; +import 'package:krow_core/core.dart'; +import 'package:krow_domain/krow_domain.dart'; class PerformanceReportPage extends StatefulWidget { const PerformanceReportPage({super.key}); @@ -26,7 +26,6 @@ class _PerformanceReportPageState extends State { create: (BuildContext context) => Modular.get() ..add(LoadPerformanceReport(startDate: _startDate, endDate: _endDate)), child: Scaffold( - backgroundColor: UiColors.bgMenu, body: BlocBuilder( builder: (BuildContext context, PerformanceState state) { if (state is PerformanceLoading) { @@ -143,7 +142,7 @@ class _PerformanceReportPageState extends State { Row( children: [ GestureDetector( - onTap: () => Navigator.of(context).pop(), + onTap: () => Modular.to.popSafe(), child: Container( width: 40, height: 40, diff --git a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/reports_page.dart b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/reports_page.dart index 10a6c620..79212649 100644 --- a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/reports_page.dart +++ b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/reports_page.dart @@ -1,7 +1,5 @@ -// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs, implementation_imports -import 'package:client_reports/src/presentation/blocs/summary/reports_summary_bloc.dart'; +import 'package:client_reports/src/presentation/blocs/summary/reports_summary_bloc.dart'; import 'package:client_reports/src/presentation/blocs/summary/reports_summary_event.dart'; -import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_modular/flutter_modular.dart'; @@ -80,10 +78,9 @@ class _ReportsPageState extends State @override Widget build(BuildContext context) { - return BlocProvider.value( + return BlocProvider.value( value: _summaryBloc, child: Scaffold( - backgroundColor: UiColors.bgMenu, body: SingleChildScrollView( child: Column( children: [ diff --git a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/spend_report_page.dart b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/spend_report_page.dart index 9b6becd6..7ba1eeb9 100644 --- a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/spend_report_page.dart +++ b/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/spend_report_page.dart @@ -1,5 +1,4 @@ -// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs -import 'package:client_reports/src/presentation/blocs/spend/spend_bloc.dart'; +import 'package:client_reports/src/presentation/blocs/spend/spend_bloc.dart'; import 'package:client_reports/src/presentation/blocs/spend/spend_event.dart'; import 'package:client_reports/src/presentation/blocs/spend/spend_state.dart'; import 'package:core_localization/core_localization.dart'; @@ -9,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:intl/intl.dart'; +import 'package:krow_core/core.dart'; import 'package:krow_domain/krow_domain.dart'; class SpendReportPage extends StatefulWidget { @@ -35,11 +35,10 @@ class _SpendReportPageState extends State { @override Widget build(BuildContext context) { - return BlocProvider( + return BlocProvider( create: (BuildContext context) => Modular.get() ..add(LoadSpendReport(startDate: _startDate, endDate: _endDate)), child: Scaffold( - backgroundColor: UiColors.bgMenu, body: BlocBuilder( builder: (BuildContext context, SpendState state) { if (state is SpendLoading) { @@ -72,7 +71,7 @@ class _SpendReportPageState extends State { Row( children: [ GestureDetector( - onTap: () => Navigator.of(context).pop(), + onTap: () => Modular.to.popSafe(), child: Container( width: 40, height: 40, diff --git a/apps/mobile/packages/features/staff/authentication/lib/src/presentation/pages/phone_verification_page.dart b/apps/mobile/packages/features/staff/authentication/lib/src/presentation/pages/phone_verification_page.dart index d70eb8ad..d4c3b652 100644 --- a/apps/mobile/packages/features/staff/authentication/lib/src/presentation/pages/phone_verification_page.dart +++ b/apps/mobile/packages/features/staff/authentication/lib/src/presentation/pages/phone_verification_page.dart @@ -155,7 +155,7 @@ class _PhoneVerificationPageState extends State { BlocProvider.of( context, ).add(AuthResetRequested(mode: widget.mode)); - Navigator.of(context).pop(); + Modular.to.popSafe();; }, ), body: SafeArea( diff --git a/apps/mobile/packages/features/staff/home/lib/src/presentation/widgets/home_page/full_width_divider.dart b/apps/mobile/packages/features/staff/home/lib/src/presentation/widgets/home_page/full_width_divider.dart index 3ffaf542..9712bfac 100644 --- a/apps/mobile/packages/features/staff/home/lib/src/presentation/widgets/home_page/full_width_divider.dart +++ b/apps/mobile/packages/features/staff/home/lib/src/presentation/widgets/home_page/full_width_divider.dart @@ -11,16 +11,16 @@ class FullWidthDivider extends StatelessWidget { @override Widget build(BuildContext context) { - final screenWidth = MediaQuery.of(context).size.width; + //final screenWidth = MediaQuery.of(context).size.width; return Column( children: [ const SizedBox(height: UiConstants.space10), - Transform.translate( - offset: const Offset(-UiConstants.space4, 0), - child: SizedBox(width: screenWidth, child: const Divider()), - ), - const SizedBox(height: UiConstants.space10), + // Transform.translate( + // offset: const Offset(-UiConstants.space4, 0), + // child: SizedBox(width: screenWidth, child: const Divider()), + // ), + // const SizedBox(height: UiConstants.space10), ], ); } diff --git a/apps/mobile/packages/features/staff/payments/lib/src/data/repositories/payments_repository_impl.dart b/apps/mobile/packages/features/staff/payments/lib/src/data/repositories/payments_repository_impl.dart index 726a84b1..3c701b36 100644 --- a/apps/mobile/packages/features/staff/payments/lib/src/data/repositories/payments_repository_impl.dart +++ b/apps/mobile/packages/features/staff/payments/lib/src/data/repositories/payments_repository_impl.dart @@ -67,6 +67,16 @@ class PaymentsRepositoryImpl .execute(); return response.data.recentPayments.map((dc.ListRecentPaymentsByStaffIdRecentPayments payment) { + // Extract shift details from nested application structure + final String? shiftTitle = payment.application.shiftRole.shift.title; + final String? locationAddress = payment.application.shiftRole.shift.locationAddress; + final double? hoursWorked = payment.application.shiftRole.hours; + final double? hourlyRate = payment.application.shiftRole.role.costPerHour; + // Extract hub details from order + final String? locationHub = payment.invoice.order.teamHub.hubName; + final String? hubAddress = payment.invoice.order.teamHub.address; + final String? shiftLocation = locationAddress ?? hubAddress; + return StaffPayment( id: payment.id, staffId: payment.staffId, @@ -74,6 +84,12 @@ class PaymentsRepositoryImpl amount: payment.invoice.amount, status: PaymentAdapter.toPaymentStatus(payment.status?.stringValue ?? 'UNKNOWN'), paidAt: _service.toDateTime(payment.invoice.issueDate), + shiftTitle: shiftTitle, + shiftLocation: locationHub, + locationAddress: shiftLocation, + hoursWorked: hoursWorked, + hourlyRate: hourlyRate, + workedTime: payment.workedTime, ); }).toList(); }); diff --git a/apps/mobile/packages/features/staff/payments/lib/src/presentation/pages/payments_page.dart b/apps/mobile/packages/features/staff/payments/lib/src/presentation/pages/payments_page.dart index b1ce9e4e..b1ff94f3 100644 --- a/apps/mobile/packages/features/staff/payments/lib/src/presentation/pages/payments_page.dart +++ b/apps/mobile/packages/features/staff/payments/lib/src/presentation/pages/payments_page.dart @@ -1,5 +1,4 @@ import 'package:design_system/design_system.dart'; -import 'package:krow_core/core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_modular/flutter_modular.dart'; @@ -10,7 +9,6 @@ import '../blocs/payments/payments_bloc.dart'; import '../blocs/payments/payments_event.dart'; import '../blocs/payments/payments_state.dart'; import '../widgets/payment_stats_card.dart'; -import '../widgets/pending_pay_card.dart'; import '../widgets/payment_history_item.dart'; import '../widgets/earnings_graph.dart'; @@ -172,17 +170,7 @@ class _PaymentsPageState extends State { ), ], ), - const SizedBox(height: UiConstants.space4), - - // Pending Pay - if (state.summary.pendingEarnings > 0) - PendingPayCard( - amount: state.summary.pendingEarnings, - onCashOut: () { - Modular.to.pushNamed('${StaffPaths.payments}early-pay'); - }, - ), - const SizedBox(height: UiConstants.space6), + const SizedBox(height: UiConstants.space8), // Recent Payments if (state.history.isNotEmpty) @@ -191,7 +179,7 @@ class _PaymentsPageState extends State { children: [ Text( "Recent Payments", - style: UiTypography.body2m.textPrimary, + style: UiTypography.body1b, ), const SizedBox(height: UiConstants.space3), Column( @@ -201,16 +189,16 @@ class _PaymentsPageState extends State { bottom: UiConstants.space2), child: PaymentHistoryItem( amount: payment.amount, - title: "Shift Payment", - location: "Varies", - address: "Payment ID: ${payment.id}", + title: payment.shiftTitle ?? "Shift Payment", + location: payment.shiftLocation ?? "Varies", + address: payment.locationAddress ?? payment.id, date: payment.paidAt != null ? DateFormat('E, MMM d') .format(payment.paidAt!) : 'Pending', - workedTime: "Completed", - hours: 0, - rate: 0.0, + workedTime: payment.workedTime ?? "Completed", + hours: (payment.hoursWorked ?? 0).toInt(), + rate: payment.hourlyRate ?? 0.0, status: payment.status.name.toUpperCase(), ), ); diff --git a/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/payment_history_item.dart b/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/payment_history_item.dart index e068caee..44fe3304 100644 --- a/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/payment_history_item.dart +++ b/apps/mobile/packages/features/staff/payments/lib/src/presentation/widgets/payment_history_item.dart @@ -32,13 +32,7 @@ class PaymentHistoryItem extends StatelessWidget { decoration: BoxDecoration( color: UiColors.white, borderRadius: BorderRadius.circular(UiConstants.radiusBase), - boxShadow: [ - BoxShadow( - color: UiColors.black.withValues(alpha: 0.05), - blurRadius: 2, - offset: const Offset(0, 1), - ), - ], + border: Border.all(color: UiColors.border, width: 0.5), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -77,7 +71,7 @@ class PaymentHistoryItem extends StatelessWidget { borderRadius: BorderRadius.circular(UiConstants.radiusBase), ), child: const Icon( - UiIcons.chart, + UiIcons.dollar, color: UiColors.mutedForeground, size: 24, ), @@ -98,7 +92,7 @@ class PaymentHistoryItem extends StatelessWidget { children: [ Text( title, - style: UiTypography.body2b.textPrimary, + style: UiTypography.body2m, ), Text( location, @@ -112,7 +106,7 @@ class PaymentHistoryItem extends StatelessWidget { children: [ Text( "\$${amount.toStringAsFixed(0)}", - style: UiTypography.headline4m.textPrimary, + style: UiTypography.headline4b, ), Text( "\$${rate.toStringAsFixed(0)}/hr · ${hours}h", diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shift_details/shift_details_bloc.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shift_details/shift_details_bloc.dart index 5d46c536..3f5357b3 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shift_details/shift_details_bloc.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shift_details/shift_details_bloc.dart @@ -1,5 +1,6 @@ import 'package:bloc/bloc.dart'; import 'package:krow_core/core.dart'; +import 'package:krow_data_connect/krow_data_connect.dart'; import '../../../domain/usecases/apply_for_shift_usecase.dart'; import '../../../domain/usecases/decline_shift_usecase.dart'; import '../../../domain/usecases/get_shift_details_usecase.dart'; @@ -12,11 +13,13 @@ class ShiftDetailsBloc extends Bloc final GetShiftDetailsUseCase getShiftDetails; final ApplyForShiftUseCase applyForShift; final DeclineShiftUseCase declineShift; + final GetProfileCompletionUseCase getProfileCompletion; ShiftDetailsBloc({ required this.getShiftDetails, required this.applyForShift, required this.declineShift, + required this.getProfileCompletion, }) : super(ShiftDetailsInitial()) { on(_onLoadDetails); on(_onBookShift); @@ -34,8 +37,9 @@ class ShiftDetailsBloc extends Bloc final shift = await getShiftDetails( GetShiftDetailsArguments(shiftId: event.shiftId, roleId: event.roleId), ); + final isProfileComplete = await getProfileCompletion(); if (shift != null) { - emit(ShiftDetailsLoaded(shift)); + emit(ShiftDetailsLoaded(shift, isProfileComplete: isProfileComplete)); } else { emit(const ShiftDetailsError("Shift not found")); } diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shift_details/shift_details_state.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shift_details/shift_details_state.dart index cf6cda49..b9a0fbeb 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shift_details/shift_details_state.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shift_details/shift_details_state.dart @@ -14,10 +14,11 @@ class ShiftDetailsLoading extends ShiftDetailsState {} class ShiftDetailsLoaded extends ShiftDetailsState { final Shift shift; - const ShiftDetailsLoaded(this.shift); + final bool isProfileComplete; + const ShiftDetailsLoaded(this.shift, {this.isProfileComplete = false}); @override - List get props => [shift]; + List get props => [shift, isProfileComplete]; } class ShiftDetailsError extends ShiftDetailsState { diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shift_details_page.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shift_details_page.dart index 05449f48..06fd236f 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shift_details_page.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shift_details_page.dart @@ -125,6 +125,9 @@ class _ShiftDetailsPageState extends State { final Shift displayShift = widget.shift; final i18n = Translations.of(context).staff_shifts.shift_details; + final isProfileComplete = state is ShiftDetailsLoaded + ? state.isProfileComplete + : false; final duration = _calculateDuration(displayShift); final estimatedTotal = @@ -142,6 +145,16 @@ class _ShiftDetailsPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + if (!isProfileComplete) + Padding( + padding: const EdgeInsets.all(UiConstants.space6), + child: UiNoticeBanner( + title: 'Complete Your Account', + description: + 'Complete your account to book this shift and start earning', + icon: UiIcons.sparkles, + ), + ), ShiftDetailsHeader(shift: displayShift), const Divider(height: 1, thickness: 0.5), ShiftStatsRow( @@ -194,20 +207,21 @@ class _ShiftDetailsPageState extends State { ), ), ), - ShiftDetailsBottomBar( - shift: displayShift, - onApply: () => _bookShift(context, displayShift), - onDecline: () => BlocProvider.of( - context, - ).add(DeclineShiftDetailsEvent(displayShift.id)), - onAccept: () => - BlocProvider.of(context).add( - BookShiftDetailsEvent( - displayShift.id, - roleId: displayShift.roleId, + if (isProfileComplete) + ShiftDetailsBottomBar( + shift: displayShift, + onApply: () => _bookShift(context, displayShift), + onDecline: () => BlocProvider.of( + context, + ).add(DeclineShiftDetailsEvent(displayShift.id)), + onAccept: () => + BlocProvider.of(context).add( + BookShiftDetailsEvent( + displayShift.id, + roleId: displayShift.roleId, + ), ), - ), - ), + ), ], ), ); diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/shift_details_module.dart b/apps/mobile/packages/features/staff/shifts/lib/src/shift_details_module.dart index f22fc524..fba55262 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/shift_details_module.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/shift_details_module.dart @@ -1,4 +1,5 @@ import 'package:flutter_modular/flutter_modular.dart'; +import 'package:krow_data_connect/krow_data_connect.dart'; import 'domain/repositories/shifts_repository_interface.dart'; import 'data/repositories_impl/shifts_repository_impl.dart'; import 'domain/usecases/get_shift_details_usecase.dart'; @@ -14,11 +15,21 @@ class ShiftDetailsModule extends Module { // Repository i.add(ShiftsRepositoryImpl.new); + // StaffConnectorRepository for profile completion + i.addLazySingleton( + () => StaffConnectorRepositoryImpl(), + ); + // UseCases i.add(GetShiftDetailsUseCase.new); i.add(AcceptShiftUseCase.new); i.add(DeclineShiftUseCase.new); i.add(ApplyForShiftUseCase.new); + i.addLazySingleton( + () => GetProfileCompletionUseCase( + repository: i.get(), + ), + ); // Bloc i.add(ShiftDetailsBloc.new); diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/staff_shifts_module.dart b/apps/mobile/packages/features/staff/shifts/lib/src/staff_shifts_module.dart index 5934588f..09866f32 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/staff_shifts_module.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/staff_shifts_module.dart @@ -32,18 +32,18 @@ class StaffShiftsModule extends Module { ); // Repository - i.add(ShiftsRepositoryImpl.new); + i.addLazySingleton(ShiftsRepositoryImpl.new); // UseCases - i.add(GetMyShiftsUseCase.new); - i.add(GetAvailableShiftsUseCase.new); - i.add(GetPendingAssignmentsUseCase.new); - i.add(GetCancelledShiftsUseCase.new); - i.add(GetHistoryShiftsUseCase.new); - i.add(AcceptShiftUseCase.new); - i.add(DeclineShiftUseCase.new); - i.add(ApplyForShiftUseCase.new); - i.add(GetShiftDetailsUseCase.new); + i.addLazySingleton(GetMyShiftsUseCase.new); + i.addLazySingleton(GetAvailableShiftsUseCase.new); + i.addLazySingleton(GetPendingAssignmentsUseCase.new); + i.addLazySingleton(GetCancelledShiftsUseCase.new); + i.addLazySingleton(GetHistoryShiftsUseCase.new); + i.addLazySingleton(AcceptShiftUseCase.new); + i.addLazySingleton(DeclineShiftUseCase.new); + i.addLazySingleton(ApplyForShiftUseCase.new); + i.addLazySingleton(GetShiftDetailsUseCase.new); // Bloc i.add( diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml index 6f3eca62..bd577ae8 100644 --- a/apps/web/pnpm-lock.yaml +++ b/apps/web/pnpm-lock.yaml @@ -4,9 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -overrides: - '@dataconnect/generated': link:src/dataconnect-generated - importers: .: diff --git a/apps/web/pnpm-workspace.yaml b/apps/web/pnpm-workspace.yaml index 117a15bd..9410b45d 100644 --- a/apps/web/pnpm-workspace.yaml +++ b/apps/web/pnpm-workspace.yaml @@ -1,2 +1,5 @@ +packages: + - '.' + overrides: '@dataconnect/generated': link:src/dataconnect-generated diff --git a/apps/web/src/common/config/navigation.ts b/apps/web/src/common/config/navigation.ts index ede3d92a..7de926c6 100644 --- a/apps/web/src/common/config/navigation.ts +++ b/apps/web/src/common/config/navigation.ts @@ -62,12 +62,6 @@ export const NAV_CONFIG: NavGroup[] = [ icon: LayoutDashboard, allowedRoles: ['Vendor'], }, - { - label: 'Savings Engine', - path: '/savings', - icon: PiggyBank, - allowedRoles: ALL_ROLES, - }, { label: 'Vendor Performance', path: '/performance', @@ -117,23 +111,6 @@ export const NAV_CONFIG: NavGroup[] = [ }, ], }, - { - title: 'Marketplace', - items: [ - { - label: 'Discovery', - path: '/marketplace', - icon: ShoppingBag, - allowedRoles: ['Client', 'Admin'], - }, - { - label: 'Compare Rates', - path: '/marketplace/compare', - icon: Scale, - allowedRoles: ['Client', 'Admin'], - }, - ], - }, { title: 'Workforce', items: [ @@ -143,18 +120,6 @@ export const NAV_CONFIG: NavGroup[] = [ icon: Users, allowedRoles: ALL_ROLES, }, - { - label: 'Onboarding', - path: '/onboarding', - icon: UserPlus, - allowedRoles: ALL_ROLES, - }, - { - label: 'Teams', - path: '/teams', - icon: Users2, - allowedRoles: ALL_ROLES, - }, { label: 'Compliance', path: '/compliance', @@ -197,44 +162,4 @@ export const NAV_CONFIG: NavGroup[] = [ }, ], }, - { - title: 'Analytics & Comm', - items: [ - { - label: 'Reports', - path: '/reports', - icon: PieChart, - allowedRoles: ALL_ROLES, - }, - { - label: 'Activity Log', - path: '/activity', - icon: History, - allowedRoles: ['Vendor', 'Admin'], - }, - { - label: 'Messages', - path: '/messages', - icon: MessageSquare, - allowedRoles: ALL_ROLES, - }, - { - label: 'Tutorials', - path: '/tutorials', - icon: BookOpen, - allowedRoles: ['Client', 'Admin'], - }, - ], - }, - { - title: 'Support', - items: [ - { - label: 'Help Center', - path: '/support', - icon: HelpCircle, - allowedRoles: ['Client', 'Admin'], - }, - ], - }, ]; diff --git a/apps/web/src/features/operations/orders/components/CreateOrderDialog.tsx b/apps/web/src/features/operations/orders/components/CreateOrderDialog.tsx index 718e6e82..763e1f81 100644 --- a/apps/web/src/features/operations/orders/components/CreateOrderDialog.tsx +++ b/apps/web/src/features/operations/orders/components/CreateOrderDialog.tsx @@ -6,7 +6,7 @@ import { DialogTitle, } from "@/common/components/ui/dialog"; import EventFormWizard from "./EventFormWizard"; -import { useCreateOrder, useListBusinesses, useListHubs } from "@/dataconnect-generated/react"; +import { useCreateOrder, useListBusinesses, useListTeamHubs } from "@/dataconnect-generated/react"; import { OrderType, OrderStatus } from "@/dataconnect-generated"; import { dataConnect } from "@/features/auth/firebase"; import { useToast } from "@/common/components/ui/use-toast"; @@ -26,7 +26,7 @@ export default function CreateOrderDialog({ open, onOpenChange }: CreateOrderDia const [selectedHubId, setSelectedHubId] = React.useState(""); const { data: businessesData } = useListBusinesses(dataConnect); - const { data: hubsData } = useListHubs(dataConnect); + const { data: hubsData } = useListTeamHubs(dataConnect); const createOrderMutation = useCreateOrder(dataConnect, { onSuccess: () => { @@ -109,9 +109,9 @@ export default function CreateOrderDialog({ open, onOpenChange }: CreateOrderDia - {hubsData?.hubs.map((h) => ( + {hubsData?.teamHubs.map((h) => ( - {h.name} + {h.hubName} ))} diff --git a/codemagic.yaml b/codemagic.yaml index d853fbba..d90d8463 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -4,7 +4,7 @@ # Reusable script for building the Flutter app client-app-android-apk-build-script: &client-app-android-apk-build-script - name: 👷🤖 Build Client App APK (Android) + name: 👷 🤖 Build Client App APK (Android) script: | dart pub global activate melos export PATH="$PATH":"$HOME/.pub-cache/bin" @@ -12,7 +12,7 @@ client-app-android-apk-build-script: &client-app-android-apk-build-script make mobile-client-build PLATFORM=apk MODE=release client-app-ios-build-script: &client-app-ios-build-script - name: 👷🍎 Build Client App (iOS) + name: 👷 🍎 Build Client App (iOS) script: | dart pub global activate melos export PATH="$PATH":"$HOME/.pub-cache/bin" @@ -20,7 +20,7 @@ client-app-ios-build-script: &client-app-ios-build-script make mobile-client-build PLATFORM=ios MODE=release staff-app-android-apk-build-script: &staff-app-android-apk-build-script - name: 👷🤖 Build Staff App APK (Android) + name: 👷 🤖 Build Staff App APK (Android) script: | dart pub global activate melos export PATH="$PATH":"$HOME/.pub-cache/bin" @@ -28,7 +28,7 @@ staff-app-android-apk-build-script: &staff-app-android-apk-build-script make mobile-staff-build PLATFORM=apk MODE=release staff-app-ios-build-script: &staff-app-ios-build-script - name: 👷🍎 Build Staff App (iOS) + name: 👷 🍎 Build Staff App (iOS) script: | dart pub global activate melos export PATH="$PATH":"$HOME/.pub-cache/bin" @@ -37,7 +37,7 @@ staff-app-ios-build-script: &staff-app-ios-build-script # Reusable script for distributing Android to Firebase distribute-android-script: &distribute-android-script - name: 🚛🤖 Distribute Android to Firebase App Distribution + name: 🚛 🤖 Distribute Android to Firebase App Distribution script: | # Distribute Android APK # Note: Using wildcards to catch app-release.apk @@ -167,7 +167,7 @@ workflows: # ================================================================================= client-app-dev-android: <<: *client-app-base - name: 🚛🤖 Client App Dev (Android App Distribution) + name: 🚛 🤖 Client App Dev (Android App Distribution) environment: flutter: stable xcode: latest @@ -175,11 +175,7 @@ workflows: groups: - client_app_dev_credentials android_signing: - - keystore: krow_client_dev - keystore_environment_variable: CM_KEYSTORE_PATH_CLIENT - keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_CLIENT - key_alias_environment_variable: CM_KEY_ALIAS_CLIENT - key_password_environment_variable: CM_KEY_PASSWORD_CLIENT + - keystore: KROW_CLIENT_DEV vars: ENV: dev scripts: @@ -196,11 +192,7 @@ workflows: groups: - client_app_staging_credentials android_signing: - - keystore: krow_client_staging - keystore_environment_variable: CM_KEYSTORE_PATH_CLIENT - keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_CLIENT - key_alias_environment_variable: CM_KEY_ALIAS_CLIENT - key_password_environment_variable: CM_KEY_PASSWORD_CLIENT + - keystore: KROW_CLIENT_STAGING vars: ENV: staging scripts: @@ -209,12 +201,12 @@ workflows: client-app-prod-android: <<: *client-app-base - name: 🚛🤖 Client App Prod (Android App Distribution) + name: 🚛 🤖 Client App Prod (Android App Distribution) environment: groups: - client_app_prod_credentials android_signing: - - keystore: krow_client_prod + - keystore: KROW_CLIENT_PROD keystore_environment_variable: CM_KEYSTORE_PATH_CLIENT keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_CLIENT key_alias_environment_variable: CM_KEY_ALIAS_CLIENT @@ -230,7 +222,7 @@ workflows: # ================================================================================= client-app-dev-ios: <<: *client-app-base - name: 🚛🍎 Client App Dev (iOS App Distribution) + name: 🚛 🍎 Client App Dev (iOS App Distribution) environment: groups: - client_app_dev_credentials @@ -242,7 +234,7 @@ workflows: client-app-staging-ios: <<: *client-app-base - name: 🚛🍎 Client App Staging (iOS App Distribution) + name: 🚛 🍎 Client App Staging (iOS App Distribution) environment: groups: - client_app_staging_credentials @@ -254,7 +246,7 @@ workflows: client-app-prod-ios: <<: *client-app-base - name: 🚛🍎 Client App Prod (iOS App Distribution) + name: 🚛 🍎 Client App Prod (iOS App Distribution) environment: groups: - client_app_prod_credentials @@ -269,7 +261,7 @@ workflows: # ================================================================================= staff-app-dev-android: <<: *staff-app-base - name: 🚛🤖👨‍🍳 Staff App Dev (Android App Distribution) + name: 🚛 🤖 👨‍🍳 Staff App Dev (Android App Distribution) environment: flutter: stable xcode: latest @@ -277,11 +269,7 @@ workflows: groups: - staff_app_dev_credentials android_signing: - - keystore: krow_staff_dev - keystore_environment_variable: CM_KEYSTORE_PATH_STAFF - keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_STAFF - key_alias_environment_variable: CM_KEY_ALIAS_STAFF - key_password_environment_variable: CM_KEY_PASSWORD_STAFF + - keystore: KROW_STAFF_DEV vars: ENV: dev scripts: @@ -290,7 +278,7 @@ workflows: staff-app-staging-android: <<: *staff-app-base - name: 🚛🤖👨‍🍳 Staff App Staging (Android App Distribution) + name: 🚛 🤖 👨‍🍳 Staff App Staging (Android App Distribution) environment: flutter: stable xcode: latest @@ -298,11 +286,7 @@ workflows: groups: - staff_app_staging_credentials android_signing: - - keystore: krow_staff_staging - keystore_environment_variable: CM_KEYSTORE_PATH_STAFF - keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_STAFF - key_alias_environment_variable: CM_KEY_ALIAS_STAFF - key_password_environment_variable: CM_KEY_PASSWORD_STAFF + - keystore: KROW_STAFF_STAGING vars: ENV: staging scripts: @@ -311,7 +295,7 @@ workflows: staff-app-prod-android: <<: *staff-app-base - name: 🚛🤖👨‍🍳 Staff App Prod (Android App Distribution) + name: 🚛 🤖 👨‍🍳 Staff App Prod (Android App Distribution) environment: flutter: stable xcode: latest @@ -319,11 +303,7 @@ workflows: groups: - staff_app_prod_credentials android_signing: - - keystore: krow_staff_prod - keystore_environment_variable: CM_KEYSTORE_PATH_STAFF - keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_STAFF - key_alias_environment_variable: CM_KEY_ALIAS_STAFF - key_password_environment_variable: CM_KEY_PASSWORD_STAFF + - keystore: KROW_STAFF_PROD vars: ENV: prod scripts: @@ -335,7 +315,7 @@ workflows: # ================================================================================= staff-app-dev-ios: <<: *staff-app-base - name: 🚛🍎👨‍🍳 Staff App Dev (iOS App Distribution) + name: 🚛 🍎 👨‍🍳 Staff App Dev (iOS App Distribution) environment: groups: - staff_app_dev_credentials @@ -347,7 +327,7 @@ workflows: staff-app-staging-ios: <<: *staff-app-base - name: 🚛🍎👨‍🍳 Staff App Staging (iOS App Distribution) + name: 🚛 🍎 👨‍🍳 Staff App Staging (iOS App Distribution) environment: groups: - staff_app_staging_credentials @@ -359,7 +339,7 @@ workflows: staff-app-prod-ios: <<: *staff-app-base - name: 🚛🍎👨‍🍳 Staff App Prod (iOS App Distribution) + name: 🚛 🍎 👨‍🍳 Staff App Prod (iOS App Distribution) environment: groups: - staff_app_prod_credentials diff --git a/docs/MILESTONES/M4/demos/m4-client-note.md b/docs/MILESTONES/M4/demos/m4-client-note.md new file mode 100644 index 00000000..550282ca --- /dev/null +++ b/docs/MILESTONES/M4/demos/m4-client-note.md @@ -0,0 +1,82 @@ +# KROW Workforce Platform — M4 Guide + +**Version:** Milestone 4 (0.0.1-IlianaStaffM4 and 0.0.1-IlianaClientM4) +**Estimated Duration:** 25-30 minutes + +--- + +## 📦 Deliverables + +- **Client Mobile Application** (v0.0.1-IlianaClientM4) +- **Staff Mobile Application** (v0.0.1-IlianaStaffM4) +- **Full Demo Video** - Comprehensive walkthrough of all (M1 - M4) completed features of the mobile applications. + +--- + +## 1. Overview + +### Core Improvements +M4 delivers three key areas of improvement: + +1. **Overall Application Improvements** + - Auth session persistence: Users stay signed in after reopening the app + - Stability fixes from M3 client feedback and dev team discoveries + - UI/UX improvements across key screens for clarity and speed + +2. **Client App Updates** + - Complete order creation flow (Rapid, Permanent, Recurring orders) + - Shift manager assignment support + - Paid/unpaid break handling in orders + - Complete Reports section (Daily Ops, Spend, Coverage, No-show, Performance) + - Cost centres in hubs for location/business unit tracking + - Billing approval workflow for pending bills + +3. **Staff App Updates** + - Profile completion requirements gating payments and clockings + - Worker benefits integration + - Enhanced shift discovery with filtering by location + - Spanish localization support + - AI-verified document uploads (Attire, Documents, Certificates) + - FAQ and Privacy Policy + - Worker profile visibility controls + +--- + +## 2. Required Test Accounts + +**Client Account (Business User):** +- Email: `legendary@krowd.com` +- Password: `Demo2026!` +- Client Name: "KROW" + +**Staff Account (Worker):** +- Phone: `+15557654321` +- OTP Code: `123456` (testing mode) +- Name: "Mariana Torres" + +***Note on Profile Completion*** +When a staff user hasn't completed their profile, they see an empty/incomplete state on their home screen. Currently tracked sections to mark as complete: +- Profile Information (full name, email, phone, preferred locations) +- Emergency Contact + +Future sections can be added as mandatory, such as Tax Forms, Bank Account, Documents, Certificates, and Attires. + +***Profile Blocking Rules*** +When the profile is incomplete, the following features are blocked to encourage completion: +- Clock-in page is hidden +- Payments are blocked +- "My Shifts" and History sections are hidden +- Users can view available shifts but cannot book them + +This ensures we have all necessary information for compliance and payroll before workers are allowed to work. + +--- + +## 3. M4 Key Deliverables + +✅ Stronger reliability and stability +✅ Completed client ordering and reporting workflows +✅ Better profile and shift tooling for staff +✅ AI-assisted document verification +✅ Localization support (Spanish) +✅ Improved billing and cost tracking controls