diff --git a/apps/mobile/packages/features/client/client_main/pubspec.yaml b/apps/mobile/packages/features/client/client_main/pubspec.yaml index 4420cdcd..139eaca1 100644 --- a/apps/mobile/packages/features/client/client_main/pubspec.yaml +++ b/apps/mobile/packages/features/client/client_main/pubspec.yaml @@ -5,17 +5,14 @@ publish_to: none resolution: workspace environment: - sdk: '>=3.10.0 <4.0.0' + sdk: ">=3.10.0 <4.0.0" flutter: ">=3.0.0" dependencies: flutter: sdk: flutter - flutter_bloc: ^8.1.0 - flutter_modular: ^6.3.0 - equatable: ^2.0.5 - - # Architecture Packages + + # Architecture Packages design_system: path: ../../../design_system core_localization: @@ -30,10 +27,12 @@ dependencies: path: ../view_orders billing: path: ../billing + krow_core: + path: ../../../core - # Intentionally commenting these out as they might not exist yet - # client_settings: - # path: ../settings + flutter_bloc: ^8.1.0 + flutter_modular: ^6.3.0 + equatable: ^2.0.5 dev_dependencies: flutter_test: diff --git a/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart b/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart index fff9a19c..cd6c15fb 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart @@ -390,6 +390,12 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte throw UnimplementedError('Rapid order IA is not connected yet.'); } + @override + Future reorder(String previousOrderId, DateTime newDate) async { + // TODO: Implement reorder functionality to fetch the previous order and create a new one with the updated date. + throw UnimplementedError('Reorder functionality is not yet implemented.'); + } + double _calculateShiftCost(domain.OneTimeOrder order) { double total = 0; for (final domain.OneTimeOrderPosition position in order.positions) { diff --git a/apps/mobile/packages/features/client/create_order/lib/src/domain/repositories/client_create_order_repository_interface.dart b/apps/mobile/packages/features/client/create_order/lib/src/domain/repositories/client_create_order_repository_interface.dart index 0fe29f6b..d7eed014 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/domain/repositories/client_create_order_repository_interface.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/domain/repositories/client_create_order_repository_interface.dart @@ -27,4 +27,10 @@ abstract interface class ClientCreateOrderRepositoryInterface { /// /// [description] is the text message (or transcribed voice) describing the need. Future createRapidOrder(String description); + + /// Reorders an existing staffing order with a new date. + /// + /// [previousOrderId] is the ID of the order to reorder. + /// [newDate] is the new date for the order. + Future reorder(String previousOrderId, DateTime newDate); } diff --git a/apps/mobile/packages/features/client/create_order/lib/src/domain/usecases/reorder_usecase.dart b/apps/mobile/packages/features/client/create_order/lib/src/domain/usecases/reorder_usecase.dart index 296816cf..ddd90f2c 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/domain/usecases/reorder_usecase.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/domain/usecases/reorder_usecase.dart @@ -13,7 +13,7 @@ class ReorderArguments { } /// Use case for reordering an existing staffing order. -class ReorderUseCase implements UseCase, ReorderArguments> { +class ReorderUseCase implements UseCase { const ReorderUseCase(this._repository); final ClientCreateOrderRepositoryInterface _repository; diff --git a/apps/mobile/packages/features/client/home/lib/src/presentation/widgets/dashboard_widget_builder.dart b/apps/mobile/packages/features/client/home/lib/src/presentation/widgets/dashboard_widget_builder.dart index 488a9bb3..0964f2ee 100644 --- a/apps/mobile/packages/features/client/home/lib/src/presentation/widgets/dashboard_widget_builder.dart +++ b/apps/mobile/packages/features/client/home/lib/src/presentation/widgets/dashboard_widget_builder.dart @@ -85,7 +85,7 @@ class DashboardWidgetBuilder extends StatelessWidget { return; } Modular.to.navigate( - '/client-main/orders/', + ClientPaths.orders, arguments: { 'initialDate': initialDate.toIso8601String(), }, 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 deleted file mode 100644 index 24a0bef4..00000000 --- a/apps/mobile/packages/features/client/reports/lib/src/presentation/pages/coverage_report_page.dart +++ /dev/null @@ -1,464 +0,0 @@ -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: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'; - -class CoverageReportPage extends StatefulWidget { - const CoverageReportPage({super.key}); - - @override - State createState() => _CoverageReportPageState(); -} - -class _CoverageReportPageState extends State { - DateTime _startDate = DateTime.now(); - DateTime _endDate = DateTime.now().add(const Duration(days: 6)); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (context) => Modular.get() - ..add(LoadCoverageReport(startDate: _startDate, endDate: _endDate)), - child: Scaffold( - backgroundColor: UiColors.bgMenu, - body: BlocBuilder( - builder: (context, state) { - if (state is CoverageLoading) { - return const Center(child: CircularProgressIndicator()); - } - - if (state is CoverageError) { - return Center(child: Text(state.message)); - } - - if (state is CoverageLoaded) { - final report = state.report; - - // Compute "Full" and "Needs Help" counts from daily coverage - final fullDays = report.dailyCoverage - .where((d) => d.percentage >= 100) - .length; - final needsHelpDays = report.dailyCoverage - .where((d) => d.percentage < 80) - .length; - - return SingleChildScrollView( - child: Column( - children: [ - // ── Header ─────────────────────────────────────────── - Container( - padding: const EdgeInsets.only( - top: 60, - left: 20, - right: 20, - bottom: 80, // Increased bottom padding for overlap background - ), - decoration: const BoxDecoration( - gradient: LinearGradient( - colors: [ - UiColors.primary, - UiColors.buttonPrimaryHover, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - ), - child: Column( - children: [ - // Title row - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - GestureDetector( - onTap: () => Navigator.of(context).pop(), - child: Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: UiColors.white.withOpacity(0.2), - shape: BoxShape.circle, - ), - child: const Icon( - UiIcons.arrowLeft, - color: UiColors.white, - size: 20, - ), - ), - ), - const SizedBox(width: 12), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - context.t.client_reports.coverage_report - .title, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: UiColors.white, - ), - ), - Text( - context.t.client_reports.coverage_report - .subtitle, - style: TextStyle( - fontSize: 12, - color: - UiColors.white.withOpacity(0.7), - ), - ), - ], - ), - ], - ), - // Export button -/* - GestureDetector( - onTap: () { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.t.client_reports.coverage_report - .placeholders.export_message, - ), - duration: const Duration(seconds: 2), - ), - ); - }, - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), - decoration: BoxDecoration( - color: UiColors.white, - borderRadius: BorderRadius.circular(8), - ), - child: const Row( - children: [ - Icon(UiIcons.download, - size: 14, color: UiColors.primary), - SizedBox(width: 6), - Text( - 'Export', - style: TextStyle( - color: UiColors.primary, - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ), -*/ - ], - ), - ], - ), - ), - - // ── 3 summary stat chips (Moved here for overlap) ── - Transform.translate( - offset: const Offset(0, -60), // Pull up to overlap header - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Row( - children: [ - _CoverageStatCard( - icon: UiIcons.trendingUp, - label: context.t.client_reports.coverage_report.metrics.avg_coverage, - value: '${report.overallCoverage.toStringAsFixed(0)}%', - iconColor: UiColors.primary, - ), - const SizedBox(width: 12), - _CoverageStatCard( - icon: UiIcons.checkCircle, - label: context.t.client_reports.coverage_report.metrics.full, - value: fullDays.toString(), - iconColor: UiColors.success, - ), - const SizedBox(width: 12), - _CoverageStatCard( - icon: UiIcons.warning, - label: context.t.client_reports.coverage_report.metrics.needs_help, - value: needsHelpDays.toString(), - iconColor: UiColors.error, - ), - ], - ), - ), - ), - - // ── Content ────────────────────────────────────────── - Transform.translate( - offset: const Offset(0, -60), // Pull up to overlap header - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 32), - - // Section label - Text( - context.t.client_reports.coverage_report.next_7_days, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: UiColors.textPrimary, - letterSpacing: 0.5, - ), - ), - const SizedBox(height: 16), - - if (report.dailyCoverage.isEmpty) - Container( - padding: const EdgeInsets.all(40), - alignment: Alignment.center, - child: Text( - context.t.client_reports.coverage_report.empty_state, - style: const TextStyle( - color: UiColors.textSecondary, - ), - ), - ) - else - ...report.dailyCoverage.map( - (day) => _DayCoverageCard( - date: DateFormat('EEE, MMM d').format(day.date), - filled: day.filled, - needed: day.needed, - percentage: day.percentage, - ), - ), - - const SizedBox(height: 100), - ], - ), - ), - ), - ], - ), - ); - } - return const SizedBox.shrink(); - }, - ), - ), - ); - } -} - -// ── Header stat chip (inside the blue header) ───────────────────────────────── -// ── Header stat card (boxes inside the blue header overlap) ─────────────────── -class _CoverageStatCard extends StatelessWidget { - final IconData icon; - final String label; - final String value; - final Color iconColor; - - const _CoverageStatCard({ - required this.icon, - required this.label, - required this.value, - required this.iconColor, - }); - - @override - Widget build(BuildContext context) { - return Expanded( - child: Container( - padding: const EdgeInsets.all(16), // Increased padding - decoration: BoxDecoration( - color: UiColors.white, - borderRadius: BorderRadius.circular(16), // More rounded - boxShadow: [ - BoxShadow( - color: UiColors.black.withOpacity(0.04), - blurRadius: 10, - offset: const Offset(0, 4), - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Icon( - icon, - size: 14, - color: iconColor, - ), - const SizedBox(width: 6), - Expanded( - child: Text( - label, - style: const TextStyle( - fontSize: 11, - color: UiColors.textSecondary, - fontWeight: FontWeight.w500, - ), - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - const SizedBox(height: 8), - Text( - value, - style: const TextStyle( - fontSize: 20, // Slightly smaller to fit if needed - fontWeight: FontWeight.bold, - color: UiColors.textPrimary, - ), - ), - ], - ), - ), - ); - } -} - -// ── Day coverage card ───────────────────────────────────────────────────────── -class _DayCoverageCard extends StatelessWidget { - final String date; - final int filled; - final int needed; - final double percentage; - - const _DayCoverageCard({ - required this.date, - required this.filled, - required this.needed, - required this.percentage, - }); - - @override - Widget build(BuildContext context) { - final isFullyStaffed = percentage >= 100; - final spotsRemaining = (needed - filled).clamp(0, needed); - - final barColor = percentage >= 95 - ? UiColors.success - : percentage >= 80 - ? UiColors.primary - : UiColors.error; - - final badgeColor = percentage >= 95 - ? UiColors.success - : percentage >= 80 - ? UiColors.primary - : UiColors.error; - - final badgeBg = percentage >= 95 - ? UiColors.tagSuccess - : percentage >= 80 - ? UiColors.primary.withOpacity(0.1) // Blue tint - : UiColors.tagError; - - return Container( - margin: const EdgeInsets.only(bottom: 12), - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: UiColors.white, - borderRadius: BorderRadius.circular(12), - boxShadow: [ - BoxShadow( - color: UiColors.black.withOpacity(0.03), - blurRadius: 6, - ), - ], - ), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - date, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - color: UiColors.textPrimary, - ), - ), - const SizedBox(height: 2), - Text( - context.t.client_reports.coverage_report.shift_item.confirmed_workers(confirmed: filled.toString(), needed: needed.toString()), - style: const TextStyle( - fontSize: 12, - color: UiColors.textSecondary, - ), - ), - ], - ), - // Percentage badge - Container( - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 5, - ), - decoration: BoxDecoration( - color: badgeBg, - borderRadius: BorderRadius.circular(8), - ), - child: Text( - '${percentage.toStringAsFixed(0)}%', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: badgeColor, - ), - ), - ), - ], - ), - const SizedBox(height: 12), - ClipRRect( - borderRadius: BorderRadius.circular(4), - child: LinearProgressIndicator( - value: (percentage / 100).clamp(0.0, 1.0), - backgroundColor: UiColors.bgSecondary, - valueColor: AlwaysStoppedAnimation(barColor), - minHeight: 6, - ), - ), - const SizedBox(height: 8), - Align( - alignment: Alignment.centerRight, - child: Text( - isFullyStaffed - ? context.t.client_reports.coverage_report.shift_item.fully_staffed - : spotsRemaining == 1 - ? context.t.client_reports.coverage_report.shift_item.one_spot_remaining - : context.t.client_reports.coverage_report.shift_item.spots_remaining(count: spotsRemaining.toString()), - style: TextStyle( - fontSize: 11, - color: isFullyStaffed - ? UiColors.success - : UiColors.textSecondary, - fontWeight: isFullyStaffed - ? FontWeight.w500 - : FontWeight.normal, - ), - ), - ), - ], - ), - ); - } -} 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 6c3f538e..fbc60def 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 @@ -7,6 +7,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'; class ReportsPage extends StatefulWidget { const ReportsPage({super.key}); @@ -36,8 +37,8 @@ class _ReportsPageState extends State DateTime.now(), ), ( - DateTime(DateTime.now().year, ((DateTime.now().month - 1) ~/ 3) * 3 + 1, - 1), + DateTime( + DateTime.now().year, ((DateTime.now().month - 1) ~/ 3) * 3 + 1, 1), DateTime.now(), ), ]; @@ -102,8 +103,7 @@ class _ReportsPageState extends State Row( children: [ GestureDetector( - onTap: () => - Modular.to.navigate('/client-main/home'), + onTap: () => Modular.to.toClientHome(), child: Container( width: 40, height: 40, @@ -209,8 +209,8 @@ class _ReportsPageState extends State } final summary = (state as ReportsSummaryLoaded).summary; - final currencyFmt = - NumberFormat.currency(symbol: '\$', decimalDigits: 0); + final currencyFmt = NumberFormat.currency( + symbol: '\$', decimalDigits: 0); return GridView.count( crossAxisCount: 2, @@ -261,8 +261,7 @@ class _ReportsPageState extends State icon: UiIcons.trendingUp, label: context .t.client_reports.metrics.fill_rate.label, - value: - '${summary.fillRate.toStringAsFixed(0)}%', + value: '${summary.fillRate.toStringAsFixed(0)}%', badgeText: context .t.client_reports.metrics.fill_rate.badge, badgeColor: UiColors.tagInProgress, @@ -271,12 +270,12 @@ class _ReportsPageState extends State ), _MetricCard( icon: UiIcons.clock, - label: context.t.client_reports.metrics - .avg_fill_time.label, + label: context + .t.client_reports.metrics.avg_fill_time.label, value: '${summary.avgFillTimeHours.toStringAsFixed(1)} hrs', - badgeText: context.t.client_reports.metrics - .avg_fill_time.badge, + badgeText: context + .t.client_reports.metrics.avg_fill_time.badge, badgeColor: UiColors.tagInProgress, badgeTextColor: UiColors.textLink, iconColor: UiColors.iconActive, @@ -474,8 +473,7 @@ class _MetricCard extends StatelessWidget { ), const SizedBox(height: 4), Container( - padding: - const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: badgeColor, borderRadius: BorderRadius.circular(10), @@ -580,4 +578,3 @@ class _ReportCard extends StatelessWidget { ); } } - diff --git a/apps/mobile/packages/features/client/reports/lib/src/reports_module.dart b/apps/mobile/packages/features/client/reports/lib/src/reports_module.dart index 959ad51f..d1dc3387 100644 --- a/apps/mobile/packages/features/client/reports/lib/src/reports_module.dart +++ b/apps/mobile/packages/features/client/reports/lib/src/reports_module.dart @@ -1,13 +1,11 @@ import 'package:client_reports/src/data/repositories_impl/reports_repository_impl.dart'; import 'package:client_reports/src/domain/repositories/reports_repository.dart'; import 'package:client_reports/src/presentation/blocs/daily_ops/daily_ops_bloc.dart'; -import 'package:client_reports/src/presentation/blocs/spend/spend_bloc.dart'; -import 'package:client_reports/src/presentation/blocs/coverage/coverage_bloc.dart'; import 'package:client_reports/src/presentation/blocs/forecast/forecast_bloc.dart'; -import 'package:client_reports/src/presentation/blocs/performance/performance_bloc.dart'; import 'package:client_reports/src/presentation/blocs/no_show/no_show_bloc.dart'; +import 'package:client_reports/src/presentation/blocs/performance/performance_bloc.dart'; +import 'package:client_reports/src/presentation/blocs/spend/spend_bloc.dart'; import 'package:client_reports/src/presentation/blocs/summary/reports_summary_bloc.dart'; -import 'package:client_reports/src/presentation/pages/coverage_report_page.dart'; import 'package:client_reports/src/presentation/pages/daily_ops_report_page.dart'; import 'package:client_reports/src/presentation/pages/forecast_report_page.dart'; import 'package:client_reports/src/presentation/pages/no_show_report_page.dart'; @@ -26,7 +24,6 @@ class ReportsModule extends Module { i.addLazySingleton(ReportsRepositoryImpl.new); i.add(DailyOpsBloc.new); i.add(SpendBloc.new); - i.add(CoverageBloc.new); i.add(ForecastBloc.new); i.add(PerformanceBloc.new); i.add(NoShowBloc.new); @@ -41,6 +38,5 @@ class ReportsModule extends Module { r.child('/forecast', child: (_) => const ForecastReportPage()); r.child('/performance', child: (_) => const PerformanceReportPage()); r.child('/no-show', child: (_) => const NoShowReportPage()); - r.child('/coverage', child: (_) => const CoverageReportPage()); } } diff --git a/apps/mobile/packages/features/client/settings/lib/src/presentation/widgets/client_settings_page/settings_logout.dart b/apps/mobile/packages/features/client/settings/lib/src/presentation/widgets/client_settings_page/settings_logout.dart index ea359254..1efc5139 100644 --- a/apps/mobile/packages/features/client/settings/lib/src/presentation/widgets/client_settings_page/settings_logout.dart +++ b/apps/mobile/packages/features/client/settings/lib/src/presentation/widgets/client_settings_page/settings_logout.dart @@ -3,7 +3,6 @@ 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_core/core.dart'; import '../../blocs/client_settings_bloc.dart'; /// A widget that displays the log out button. @@ -59,7 +58,7 @@ class SettingsLogout extends StatelessWidget { style: UiTypography.headline3m.textPrimary, ), content: Text( - t.client_settings.profile.log_out_confirmation, + 'Are you sure you want to log out?', style: UiTypography.body2r.textSecondary, ), actions: [ diff --git a/apps/mobile/packages/features/staff/staff_main/lib/src/presentation/constants/staff_main_routes.dart b/apps/mobile/packages/features/staff/staff_main/lib/src/presentation/constants/staff_main_routes.dart deleted file mode 100644 index db753d22..00000000 --- a/apps/mobile/packages/features/staff/staff_main/lib/src/presentation/constants/staff_main_routes.dart +++ /dev/null @@ -1,16 +0,0 @@ -abstract class StaffMainRoutes { - static const String modulePath = '/worker-main'; - - static const String shifts = '/shifts'; - static const String payments = '/payments'; - static const String home = '/home'; - static const String clockIn = '/clock-in'; - static const String profile = '/profile'; - - // Full paths - static const String shiftsFull = '$modulePath$shifts'; - static const String paymentsFull = '$modulePath$payments'; - static const String homeFull = '$modulePath$home'; - static const String clockInFull = '$modulePath$clockIn'; - static const String profileFull = '$modulePath$profile'; -}