refactor: enhance navigation robustness by introducing popSafe and safePushNamedAndRemoveUntil methods and updating their usage.

This commit is contained in:
Achintha Isuru
2026-02-28 17:23:53 -05:00
parent c26128f1f2
commit 53b612851c
24 changed files with 481 additions and 334 deletions

View File

@@ -74,7 +74,9 @@ class _SessionListenerState extends State<SessionListener> {
// Only show if not initial state (avoid showing on cold start) // Only show if not initial state (avoid showing on cold start)
if (!_isInitialState) { if (!_isInitialState) {
debugPrint('[SessionListener] Session error: ${state.errorMessage}'); debugPrint('[SessionListener] Session error: ${state.errorMessage}');
_showSessionErrorDialog(state.errorMessage ?? 'Session error occurred'); _showSessionErrorDialog(
state.errorMessage ?? 'Session error occurred',
);
} else { } else {
_isInitialState = false; _isInitialState = false;
Modular.to.toClientGetStartedPage(); Modular.to.toClientGetStartedPage();
@@ -126,7 +128,7 @@ class _SessionListenerState extends State<SessionListener> {
TextButton( TextButton(
onPressed: () { onPressed: () {
// User can retry by dismissing and continuing // User can retry by dismissing and continuing
Modular.to.pop(); Modular.to.popSafe();
}, },
child: const Text('Continue'), child: const Text('Continue'),
), ),

View File

@@ -65,7 +65,6 @@ class _SessionListenerState extends State<SessionListener> {
_sessionExpiredDialogShown = false; _sessionExpiredDialogShown = false;
debugPrint('[SessionListener] Authenticated: ${state.userId}'); debugPrint('[SessionListener] Authenticated: ${state.userId}');
// Navigate to the main app // Navigate to the main app
Modular.to.toStaffHome(); Modular.to.toStaffHome();
break; break;
@@ -75,7 +74,9 @@ class _SessionListenerState extends State<SessionListener> {
// Only show if not initial state (avoid showing on cold start) // Only show if not initial state (avoid showing on cold start)
if (!_isInitialState) { if (!_isInitialState) {
debugPrint('[SessionListener] Session error: ${state.errorMessage}'); debugPrint('[SessionListener] Session error: ${state.errorMessage}');
_showSessionErrorDialog(state.errorMessage ?? 'Session error occurred'); _showSessionErrorDialog(
state.errorMessage ?? 'Session error occurred',
);
} else { } else {
_isInitialState = false; _isInitialState = false;
Modular.to.toGetStartedPage(); Modular.to.toGetStartedPage();
@@ -127,7 +128,7 @@ class _SessionListenerState extends State<SessionListener> {
TextButton( TextButton(
onPressed: () { onPressed: () {
// User can retry by dismissing and continuing // User can retry by dismissing and continuing
Modular.to.pop(); Modular.to.popSafe();
}, },
child: const Text('Continue'), child: const Text('Continue'),
), ),

View File

@@ -103,7 +103,7 @@ extension ClientNavigator on IModularNavigator {
/// Navigates to the full list of invoices awaiting approval. /// Navigates to the full list of invoices awaiting approval.
void toAwaitingApproval({Object? arguments}) { void toAwaitingApproval({Object? arguments}) {
safeNavigate(ClientPaths.awaitingApproval, arguments: arguments); safePush(ClientPaths.awaitingApproval, arguments: arguments);
} }
/// Navigates to the Invoice Ready page. /// Navigates to the Invoice Ready page.

View File

@@ -1,3 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'client/route_paths.dart'; import 'client/route_paths.dart';
import 'staff/route_paths.dart'; import 'staff/route_paths.dart';
@@ -29,8 +30,8 @@ extension NavigationExtensions on IModularNavigator {
return true; return true;
} catch (e) { } catch (e) {
// In production, you might want to log this to a monitoring service // In production, you might want to log this to a monitoring service
// ignore: avoid_print // ignore: avoid_debugPrint
print('Navigation error to $path: $e'); debugPrint('Navigation error to $path: $e');
navigateToHome(); navigateToHome();
return false; return false;
} }
@@ -54,8 +55,29 @@ extension NavigationExtensions on IModularNavigator {
return await pushNamed<T>(routeName, arguments: arguments); return await pushNamed<T>(routeName, arguments: arguments);
} catch (e) { } catch (e) {
// In production, you might want to log this to a monitoring service // In production, you might want to log this to a monitoring service
// ignore: avoid_print // ignore: avoid_debugPrint
print('Push navigation error to $routeName: $e'); debugPrint('Push navigation error to $routeName: $e');
navigateToHome();
return null;
}
}
/// Safely pushes a named route and removes until a predicate is met.
Future<T?> safePushNamedAndRemoveUntil<T extends Object?>(
String routeName,
bool Function(Route<dynamic>) predicate, {
Object? arguments,
}) async {
try {
return await pushNamedAndRemoveUntil<T>(
routeName,
predicate,
arguments: arguments,
);
} catch (e) {
// In production, you might want to log this to a monitoring service
// ignore: avoid_debugPrint
debugPrint('PushNamedAndRemoveUntil error to $routeName: $e');
navigateToHome(); navigateToHome();
return null; return null;
} }
@@ -72,9 +94,9 @@ extension NavigationExtensions on IModularNavigator {
/// Pops the current route if possible, otherwise navigates to home. /// Pops the current route if possible, otherwise navigates to home.
/// ///
/// Returns `true` if a route was popped, `false` if it navigated to home. /// Returns `true` if a route was popped, `false` if it navigated to home.
bool popSafe() { bool popSafe<T extends Object?>([T? result]) {
if (canPop()) { if (canPop()) {
pop(); pop(result);
return true; return true;
} }
navigateToHome(); navigateToHome();

View File

@@ -53,7 +53,7 @@ extension StaffNavigator on IModularNavigator {
} }
void toStaffHome() { void toStaffHome() {
pushNamedAndRemoveUntil(StaffPaths.home, (_) => false); safePushNamedAndRemoveUntil(StaffPaths.home, (_) => false);
} }
void toBenefits() { void toBenefits() {
@@ -61,7 +61,7 @@ extension StaffNavigator on IModularNavigator {
} }
void toStaffMain() { void toStaffMain() {
pushNamedAndRemoveUntil('${StaffPaths.main}/home/', (_) => false); safePushNamedAndRemoveUntil('${StaffPaths.main}/home/', (_) => false);
} }
void toShifts({ void toShifts({
@@ -83,11 +83,11 @@ extension StaffNavigator on IModularNavigator {
} }
void toPayments() { void toPayments() {
pushNamedAndRemoveUntil(StaffPaths.payments, (_) => false); safePushNamedAndRemoveUntil(StaffPaths.payments, (_) => false);
} }
void toClockIn() { void toClockIn() {
pushNamedAndRemoveUntil(StaffPaths.clockIn, (_) => false); safePushNamedAndRemoveUntil(StaffPaths.clockIn, (_) => false);
} }
void toProfile() { void toProfile() {
@@ -154,6 +154,18 @@ extension StaffNavigator on IModularNavigator {
safePush(StaffPaths.taxForms); safePush(StaffPaths.taxForms);
} }
void toLanguageSelection() {
safePush(StaffPaths.languageSelection);
}
void toFormI9() {
safeNavigate(StaffPaths.formI9);
}
void toFormW4() {
safeNavigate(StaffPaths.formW4);
}
void toTimeCard() { void toTimeCard() {
safePush(StaffPaths.timeCard); safePush(StaffPaths.timeCard);
} }

View File

@@ -3,6 +3,7 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
import '../blocs/billing_bloc.dart'; import '../blocs/billing_bloc.dart';
import '../blocs/billing_state.dart'; import '../blocs/billing_state.dart';
@@ -20,6 +21,7 @@ class PendingInvoicesPage extends StatelessWidget {
appBar: UiAppBar( appBar: UiAppBar(
title: t.client_billing.awaiting_approval, title: t.client_billing.awaiting_approval,
showBackButton: true, showBackButton: true,
onLeadingPressed: () => Modular.to.toClientBilling(),
), ),
body: _buildBody(context, state), body: _buildBody(context, state),
); );

View File

@@ -46,19 +46,19 @@ class ClientMainCubit extends Cubit<ClientMainState> implements Disposable {
switch (index) { switch (index) {
case 0: case 0:
Modular.to.navigate(ClientPaths.coverage); Modular.to.toClientCoverage();
break; break;
case 1: case 1:
Modular.to.navigate(ClientPaths.billing); Modular.to.toClientBilling();
break; break;
case 2: case 2:
Modular.to.navigate(ClientPaths.home); Modular.to.toClientHome();
break; break;
case 3: case 3:
Modular.to.navigate(ClientPaths.orders); Modular.to.toClientOrders();
break; break;
case 4: case 4:
Modular.to.navigate(ClientPaths.reports); Modular.to.toClientReports();
break; break;
} }
// State update will happen via _onRouteChanged // State update will happen via _onRouteChanged

View File

@@ -80,7 +80,7 @@ class _RapidOrderFormState extends State<_RapidOrderForm> {
subtitle: labels.subtitle, subtitle: labels.subtitle,
date: dateStr, date: dateStr,
time: timeStr, time: timeStr,
onBack: () => Modular.to.navigate(ClientPaths.createOrder), onBack: () => Modular.to.toCreateOrder(),
), ),
// Content // Content

View File

@@ -2,13 +2,13 @@ import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
/// A quick report card widget for navigating to specific reports. /// A quick report card widget for navigating to specific reports.
/// ///
/// Displays an icon, name, and a quick navigation to a report page. /// Displays an icon, name, and a quick navigation to a report page.
/// Used in the quick reports grid of the reports page. /// Used in the quick reports grid of the reports page.
class ReportCard extends StatelessWidget { class ReportCard extends StatelessWidget {
const ReportCard({ const ReportCard({
super.key, super.key,
required this.icon, required this.icon,
@@ -17,6 +17,7 @@ class ReportCard extends StatelessWidget {
required this.iconColor, required this.iconColor,
required this.route, required this.route,
}); });
/// The icon to display for this report. /// The icon to display for this report.
final IconData icon; final IconData icon;
@@ -35,7 +36,7 @@ class ReportCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: () => Modular.to.pushNamed(route), onTap: () => Modular.to.safePush(route),
child: Container( child: Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -86,8 +87,7 @@ class ReportCard extends StatelessWidget {
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
context.t.client_reports.quick_reports context.t.client_reports.quick_reports.two_click_export,
.two_click_export,
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: 12,
color: UiColors.textSecondary, color: UiColors.textSecondary,

View File

@@ -17,9 +17,9 @@ import '../widgets/phone_verification_page/phone_input.dart';
/// This page coordinates the authentication flow by switching between /// This page coordinates the authentication flow by switching between
/// [PhoneInput] and [OtpVerification] based on the current [AuthState]. /// [PhoneInput] and [OtpVerification] based on the current [AuthState].
class PhoneVerificationPage extends StatefulWidget { class PhoneVerificationPage extends StatefulWidget {
/// Creates a [PhoneVerificationPage]. /// Creates a [PhoneVerificationPage].
const PhoneVerificationPage({super.key, required this.mode}); const PhoneVerificationPage({super.key, required this.mode});
/// The authentication mode (login or signup). /// The authentication mode (login or signup).
final AuthMode mode; final AuthMode mode;
@@ -123,10 +123,10 @@ class _PhoneVerificationPageState extends State<PhoneVerificationPage> {
); );
Future<void>.delayed(const Duration(seconds: 5), () { Future<void>.delayed(const Duration(seconds: 5), () {
if (!mounted) return; if (!mounted) return;
Modular.to.navigate('/'); Modular.to.toInitialPage();
}); });
} else if (messageKey == 'errors.auth.unauthorized_app') { } else if (messageKey == 'errors.auth.unauthorized_app') {
Modular.to.pop(); Modular.to.popSafe();
} }
} }
}, },

View File

@@ -155,7 +155,7 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
message: t.staff_certificates.upload_modal.success_snackbar, message: t.staff_certificates.upload_modal.success_snackbar,
type: UiSnackbarType.success, type: UiSnackbarType.success,
); );
Modular.to.pop(); // Returns to certificates list Modular.to.popSafe(); // Returns to certificates list
} else if (state.status == CertificateUploadStatus.failure) { } else if (state.status == CertificateUploadStatus.failure) {
UiSnackbar.show( UiSnackbar.show(
context, context,
@@ -170,7 +170,7 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
title: title:
widget.certificate?.name ?? widget.certificate?.name ??
t.staff_certificates.upload_modal.title, t.staff_certificates.upload_modal.title,
onLeadingPressed: () => Modular.to.pop(), onLeadingPressed: () => Modular.to.popSafe(),
), ),
body: SingleChildScrollView( body: SingleChildScrollView(
padding: const EdgeInsets.all(UiConstants.space5), padding: const EdgeInsets.all(UiConstants.space5),

View File

@@ -1,10 +1,10 @@
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
class CertificatesHeader extends StatelessWidget { class CertificatesHeader extends StatelessWidget {
const CertificatesHeader({ const CertificatesHeader({
super.key, super.key,
required this.completedCount, required this.completedCount,
@@ -16,8 +16,12 @@ class CertificatesHeader extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Prevent division by zero // Prevent division by zero
final double progressValue = totalCount == 0 ? 0 : completedCount / totalCount; final double progressValue = totalCount == 0
final int progressPercent = totalCount == 0 ? 0 : (progressValue * 100).round(); ? 0
: completedCount / totalCount;
final int progressPercent = totalCount == 0
? 0
: (progressValue * 100).round();
return Container( return Container(
padding: const EdgeInsets.fromLTRB( padding: const EdgeInsets.fromLTRB(
@@ -42,7 +46,7 @@ class CertificatesHeader extends StatelessWidget {
Row( Row(
children: <Widget>[ children: <Widget>[
GestureDetector( GestureDetector(
onTap: () => Modular.to.pop(), onTap: () => Modular.to.popSafe(),
child: Container( child: Container(
width: UiConstants.space10, width: UiConstants.space10,
height: UiConstants.space10, height: UiConstants.space10,
@@ -101,7 +105,9 @@ class CertificatesHeader extends StatelessWidget {
const SizedBox(height: UiConstants.space1), const SizedBox(height: UiConstants.space1),
Text( Text(
t.staff_certificates.progress.verified_count( t.staff_certificates.progress.verified_count(
completed: completedCount, total: totalCount), completed: completedCount,
total: totalCount,
),
style: UiTypography.body3r.copyWith( style: UiTypography.body3r.copyWith(
color: UiColors.white.withValues(alpha: 0.7), color: UiColors.white.withValues(alpha: 0.7),
), ),

View File

@@ -1,8 +1,10 @@
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart' hide ModularWatchExtension; import 'package:flutter_modular/flutter_modular.dart'
hide ModularWatchExtension;
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart'; import 'package:krow_domain/krow_domain.dart';
import '../blocs/i9/form_i9_cubit.dart'; import '../blocs/i9/form_i9_cubit.dart';
@@ -18,11 +20,56 @@ class FormI9Page extends StatefulWidget {
class _FormI9PageState extends State<FormI9Page> { class _FormI9PageState extends State<FormI9Page> {
final List<String> _usStates = <String>[ final List<String> _usStates = <String>[
'AL', 'AK', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'FL', 'GA', 'AL',
'HI', 'ID', 'IL', 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MD', 'AK',
'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'AZ',
'NM', 'NY', 'NC', 'ND', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'AR',
'SD', 'TN', 'TX', 'UT', 'VT', 'VA', 'WA', 'WV', 'WI', 'WY' 'CA',
'CO',
'CT',
'DE',
'FL',
'GA',
'HI',
'ID',
'IL',
'IN',
'IA',
'KS',
'KY',
'LA',
'ME',
'MD',
'MA',
'MI',
'MN',
'MS',
'MO',
'MT',
'NE',
'NV',
'NH',
'NJ',
'NM',
'NY',
'NC',
'ND',
'OH',
'OK',
'OR',
'PA',
'RI',
'SC',
'SD',
'TN',
'TX',
'UT',
'VT',
'VA',
'WA',
'WV',
'WI',
'WY',
]; ];
@override @override
@@ -36,10 +83,19 @@ class _FormI9PageState extends State<FormI9Page> {
} }
final List<Map<String, String>> _steps = <Map<String, String>>[ final List<Map<String, String>> _steps = <Map<String, String>>[
<String, String>{'title': 'Personal Information', 'subtitle': 'Name and contact details'}, <String, String>{
'title': 'Personal Information',
'subtitle': 'Name and contact details',
},
<String, String>{'title': 'Address', 'subtitle': 'Your current address'}, <String, String>{'title': 'Address', 'subtitle': 'Your current address'},
<String, String>{'title': 'Citizenship Status', 'subtitle': 'Work authorization verification'}, <String, String>{
<String, String>{'title': 'Review & Sign', 'subtitle': 'Confirm your information'}, 'title': 'Citizenship Status',
'subtitle': 'Work authorization verification',
},
<String, String>{
'title': 'Review & Sign',
'subtitle': 'Confirm your information',
},
]; ];
bool _canProceed(FormI9State state) { bool _canProceed(FormI9State state) {
@@ -77,13 +133,27 @@ class _FormI9PageState extends State<FormI9Page> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final TranslationsStaffComplianceTaxFormsI9En i18n = Translations.of(context).staff_compliance.tax_forms.i9; final TranslationsStaffComplianceTaxFormsI9En i18n = Translations.of(
context,
).staff_compliance.tax_forms.i9;
final List<Map<String, String>> steps = <Map<String, String>>[ final List<Map<String, String>> steps = <Map<String, String>>[
<String, String>{'title': i18n.steps.personal, 'subtitle': i18n.steps.personal_sub}, <String, String>{
<String, String>{'title': i18n.steps.address, 'subtitle': i18n.steps.address_sub}, 'title': i18n.steps.personal,
<String, String>{'title': i18n.steps.citizenship, 'subtitle': i18n.steps.citizenship_sub}, 'subtitle': i18n.steps.personal_sub,
<String, String>{'title': i18n.steps.review, 'subtitle': i18n.steps.review_sub}, },
<String, String>{
'title': i18n.steps.address,
'subtitle': i18n.steps.address_sub,
},
<String, String>{
'title': i18n.steps.citizenship,
'subtitle': i18n.steps.citizenship_sub,
},
<String, String>{
'title': i18n.steps.review,
'subtitle': i18n.steps.review_sub,
},
]; ];
return BlocProvider<FormI9Cubit>.value( return BlocProvider<FormI9Cubit>.value(
@@ -95,7 +165,9 @@ class _FormI9PageState extends State<FormI9Page> {
} else if (state.status == FormI9Status.failure) { } else if (state.status == FormI9Status.failure) {
UiSnackbar.show( UiSnackbar.show(
context, context,
message: translateErrorKey(state.errorMessage ?? 'An error occurred'), message: translateErrorKey(
state.errorMessage ?? 'An error occurred',
),
type: UiSnackbarType.error, type: UiSnackbarType.error,
margin: const EdgeInsets.only( margin: const EdgeInsets.only(
left: UiConstants.space4, left: UiConstants.space4,
@@ -106,7 +178,8 @@ class _FormI9PageState extends State<FormI9Page> {
} }
}, },
builder: (BuildContext context, FormI9State state) { builder: (BuildContext context, FormI9State state) {
if (state.status == FormI9Status.success) return _buildSuccessView(i18n); if (state.status == FormI9Status.success)
return _buildSuccessView(i18n);
return Scaffold( return Scaffold(
backgroundColor: UiColors.background, backgroundColor: UiColors.background,
@@ -175,7 +248,7 @@ class _FormI9PageState extends State<FormI9Page> {
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
child: ElevatedButton( child: ElevatedButton(
onPressed: () => Modular.to.pop(true), onPressed: () => Modular.to.popSafe(true),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: UiColors.primary, backgroundColor: UiColors.primary,
foregroundColor: UiColors.white, foregroundColor: UiColors.white,
@@ -187,7 +260,11 @@ class _FormI9PageState extends State<FormI9Page> {
), ),
elevation: 0, elevation: 0,
), ),
child: Text(Translations.of(context).staff_compliance.tax_forms.w4.back_to_docs), child: Text(
Translations.of(
context,
).staff_compliance.tax_forms.w4.back_to_docs,
),
), ),
), ),
], ],
@@ -218,7 +295,7 @@ class _FormI9PageState extends State<FormI9Page> {
Row( Row(
children: <Widget>[ children: <Widget>[
GestureDetector( GestureDetector(
onTap: () => Modular.to.pop(), onTap: () => Modular.to.popSafe(),
child: const Icon( child: const Icon(
UiIcons.arrowLeft, UiIcons.arrowLeft,
color: UiColors.white, color: UiColors.white,
@@ -229,10 +306,7 @@ class _FormI9PageState extends State<FormI9Page> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(i18n.title, style: UiTypography.headline4m.white),
i18n.title,
style: UiTypography.headline4m.white,
),
Text( Text(
i18n.subtitle, i18n.subtitle,
style: UiTypography.body3r.copyWith( style: UiTypography.body3r.copyWith(
@@ -245,10 +319,9 @@ class _FormI9PageState extends State<FormI9Page> {
), ),
const SizedBox(height: UiConstants.space6), const SizedBox(height: UiConstants.space6),
Row( Row(
children: steps children: steps.asMap().entries.map((
.asMap() MapEntry<int, Map<String, String>> entry,
.entries ) {
.map((MapEntry<int, Map<String, String>> entry) {
final int idx = entry.key; final int idx = entry.key;
final bool isLast = idx == steps.length - 1; final bool isLast = idx == steps.length - 1;
return Expanded( return Expanded(
@@ -384,7 +457,8 @@ class _FormI9PageState extends State<FormI9Page> {
child: _buildTextField( child: _buildTextField(
i18n.fields.first_name, i18n.fields.first_name,
value: state.firstName, value: state.firstName,
onChanged: (String val) => context.read<FormI9Cubit>().firstNameChanged(val), onChanged: (String val) =>
context.read<FormI9Cubit>().firstNameChanged(val),
placeholder: i18n.fields.hints.first_name, placeholder: i18n.fields.hints.first_name,
), ),
), ),
@@ -393,7 +467,8 @@ class _FormI9PageState extends State<FormI9Page> {
child: _buildTextField( child: _buildTextField(
i18n.fields.last_name, i18n.fields.last_name,
value: state.lastName, value: state.lastName,
onChanged: (String val) => context.read<FormI9Cubit>().lastNameChanged(val), onChanged: (String val) =>
context.read<FormI9Cubit>().lastNameChanged(val),
placeholder: i18n.fields.hints.last_name, placeholder: i18n.fields.hints.last_name,
), ),
), ),
@@ -406,7 +481,8 @@ class _FormI9PageState extends State<FormI9Page> {
child: _buildTextField( child: _buildTextField(
i18n.fields.middle_initial, i18n.fields.middle_initial,
value: state.middleInitial, value: state.middleInitial,
onChanged: (String val) => context.read<FormI9Cubit>().middleInitialChanged(val), onChanged: (String val) =>
context.read<FormI9Cubit>().middleInitialChanged(val),
placeholder: i18n.fields.hints.middle_initial, placeholder: i18n.fields.hints.middle_initial,
), ),
), ),
@@ -416,7 +492,8 @@ class _FormI9PageState extends State<FormI9Page> {
child: _buildTextField( child: _buildTextField(
i18n.fields.other_last_names, i18n.fields.other_last_names,
value: state.otherLastNames, value: state.otherLastNames,
onChanged: (String val) => context.read<FormI9Cubit>().otherLastNamesChanged(val), onChanged: (String val) =>
context.read<FormI9Cubit>().otherLastNamesChanged(val),
placeholder: i18n.fields.maiden_name, placeholder: i18n.fields.maiden_name,
), ),
), ),
@@ -426,7 +503,8 @@ class _FormI9PageState extends State<FormI9Page> {
_buildTextField( _buildTextField(
i18n.fields.dob, i18n.fields.dob,
value: state.dob, value: state.dob,
onChanged: (String val) => context.read<FormI9Cubit>().dobChanged(val), onChanged: (String val) =>
context.read<FormI9Cubit>().dobChanged(val),
placeholder: i18n.fields.hints.dob, placeholder: i18n.fields.hints.dob,
keyboardType: TextInputType.datetime, keyboardType: TextInputType.datetime,
), ),
@@ -446,7 +524,8 @@ class _FormI9PageState extends State<FormI9Page> {
_buildTextField( _buildTextField(
i18n.fields.email, i18n.fields.email,
value: state.email, value: state.email,
onChanged: (String val) => context.read<FormI9Cubit>().emailChanged(val), onChanged: (String val) =>
context.read<FormI9Cubit>().emailChanged(val),
keyboardType: TextInputType.emailAddress, keyboardType: TextInputType.emailAddress,
placeholder: i18n.fields.hints.email, placeholder: i18n.fields.hints.email,
), ),
@@ -454,7 +533,8 @@ class _FormI9PageState extends State<FormI9Page> {
_buildTextField( _buildTextField(
i18n.fields.phone, i18n.fields.phone,
value: state.phone, value: state.phone,
onChanged: (String val) => context.read<FormI9Cubit>().phoneChanged(val), onChanged: (String val) =>
context.read<FormI9Cubit>().phoneChanged(val),
keyboardType: TextInputType.phone, keyboardType: TextInputType.phone,
placeholder: i18n.fields.hints.phone, placeholder: i18n.fields.hints.phone,
), ),
@@ -472,14 +552,16 @@ class _FormI9PageState extends State<FormI9Page> {
_buildTextField( _buildTextField(
i18n.fields.address_long, i18n.fields.address_long,
value: state.address, value: state.address,
onChanged: (String val) => context.read<FormI9Cubit>().addressChanged(val), onChanged: (String val) =>
context.read<FormI9Cubit>().addressChanged(val),
placeholder: i18n.fields.hints.address, placeholder: i18n.fields.hints.address,
), ),
const SizedBox(height: UiConstants.space4), const SizedBox(height: UiConstants.space4),
_buildTextField( _buildTextField(
i18n.fields.apt, i18n.fields.apt,
value: state.aptNumber, value: state.aptNumber,
onChanged: (String val) => context.read<FormI9Cubit>().aptNumberChanged(val), onChanged: (String val) =>
context.read<FormI9Cubit>().aptNumberChanged(val),
placeholder: i18n.fields.hints.apt, placeholder: i18n.fields.hints.apt,
), ),
const SizedBox(height: UiConstants.space4), const SizedBox(height: UiConstants.space4),
@@ -490,7 +572,8 @@ class _FormI9PageState extends State<FormI9Page> {
child: _buildTextField( child: _buildTextField(
i18n.fields.city, i18n.fields.city,
value: state.city, value: state.city,
onChanged: (String val) => context.read<FormI9Cubit>().cityChanged(val), onChanged: (String val) =>
context.read<FormI9Cubit>().cityChanged(val),
placeholder: i18n.fields.hints.city, placeholder: i18n.fields.hints.city,
), ),
), ),
@@ -541,7 +624,8 @@ class _FormI9PageState extends State<FormI9Page> {
_buildTextField( _buildTextField(
i18n.fields.zip, i18n.fields.zip,
value: state.zipCode, value: state.zipCode,
onChanged: (String val) => context.read<FormI9Cubit>().zipCodeChanged(val), onChanged: (String val) =>
context.read<FormI9Cubit>().zipCodeChanged(val),
placeholder: i18n.fields.hints.zip, placeholder: i18n.fields.hints.zip,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
), ),
@@ -557,24 +641,11 @@ class _FormI9PageState extends State<FormI9Page> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(i18n.fields.attestation, style: UiTypography.body2m.textPrimary),
i18n.fields.attestation,
style: UiTypography.body2m.textPrimary,
),
const SizedBox(height: UiConstants.space6), const SizedBox(height: UiConstants.space6),
_buildRadioOption( _buildRadioOption(context, state, 'CITIZEN', i18n.fields.citizen),
context,
state,
'CITIZEN',
i18n.fields.citizen,
),
const SizedBox(height: UiConstants.space3), const SizedBox(height: UiConstants.space3),
_buildRadioOption( _buildRadioOption(context, state, 'NONCITIZEN', i18n.fields.noncitizen),
context,
state,
'NONCITIZEN',
i18n.fields.noncitizen,
),
const SizedBox(height: UiConstants.space3), const SizedBox(height: UiConstants.space3),
_buildRadioOption( _buildRadioOption(
context, context,
@@ -587,7 +658,8 @@ class _FormI9PageState extends State<FormI9Page> {
child: _buildTextField( child: _buildTextField(
i18n.fields.uscis_number_label, i18n.fields.uscis_number_label,
value: state.uscisNumber, value: state.uscisNumber,
onChanged: (String val) => context.read<FormI9Cubit>().uscisNumberChanged(val), onChanged: (String val) =>
context.read<FormI9Cubit>().uscisNumberChanged(val),
placeholder: i18n.fields.hints.uscis, placeholder: i18n.fields.hints.uscis,
), ),
) )
@@ -607,19 +679,25 @@ class _FormI9PageState extends State<FormI9Page> {
_buildTextField( _buildTextField(
i18n.fields.admission_number, i18n.fields.admission_number,
value: state.admissionNumber, value: state.admissionNumber,
onChanged: (String val) => context.read<FormI9Cubit>().admissionNumberChanged(val), onChanged: (String val) => context
.read<FormI9Cubit>()
.admissionNumberChanged(val),
), ),
const SizedBox(height: UiConstants.space3), const SizedBox(height: UiConstants.space3),
_buildTextField( _buildTextField(
i18n.fields.passport, i18n.fields.passport,
value: state.passportNumber, value: state.passportNumber,
onChanged: (String val) => context.read<FormI9Cubit>().passportNumberChanged(val), onChanged: (String val) => context
.read<FormI9Cubit>()
.passportNumberChanged(val),
), ),
const SizedBox(height: UiConstants.space3), const SizedBox(height: UiConstants.space3),
_buildTextField( _buildTextField(
i18n.fields.country, i18n.fields.country,
value: state.countryIssuance, value: state.countryIssuance,
onChanged: (String val) => context.read<FormI9Cubit>().countryIssuanceChanged(val), onChanged: (String val) => context
.read<FormI9Cubit>()
.countryIssuanceChanged(val),
), ),
], ],
), ),
@@ -667,10 +745,7 @@ class _FormI9PageState extends State<FormI9Page> {
), ),
const SizedBox(width: UiConstants.space3), const SizedBox(width: UiConstants.space3),
Expanded( Expanded(
child: Text( child: Text(label, style: UiTypography.body2m.textPrimary),
label,
style: UiTypography.body2m.textPrimary,
),
), ),
], ],
), ),
@@ -704,8 +779,14 @@ class _FormI9PageState extends State<FormI9Page> {
style: UiTypography.headline4m.copyWith(fontSize: 14), style: UiTypography.headline4m.copyWith(fontSize: 14),
), ),
const SizedBox(height: UiConstants.space3), const SizedBox(height: UiConstants.space3),
_buildSummaryRow(i18n.fields.summary_name, '${state.firstName} ${state.lastName}'), _buildSummaryRow(
_buildSummaryRow(i18n.fields.summary_address, '${state.address}, ${state.city}'), i18n.fields.summary_name,
'${state.firstName} ${state.lastName}',
),
_buildSummaryRow(
i18n.fields.summary_address,
'${state.address}, ${state.city}',
),
_buildSummaryRow( _buildSummaryRow(
i18n.fields.summary_ssn, i18n.fields.summary_ssn,
'***-**-${state.ssn.length >= 4 ? state.ssn.substring(state.ssn.length - 4) : '****'}', '***-**-${state.ssn.length >= 4 ? state.ssn.substring(state.ssn.length - 4) : '****'}',
@@ -780,10 +861,7 @@ class _FormI9PageState extends State<FormI9Page> {
style: const TextStyle(fontFamily: 'Cursive', fontSize: 18), style: const TextStyle(fontFamily: 'Cursive', fontSize: 18),
), ),
const SizedBox(height: UiConstants.space4), const SizedBox(height: UiConstants.space4),
Text( Text(i18n.fields.date_label, style: UiTypography.body3m.textSecondary),
i18n.fields.date_label,
style: UiTypography.body3m.textSecondary,
),
const SizedBox(height: UiConstants.space1 + 2), const SizedBox(height: UiConstants.space1 + 2),
Container( Container(
width: double.infinity, width: double.infinity,
@@ -811,10 +889,7 @@ class _FormI9PageState extends State<FormI9Page> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Text( Text(label, style: UiTypography.body2r.textSecondary),
label,
style: UiTypography.body2r.textSecondary,
),
Expanded( Expanded(
child: Text( child: Text(
value, value,
@@ -828,7 +903,9 @@ class _FormI9PageState extends State<FormI9Page> {
} }
String _getReadableCitizenship(String status) { String _getReadableCitizenship(String status) {
final TranslationsStaffComplianceTaxFormsI9FieldsEn i18n = Translations.of(context).staff_compliance.tax_forms.i9.fields; final TranslationsStaffComplianceTaxFormsI9FieldsEn i18n = Translations.of(
context,
).staff_compliance.tax_forms.i9.fields;
switch (status) { switch (status) {
case 'CITIZEN': case 'CITIZEN':
return i18n.status_us_citizen; return i18n.status_us_citizen;
@@ -848,7 +925,9 @@ class _FormI9PageState extends State<FormI9Page> {
FormI9State state, FormI9State state,
List<Map<String, String>> steps, List<Map<String, String>> steps,
) { ) {
final TranslationsStaffComplianceTaxFormsI9En i18n = Translations.of(context).staff_compliance.tax_forms.i9; final TranslationsStaffComplianceTaxFormsI9En i18n = Translations.of(
context,
).staff_compliance.tax_forms.i9;
return Container( return Container(
padding: const EdgeInsets.all(UiConstants.space4), padding: const EdgeInsets.all(UiConstants.space4),
@@ -883,10 +962,7 @@ class _FormI9PageState extends State<FormI9Page> {
color: UiColors.textPrimary, color: UiColors.textPrimary,
), ),
const SizedBox(width: UiConstants.space2), const SizedBox(width: UiConstants.space2),
Text( Text(i18n.back, style: UiTypography.body2r.textPrimary),
i18n.back,
style: UiTypography.body2r.textPrimary,
),
], ],
), ),
), ),
@@ -895,8 +971,8 @@ class _FormI9PageState extends State<FormI9Page> {
Expanded( Expanded(
flex: 2, flex: 2,
child: ElevatedButton( child: ElevatedButton(
onPressed: ( onPressed:
_canProceed(state) && (_canProceed(state) &&
state.status != FormI9Status.submitting) state.status != FormI9Status.submitting)
? () => _handleNext(context, state.currentStep) ? () => _handleNext(context, state.currentStep)
: null, : null,
@@ -931,7 +1007,11 @@ class _FormI9PageState extends State<FormI9Page> {
), ),
if (state.currentStep < steps.length - 1) ...<Widget>[ if (state.currentStep < steps.length - 1) ...<Widget>[
const SizedBox(width: UiConstants.space2), const SizedBox(width: UiConstants.space2),
const Icon(UiIcons.arrowRight, size: 16, color: UiColors.white), const Icon(
UiIcons.arrowRight,
size: 16,
color: UiColors.white,
),
], ],
], ],
), ),

View File

@@ -2,8 +2,10 @@
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart' hide ModularWatchExtension; import 'package:flutter_modular/flutter_modular.dart'
hide ModularWatchExtension;
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart'; import 'package:krow_domain/krow_domain.dart';
import '../blocs/w4/form_w4_cubit.dart'; import '../blocs/w4/form_w4_cubit.dart';
@@ -84,7 +86,10 @@ class _FormW4PageState extends State<FormW4Page> {
<String, String>{'title': 'Filing Status', 'subtitle': 'Step 1c'}, <String, String>{'title': 'Filing Status', 'subtitle': 'Step 1c'},
<String, String>{'title': 'Multiple Jobs', 'subtitle': 'Step 2 (optional)'}, <String, String>{'title': 'Multiple Jobs', 'subtitle': 'Step 2 (optional)'},
<String, String>{'title': 'Dependents', 'subtitle': 'Step 3'}, <String, String>{'title': 'Dependents', 'subtitle': 'Step 3'},
<String, String>{'title': 'Other Adjustments', 'subtitle': 'Step 4 (optional)'}, <String, String>{
'title': 'Other Adjustments',
'subtitle': 'Step 4 (optional)',
},
<String, String>{'title': 'Review & Sign', 'subtitle': 'Step 5'}, <String, String>{'title': 'Review & Sign', 'subtitle': 'Step 5'},
]; ];
@@ -116,23 +121,41 @@ class _FormW4PageState extends State<FormW4Page> {
context.read<FormW4Cubit>().previousStep(); context.read<FormW4Cubit>().previousStep();
} }
int _totalCredits(FormW4State state) { int _totalCredits(FormW4State state) {
return (state.qualifyingChildren * 2000) + return (state.qualifyingChildren * 2000) + (state.otherDependents * 500);
(state.otherDependents * 500);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final TranslationsStaffComplianceTaxFormsW4En i18n = Translations.of(context).staff_compliance.tax_forms.w4; final TranslationsStaffComplianceTaxFormsW4En i18n = Translations.of(
context,
).staff_compliance.tax_forms.w4;
final List<Map<String, String>> steps = <Map<String, String>>[ final List<Map<String, String>> steps = <Map<String, String>>[
<String, String>{'title': i18n.steps.personal, 'subtitle': i18n.step_label(current: '1', total: '5')}, <String, String>{
<String, String>{'title': i18n.steps.filing, 'subtitle': i18n.step_label(current: '1c', total: '5')}, 'title': i18n.steps.personal,
<String, String>{'title': i18n.steps.multiple_jobs, 'subtitle': i18n.step_label(current: '2', total: '5')}, 'subtitle': i18n.step_label(current: '1', total: '5'),
<String, String>{'title': i18n.steps.dependents, 'subtitle': i18n.step_label(current: '3', total: '5')}, },
<String, String>{'title': i18n.steps.adjustments, 'subtitle': i18n.step_label(current: '4', total: '5')}, <String, String>{
<String, String>{'title': i18n.steps.review, 'subtitle': i18n.step_label(current: '5', total: '5')}, 'title': i18n.steps.filing,
'subtitle': i18n.step_label(current: '1c', total: '5'),
},
<String, String>{
'title': i18n.steps.multiple_jobs,
'subtitle': i18n.step_label(current: '2', total: '5'),
},
<String, String>{
'title': i18n.steps.dependents,
'subtitle': i18n.step_label(current: '3', total: '5'),
},
<String, String>{
'title': i18n.steps.adjustments,
'subtitle': i18n.step_label(current: '4', total: '5'),
},
<String, String>{
'title': i18n.steps.review,
'subtitle': i18n.step_label(current: '5', total: '5'),
},
]; ];
return BlocProvider<FormW4Cubit>.value( return BlocProvider<FormW4Cubit>.value(
@@ -144,7 +167,9 @@ class _FormW4PageState extends State<FormW4Page> {
} else if (state.status == FormW4Status.failure) { } else if (state.status == FormW4Status.failure) {
UiSnackbar.show( UiSnackbar.show(
context, context,
message: translateErrorKey(state.errorMessage ?? 'An error occurred'), message: translateErrorKey(
state.errorMessage ?? 'An error occurred',
),
type: UiSnackbarType.error, type: UiSnackbarType.error,
margin: const EdgeInsets.only( margin: const EdgeInsets.only(
left: UiConstants.space4, left: UiConstants.space4,
@@ -155,7 +180,8 @@ class _FormW4PageState extends State<FormW4Page> {
} }
}, },
builder: (BuildContext context, FormW4State state) { builder: (BuildContext context, FormW4State state) {
if (state.status == FormW4Status.success) return _buildSuccessView(i18n); if (state.status == FormW4Status.success)
return _buildSuccessView(i18n);
return Scaffold( return Scaffold(
backgroundColor: UiColors.background, backgroundColor: UiColors.background,
@@ -224,7 +250,7 @@ class _FormW4PageState extends State<FormW4Page> {
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
child: ElevatedButton( child: ElevatedButton(
onPressed: () => Modular.to.pop(true), onPressed: () => Modular.to.popSafe(true),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: UiColors.primary, backgroundColor: UiColors.primary,
foregroundColor: UiColors.white, foregroundColor: UiColors.white,
@@ -267,7 +293,7 @@ class _FormW4PageState extends State<FormW4Page> {
Row( Row(
children: <Widget>[ children: <Widget>[
GestureDetector( GestureDetector(
onTap: () => Modular.to.pop(), onTap: () => Modular.to.popSafe(),
child: const Icon( child: const Icon(
UiIcons.arrowLeft, UiIcons.arrowLeft,
color: UiColors.white, color: UiColors.white,
@@ -278,10 +304,7 @@ class _FormW4PageState extends State<FormW4Page> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(i18n.title, style: UiTypography.headline4m.white),
i18n.title,
style: UiTypography.headline4m.white,
),
Text( Text(
i18n.subtitle, i18n.subtitle,
style: UiTypography.body3r.copyWith( style: UiTypography.body3r.copyWith(
@@ -294,10 +317,9 @@ class _FormW4PageState extends State<FormW4Page> {
), ),
const SizedBox(height: UiConstants.space6), const SizedBox(height: UiConstants.space6),
Row( Row(
children: steps children: steps.asMap().entries.map((
.asMap() MapEntry<int, Map<String, String>> entry,
.entries ) {
.map((MapEntry<int, Map<String, String>> entry) {
final int idx = entry.key; final int idx = entry.key;
final bool isLast = idx == steps.length - 1; final bool isLast = idx == steps.length - 1;
return Expanded( return Expanded(
@@ -434,7 +456,8 @@ class _FormW4PageState extends State<FormW4Page> {
child: _buildTextField( child: _buildTextField(
i18n.fields.first_name, i18n.fields.first_name,
value: state.firstName, value: state.firstName,
onChanged: (String val) => context.read<FormW4Cubit>().firstNameChanged(val), onChanged: (String val) =>
context.read<FormW4Cubit>().firstNameChanged(val),
placeholder: i18n.fields.placeholder_john, placeholder: i18n.fields.placeholder_john,
), ),
), ),
@@ -443,7 +466,8 @@ class _FormW4PageState extends State<FormW4Page> {
child: _buildTextField( child: _buildTextField(
i18n.fields.last_name, i18n.fields.last_name,
value: state.lastName, value: state.lastName,
onChanged: (String val) => context.read<FormW4Cubit>().lastNameChanged(val), onChanged: (String val) =>
context.read<FormW4Cubit>().lastNameChanged(val),
placeholder: i18n.fields.placeholder_smith, placeholder: i18n.fields.placeholder_smith,
), ),
), ),
@@ -465,14 +489,16 @@ class _FormW4PageState extends State<FormW4Page> {
_buildTextField( _buildTextField(
i18n.fields.address, i18n.fields.address,
value: state.address, value: state.address,
onChanged: (String val) => context.read<FormW4Cubit>().addressChanged(val), onChanged: (String val) =>
context.read<FormW4Cubit>().addressChanged(val),
placeholder: i18n.fields.placeholder_address, placeholder: i18n.fields.placeholder_address,
), ),
const SizedBox(height: UiConstants.space4), const SizedBox(height: UiConstants.space4),
_buildTextField( _buildTextField(
i18n.fields.city_state_zip, i18n.fields.city_state_zip,
value: state.cityStateZip, value: state.cityStateZip,
onChanged: (String val) => context.read<FormW4Cubit>().cityStateZipChanged(val), onChanged: (String val) =>
context.read<FormW4Cubit>().cityStateZipChanged(val),
placeholder: i18n.fields.placeholder_csz, placeholder: i18n.fields.placeholder_csz,
), ),
], ],
@@ -506,21 +532,9 @@ class _FormW4PageState extends State<FormW4Page> {
), ),
), ),
const SizedBox(height: UiConstants.space6), const SizedBox(height: UiConstants.space6),
_buildRadioOption( _buildRadioOption(context, state, 'SINGLE', i18n.fields.single, null),
context,
state,
'SINGLE',
i18n.fields.single,
null,
),
const SizedBox(height: UiConstants.space3), const SizedBox(height: UiConstants.space3),
_buildRadioOption( _buildRadioOption(context, state, 'MARRIED', i18n.fields.married, null),
context,
state,
'MARRIED',
i18n.fields.married,
null,
),
const SizedBox(height: UiConstants.space3), const SizedBox(height: UiConstants.space3),
_buildRadioOption( _buildRadioOption(
context, context,
@@ -573,16 +587,10 @@ class _FormW4PageState extends State<FormW4Page> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(label, style: UiTypography.body2m.textPrimary),
label,
style: UiTypography.body2m.textPrimary,
),
if (subLabel != null) ...<Widget>[ if (subLabel != null) ...<Widget>[
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(subLabel, style: UiTypography.body3r.textSecondary),
subLabel,
style: UiTypography.body3r.textSecondary,
),
], ],
], ],
), ),
@@ -609,11 +617,7 @@ class _FormW4PageState extends State<FormW4Page> {
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
const Icon( const Icon(UiIcons.help, color: UiColors.accent, size: 20),
UiIcons.help,
color: UiColors.accent,
size: 20,
),
const SizedBox(width: UiConstants.space3), const SizedBox(width: UiConstants.space3),
Expanded( Expanded(
child: Column( child: Column(
@@ -636,8 +640,9 @@ class _FormW4PageState extends State<FormW4Page> {
), ),
const SizedBox(height: UiConstants.space6), const SizedBox(height: UiConstants.space6),
GestureDetector( GestureDetector(
onTap: () => onTap: () => context.read<FormW4Cubit>().multipleJobsChanged(
context.read<FormW4Cubit>().multipleJobsChanged(!state.multipleJobs), !state.multipleJobs,
),
child: Container( child: Container(
padding: const EdgeInsets.all(UiConstants.space4), padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -654,10 +659,14 @@ class _FormW4PageState extends State<FormW4Page> {
width: 24, width: 24,
height: 24, height: 24,
decoration: BoxDecoration( decoration: BoxDecoration(
color: state.multipleJobs ? UiColors.primary : UiColors.bgPopup, color: state.multipleJobs
? UiColors.primary
: UiColors.bgPopup,
borderRadius: UiConstants.radiusMd, borderRadius: UiConstants.radiusMd,
border: Border.all( border: Border.all(
color: state.multipleJobs ? UiColors.primary : UiColors.border, color: state.multipleJobs
? UiColors.primary
: UiColors.border,
), ),
), ),
child: state.multipleJobs child: state.multipleJobs
@@ -741,7 +750,8 @@ class _FormW4PageState extends State<FormW4Page> {
i18n.fields.children_under_17, i18n.fields.children_under_17,
i18n.fields.children_each, i18n.fields.children_each,
(FormW4State s) => s.qualifyingChildren, (FormW4State s) => s.qualifyingChildren,
(int val) => context.read<FormW4Cubit>().qualifyingChildrenChanged(val), (int val) =>
context.read<FormW4Cubit>().qualifyingChildrenChanged(val),
), ),
const Padding( const Padding(
padding: EdgeInsets.symmetric(vertical: 16), padding: EdgeInsets.symmetric(vertical: 16),
@@ -753,7 +763,8 @@ class _FormW4PageState extends State<FormW4Page> {
i18n.fields.other_dependents, i18n.fields.other_dependents,
i18n.fields.other_each, i18n.fields.other_each,
(FormW4State s) => s.otherDependents, (FormW4State s) => s.otherDependents,
(int val) => context.read<FormW4Cubit>().otherDependentsChanged(val), (int val) =>
context.read<FormW4Cubit>().otherDependentsChanged(val),
), ),
], ],
), ),
@@ -775,9 +786,7 @@ class _FormW4PageState extends State<FormW4Page> {
), ),
Text( Text(
'\$${_totalCredits(state)}', '\$${_totalCredits(state)}',
style: UiTypography.body2b.textSuccess.copyWith( style: UiTypography.body2b.textSuccess.copyWith(fontSize: 18),
fontSize: 18,
),
), ),
], ],
), ),
@@ -802,22 +811,14 @@ class _FormW4PageState extends State<FormW4Page> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(child: Text(label, style: UiTypography.body2m)),
child: Text(
label,
style: UiTypography.body2m,
),
),
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: UiColors.tagSuccess, color: UiColors.tagSuccess,
borderRadius: UiConstants.radiusLg, borderRadius: UiConstants.radiusLg,
), ),
child: Text( child: Text(badge, style: UiTypography.footnote2b.textSuccess),
badge,
style: UiTypography.footnote2b.textSuccess,
),
), ),
], ],
), ),
@@ -839,10 +840,7 @@ class _FormW4PageState extends State<FormW4Page> {
), ),
), ),
), ),
_buildCircleBtn( _buildCircleBtn(UiIcons.add, () => onChanged(value + 1)),
UiIcons.add,
() => onChanged(value + 1),
),
], ],
), ),
], ],
@@ -881,7 +879,8 @@ class _FormW4PageState extends State<FormW4Page> {
_buildTextField( _buildTextField(
i18n.fields.other_income, i18n.fields.other_income,
value: state.otherIncome, value: state.otherIncome,
onChanged: (String val) => context.read<FormW4Cubit>().otherIncomeChanged(val), onChanged: (String val) =>
context.read<FormW4Cubit>().otherIncomeChanged(val),
placeholder: i18n.fields.hints.zero, placeholder: i18n.fields.hints.zero,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
), ),
@@ -896,7 +895,8 @@ class _FormW4PageState extends State<FormW4Page> {
_buildTextField( _buildTextField(
i18n.fields.deductions, i18n.fields.deductions,
value: state.deductions, value: state.deductions,
onChanged: (String val) => context.read<FormW4Cubit>().deductionsChanged(val), onChanged: (String val) =>
context.read<FormW4Cubit>().deductionsChanged(val),
placeholder: i18n.fields.hints.zero, placeholder: i18n.fields.hints.zero,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
), ),
@@ -911,7 +911,8 @@ class _FormW4PageState extends State<FormW4Page> {
_buildTextField( _buildTextField(
i18n.fields.extra_withholding, i18n.fields.extra_withholding,
value: state.extraWithholding, value: state.extraWithholding,
onChanged: (String val) => context.read<FormW4Cubit>().extraWithholdingChanged(val), onChanged: (String val) =>
context.read<FormW4Cubit>().extraWithholdingChanged(val),
placeholder: i18n.fields.hints.zero, placeholder: i18n.fields.hints.zero,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
), ),
@@ -1019,10 +1020,7 @@ class _FormW4PageState extends State<FormW4Page> {
style: const TextStyle(fontFamily: 'Cursive', fontSize: 18), style: const TextStyle(fontFamily: 'Cursive', fontSize: 18),
), ),
const SizedBox(height: UiConstants.space4), const SizedBox(height: UiConstants.space4),
Text( Text(i18n.fields.date_label, style: UiTypography.body3m.textSecondary),
i18n.fields.date_label,
style: UiTypography.body3m.textSecondary,
),
const SizedBox(height: 6), const SizedBox(height: 6),
Container( Container(
width: double.infinity, width: double.infinity,
@@ -1050,10 +1048,7 @@ class _FormW4PageState extends State<FormW4Page> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Text( Text(label, style: UiTypography.body2r.textSecondary),
label,
style: UiTypography.body2r.textSecondary,
),
Text( Text(
value, value,
style: UiTypography.body2m.copyWith( style: UiTypography.body2m.copyWith(
@@ -1066,7 +1061,9 @@ class _FormW4PageState extends State<FormW4Page> {
} }
String _getFilingStatusLabel(String status) { String _getFilingStatusLabel(String status) {
final TranslationsStaffComplianceTaxFormsW4FieldsEn i18n = Translations.of(context).staff_compliance.tax_forms.w4.fields; final TranslationsStaffComplianceTaxFormsW4FieldsEn i18n = Translations.of(
context,
).staff_compliance.tax_forms.w4.fields;
switch (status) { switch (status) {
case 'SINGLE': case 'SINGLE':
return i18n.status_single; return i18n.status_single;
@@ -1084,7 +1081,9 @@ class _FormW4PageState extends State<FormW4Page> {
FormW4State state, FormW4State state,
List<Map<String, String>> steps, List<Map<String, String>> steps,
) { ) {
final TranslationsStaffComplianceTaxFormsW4En i18n = Translations.of(context).staff_compliance.tax_forms.w4; final TranslationsStaffComplianceTaxFormsW4En i18n = Translations.of(
context,
).staff_compliance.tax_forms.w4;
return Container( return Container(
padding: const EdgeInsets.all(UiConstants.space4), padding: const EdgeInsets.all(UiConstants.space4),
@@ -1131,8 +1130,8 @@ class _FormW4PageState extends State<FormW4Page> {
Expanded( Expanded(
flex: 2, flex: 2,
child: ElevatedButton( child: ElevatedButton(
onPressed: ( onPressed:
_canProceed(state) && (_canProceed(state) &&
state.status != FormW4Status.submitting) state.status != FormW4Status.submitting)
? () => _handleNext(context, state.currentStep) ? () => _handleNext(context, state.currentStep)
: null, : null,
@@ -1167,7 +1166,11 @@ class _FormW4PageState extends State<FormW4Page> {
), ),
if (state.currentStep < steps.length - 1) ...<Widget>[ if (state.currentStep < steps.length - 1) ...<Widget>[
const SizedBox(width: UiConstants.space2), const SizedBox(width: UiConstants.space2),
const Icon(UiIcons.arrowRight, size: 16, color: UiColors.white), const Icon(
UiIcons.arrowRight,
size: 16,
color: UiColors.white,
),
], ],
], ],
), ),
@@ -1179,5 +1182,3 @@ class _FormW4PageState extends State<FormW4Page> {
); );
} }
} }

View File

@@ -3,6 +3,7 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart'; import 'package:krow_domain/krow_domain.dart';
import '../blocs/tax_forms/tax_forms_cubit.dart'; import '../blocs/tax_forms/tax_forms_cubit.dart';
import '../blocs/tax_forms/tax_forms_state.dart'; import '../blocs/tax_forms/tax_forms_state.dart';
@@ -18,7 +19,7 @@ class TaxFormsPage extends StatelessWidget {
elevation: 0, elevation: 0,
leading: IconButton( leading: IconButton(
icon: const Icon(UiIcons.arrowLeft, color: UiColors.bgPopup), icon: const Icon(UiIcons.arrowLeft, color: UiColors.bgPopup),
onPressed: () => Modular.to.pop(), onPressed: () => Modular.to.popSafe(),
), ),
title: Text( title: Text(
'Tax Documents', 'Tax Documents',
@@ -64,7 +65,9 @@ class TaxFormsPage extends StatelessWidget {
if (state.status == TaxFormsStatus.failure) { if (state.status == TaxFormsStatus.failure) {
return Center( return Center(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space5), padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space5,
),
child: Text( child: Text(
state.errorMessage != null state.errorMessage != null
? translateErrorKey(state.errorMessage!) ? translateErrorKey(state.errorMessage!)
@@ -84,7 +87,9 @@ class TaxFormsPage extends StatelessWidget {
spacing: UiConstants.space6, spacing: UiConstants.space6,
children: <Widget>[ children: <Widget>[
_buildProgressOverview(state.forms), _buildProgressOverview(state.forms),
...state.forms.map((TaxForm form) => _buildFormCard(context, form)), ...state.forms.map(
(TaxForm form) => _buildFormCard(context, form),
),
_buildInfoCard(), _buildInfoCard(),
], ],
), ),
@@ -118,10 +123,7 @@ class TaxFormsPage extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Text( Text('Document Progress', style: UiTypography.body2m.textPrimary),
'Document Progress',
style: UiTypography.body2m.textPrimary,
),
Text( Text(
'$completedCount/$totalCount', '$completedCount/$totalCount',
style: UiTypography.body2m.textSecondary, style: UiTypography.body2m.textSecondary,
@@ -150,12 +152,18 @@ class TaxFormsPage extends StatelessWidget {
return GestureDetector( return GestureDetector(
onTap: () async { onTap: () async {
if (form is I9TaxForm) { if (form is I9TaxForm) {
final Object? result = await Modular.to.pushNamed('i9', arguments: form); final Object? result = await Modular.to.pushNamed(
'i9',
arguments: form,
);
if (result == true && context.mounted) { if (result == true && context.mounted) {
await BlocProvider.of<TaxFormsCubit>(context).loadTaxForms(); await BlocProvider.of<TaxFormsCubit>(context).loadTaxForms();
} }
} else if (form is W4TaxForm) { } else if (form is W4TaxForm) {
final Object? result = await Modular.to.pushNamed('w4', arguments: form); final Object? result = await Modular.to.pushNamed(
'w4',
arguments: form,
);
if (result == true && context.mounted) { if (result == true && context.mounted) {
await BlocProvider.of<TaxFormsCubit>(context).loadTaxForms(); await BlocProvider.of<TaxFormsCubit>(context).loadTaxForms();
} }
@@ -245,10 +253,7 @@ class TaxFormsPage extends StatelessWidget {
color: UiColors.textSuccess, color: UiColors.textSuccess,
), ),
const SizedBox(width: UiConstants.space1), const SizedBox(width: UiConstants.space1),
Text( Text('Completed', style: UiTypography.footnote2b.textSuccess),
'Completed',
style: UiTypography.footnote2b.textSuccess,
),
], ],
), ),
); );
@@ -267,10 +272,7 @@ class TaxFormsPage extends StatelessWidget {
children: <Widget>[ children: <Widget>[
const Icon(UiIcons.clock, size: 12, color: UiColors.textWarning), const Icon(UiIcons.clock, size: 12, color: UiColors.textWarning),
const SizedBox(width: UiConstants.space1), const SizedBox(width: UiConstants.space1),
Text( Text('In Progress', style: UiTypography.footnote2b.textWarning),
'In Progress',
style: UiTypography.footnote2b.textWarning,
),
], ],
), ),
); );

View File

@@ -4,7 +4,7 @@ import 'package:flutter_modular/flutter_modular.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
import 'package:krow_domain/krow_domain.dart'; import 'package:krow_domain/krow_domain.dart';
// ignore: depend_on_referenced_packages import 'package:krow_core/core.dart';
import '../blocs/bank_account_cubit.dart'; import '../blocs/bank_account_cubit.dart';
import '../blocs/bank_account_state.dart'; import '../blocs/bank_account_state.dart';
@@ -32,12 +32,9 @@ class BankAccountPage extends StatelessWidget {
elevation: 0, elevation: 0,
leading: IconButton( leading: IconButton(
icon: const Icon(UiIcons.arrowLeft, color: UiColors.textSecondary), icon: const Icon(UiIcons.arrowLeft, color: UiColors.textSecondary),
onPressed: () => Modular.to.pop(), onPressed: () => Modular.to.popSafe(),
),
title: Text(
strings.title,
style: UiTypography.headline3m.textPrimary,
), ),
title: Text(strings.title, style: UiTypography.headline3m.textPrimary),
bottom: PreferredSize( bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0), preferredSize: const Size.fromHeight(1.0),
child: Container(color: UiColors.border, height: 1.0), child: Container(color: UiColors.border, height: 1.0),
@@ -61,7 +58,8 @@ class BankAccountPage extends StatelessWidget {
// Error is already shown on the page itself (lines 73-85), no need for snackbar // Error is already shown on the page itself (lines 73-85), no need for snackbar
}, },
builder: (BuildContext context, BankAccountState state) { builder: (BuildContext context, BankAccountState state) {
if (state.status == BankAccountStatus.loading && state.accounts.isEmpty) { if (state.status == BankAccountStatus.loading &&
state.accounts.isEmpty) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }
@@ -74,7 +72,9 @@ class BankAccountPage extends StatelessWidget {
? translateErrorKey(state.errorMessage!) ? translateErrorKey(state.errorMessage!)
: 'Error', : 'Error',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: UiTypography.body1m.copyWith(color: UiColors.textSecondary), style: UiTypography.body1m.copyWith(
color: UiColors.textSecondary,
),
), ),
), ),
); );
@@ -92,11 +92,14 @@ class BankAccountPage extends StatelessWidget {
const SizedBox(height: UiConstants.space6), const SizedBox(height: UiConstants.space6),
Text( Text(
strings.linked_accounts, strings.linked_accounts,
style: UiTypography.headline4m.copyWith(color: UiColors.textPrimary), style: UiTypography.headline4m.copyWith(
color: UiColors.textPrimary,
),
), ),
const SizedBox(height: UiConstants.space3), const SizedBox(height: UiConstants.space3),
...state.accounts.map((StaffBankAccount a) => _buildAccountCard(a, strings)), // Added type ...state.accounts.map(
(StaffBankAccount a) => _buildAccountCard(a, strings),
), // Added type
// Add extra padding at bottom // Add extra padding at bottom
const SizedBox(height: UiConstants.space20), const SizedBox(height: UiConstants.space20),
], ],
@@ -121,17 +124,23 @@ class BankAccountPage extends StatelessWidget {
backgroundColor: UiColors.transparent, backgroundColor: UiColors.transparent,
child: AddAccountForm( child: AddAccountForm(
strings: strings, strings: strings,
onSubmit: (String bankName, String routing, String account, String type) { onSubmit:
(
String bankName,
String routing,
String account,
String type,
) {
cubit.addAccount( cubit.addAccount(
bankName: bankName, bankName: bankName,
routingNumber: routing, routingNumber: routing,
accountNumber: account, accountNumber: account,
type: type, type: type,
); );
Modular.to.pop(); Modular.to.popSafe();
}, },
onCancel: () { onCancel: () {
Modular.to.pop(); Modular.to.popSafe();
}, },
), ),
); );
@@ -249,12 +258,13 @@ class BankAccountPage extends StatelessWidget {
), ),
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
const Icon(UiIcons.check, size: UiConstants.iconXs, color: primaryColor), const Icon(
const SizedBox(width: UiConstants.space1), UiIcons.check,
Text( size: UiConstants.iconXs,
strings.primary, color: primaryColor,
style: UiTypography.body3m.primary,
), ),
const SizedBox(width: UiConstants.space1),
Text(strings.primary, style: UiTypography.body3m.primary),
], ],
), ),
), ),

View File

@@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:krow_core/core.dart';
import '../blocs/time_card_bloc.dart'; import '../blocs/time_card_bloc.dart';
import '../widgets/month_selector.dart'; import '../widgets/month_selector.dart';
import '../widgets/shift_history_list.dart'; import '../widgets/shift_history_list.dart';
@@ -37,8 +38,11 @@ class _TimeCardPageState extends State<TimeCardPage> {
backgroundColor: UiColors.bgPopup, backgroundColor: UiColors.bgPopup,
elevation: 0, elevation: 0,
leading: IconButton( leading: IconButton(
icon: const Icon(UiIcons.chevronLeft, color: UiColors.iconSecondary), icon: const Icon(
onPressed: () => Modular.to.pop(), UiIcons.chevronLeft,
color: UiColors.iconSecondary,
),
onPressed: () => Modular.to.popSafe(),
), ),
title: Text( title: Text(
t.staff_time_card.title, t.staff_time_card.title,
@@ -69,7 +73,9 @@ class _TimeCardPageState extends State<TimeCardPage> {
child: Text( child: Text(
translateErrorKey(state.message), translateErrorKey(state.message),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: UiTypography.body1m.copyWith(color: UiColors.textSecondary), style: UiTypography.body1m.copyWith(
color: UiColors.textSecondary,
),
), ),
), ),
); );
@@ -83,12 +89,22 @@ class _TimeCardPageState extends State<TimeCardPage> {
children: <Widget>[ children: <Widget>[
MonthSelector( MonthSelector(
selectedDate: state.selectedMonth, selectedDate: state.selectedMonth,
onPreviousMonth: () => _bloc.add(ChangeMonth( onPreviousMonth: () => _bloc.add(
DateTime(state.selectedMonth.year, state.selectedMonth.month - 1), ChangeMonth(
)), DateTime(
onNextMonth: () => _bloc.add(ChangeMonth( state.selectedMonth.year,
DateTime(state.selectedMonth.year, state.selectedMonth.month + 1), state.selectedMonth.month - 1,
)), ),
),
),
onNextMonth: () => _bloc.add(
ChangeMonth(
DateTime(
state.selectedMonth.year,
state.selectedMonth.month + 1,
),
),
),
), ),
const SizedBox(height: UiConstants.space6), const SizedBox(height: UiConstants.space6),
TimeCardSummary( TimeCardSummary(
@@ -108,4 +124,3 @@ class _TimeCardPageState extends State<TimeCardPage> {
); );
} }
} }

View File

@@ -135,7 +135,7 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
leading: const Icon(Icons.photo_library), leading: const Icon(Icons.photo_library),
title: Text(t.common.gallery), title: Text(t.common.gallery),
onTap: () { onTap: () {
Modular.to.pop(); Modular.to.popSafe();
_onGallery(context); _onGallery(context);
}, },
), ),
@@ -143,7 +143,7 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
leading: const Icon(Icons.camera_alt), leading: const Icon(Icons.camera_alt),
title: Text(t.common.camera), title: Text(t.common.camera),
onTap: () { onTap: () {
Modular.to.pop(); Modular.to.popSafe();
_onCamera(context); _onCamera(context);
}, },
), ),
@@ -215,10 +215,16 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
String _getStatusText(bool hasUploadedPhoto) { String _getStatusText(bool hasUploadedPhoto) {
return switch (widget.item.verificationStatus) { return switch (widget.item.verificationStatus) {
AttireVerificationStatus.approved => t.staff_profile_attire.capture.approved, AttireVerificationStatus.approved =>
AttireVerificationStatus.rejected => t.staff_profile_attire.capture.rejected, t.staff_profile_attire.capture.approved,
AttireVerificationStatus.pending => t.staff_profile_attire.capture.pending_verification, AttireVerificationStatus.rejected =>
_ => hasUploadedPhoto ? t.staff_profile_attire.capture.pending_verification : t.staff_profile_attire.capture.not_uploaded, t.staff_profile_attire.capture.rejected,
AttireVerificationStatus.pending =>
t.staff_profile_attire.capture.pending_verification,
_ =>
hasUploadedPhoto
? t.staff_profile_attire.capture.pending_verification
: t.staff_profile_attire.capture.not_uploaded,
}; };
} }
@@ -281,7 +287,9 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
_FileTypesBanner( _FileTypesBanner(
message: t.staff_profile_attire.upload_file_types_banner, message: t
.staff_profile_attire
.upload_file_types_banner,
), ),
const SizedBox(height: UiConstants.space4), const SizedBox(height: UiConstants.space4),
ImagePreviewSection( ImagePreviewSection(
@@ -350,17 +358,10 @@ class _FileTypesBanner extends StatelessWidget {
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Icon( Icon(UiIcons.info, size: 20, color: UiColors.primary),
UiIcons.info,
size: 20,
color: UiColors.primary,
),
const SizedBox(width: UiConstants.space3), const SizedBox(width: UiConstants.space3),
Expanded( Expanded(
child: Text( child: Text(message, style: UiTypography.body2r.textSecondary),
message,
style: UiTypography.body2r.textSecondary,
),
), ),
], ],
), ),

View File

@@ -2,6 +2,7 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_domain/krow_domain.dart'; import 'package:krow_domain/krow_domain.dart';
import 'package:krow_core/core.dart';
import 'attire_upload_buttons.dart'; import 'attire_upload_buttons.dart';
@@ -98,7 +99,7 @@ class FooterSection extends StatelessWidget {
text: 'Submit Image', text: 'Submit Image',
onPressed: () { onPressed: () {
if (updatedItem != null) { if (updatedItem != null) {
Modular.to.pop(updatedItem); Modular.to.popSafe(updatedItem);
} }
}, },
), ),

View File

@@ -3,6 +3,7 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
import '../blocs/emergency_contact_bloc.dart'; import '../blocs/emergency_contact_bloc.dart';
import '../widgets/emergency_contact_add_button.dart'; import '../widgets/emergency_contact_add_button.dart';
import '../widgets/emergency_contact_form_item.dart'; import '../widgets/emergency_contact_form_item.dart';
@@ -25,7 +26,7 @@ class EmergencyContactScreen extends StatelessWidget {
elevation: 0, elevation: 0,
leading: IconButton( leading: IconButton(
icon: const Icon(UiIcons.chevronLeft, color: UiColors.textSecondary), icon: const Icon(UiIcons.chevronLeft, color: UiColors.textSecondary),
onPressed: () => Modular.to.pop(), onPressed: () => Modular.to.popSafe(),
), ),
title: Text( title: Text(
'Emergency Contact', 'Emergency Contact',

View File

@@ -3,6 +3,7 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
/// Language selection page for staff profile. /// Language selection page for staff profile.
/// ///
@@ -30,7 +31,7 @@ class LanguageSelectionPage extends StatelessWidget {
); );
Modular.to Modular.to
.pop(); // Close the language selection page after showing the snackbar .popSafe(); // Close the language selection page after showing the snackbar
} }
@override @override

View File

@@ -3,12 +3,12 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
import '../blocs/personal_info_bloc.dart'; import '../blocs/personal_info_bloc.dart';
import '../blocs/personal_info_state.dart'; import '../blocs/personal_info_state.dart';
import '../widgets/personal_info_content.dart'; import '../widgets/personal_info_content.dart';
/// The Personal Info page for staff onboarding. /// The Personal Info page for staff onboarding.
/// ///
/// This page allows staff members to view and edit their personal information /// This page allows staff members to view and edit their personal information
@@ -22,7 +22,9 @@ class PersonalInfoPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final TranslationsStaffOnboardingPersonalInfoEn i18n = Translations.of(context).staff.onboarding.personal_info; final TranslationsStaffOnboardingPersonalInfoEn i18n = Translations.of(
context,
).staff.onboarding.personal_info;
return BlocProvider<PersonalInfoBloc>( return BlocProvider<PersonalInfoBloc>(
create: (BuildContext context) => Modular.get<PersonalInfoBloc>(), create: (BuildContext context) => Modular.get<PersonalInfoBloc>(),
child: BlocListener<PersonalInfoBloc, PersonalInfoState>( child: BlocListener<PersonalInfoBloc, PersonalInfoState>(
@@ -33,7 +35,7 @@ class PersonalInfoPage extends StatelessWidget {
message: i18n.save_success, message: i18n.save_success,
type: UiSnackbarType.success, type: UiSnackbarType.success,
); );
Modular.to.pop(); Modular.to.popSafe();
} else if (state.status == PersonalInfoStatus.error) { } else if (state.status == PersonalInfoStatus.error) {
UiSnackbar.show( UiSnackbar.show(
context, context,
@@ -54,19 +56,13 @@ class PersonalInfoPage extends StatelessWidget {
UiIcons.chevronLeft, UiIcons.chevronLeft,
color: UiColors.textSecondary, color: UiColors.textSecondary,
), ),
onPressed: () => Modular.to.pop(), onPressed: () => Modular.to.popSafe(),
tooltip: MaterialLocalizations.of(context).backButtonTooltip, tooltip: MaterialLocalizations.of(context).backButtonTooltip,
), ),
title: Text( title: Text(i18n.title, style: UiTypography.title1m.textPrimary),
i18n.title,
style: UiTypography.title1m.textPrimary,
),
bottom: PreferredSize( bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0), preferredSize: const Size.fromHeight(1.0),
child: Container( child: Container(color: UiColors.border, height: 1.0),
color: UiColors.border,
height: 1.0,
),
), ),
), ),
body: SafeArea( body: SafeArea(
@@ -74,9 +70,7 @@ class PersonalInfoPage extends StatelessWidget {
builder: (BuildContext context, PersonalInfoState state) { builder: (BuildContext context, PersonalInfoState state) {
if (state.status == PersonalInfoStatus.loading || if (state.status == PersonalInfoStatus.loading ||
state.status == PersonalInfoStatus.initial) { state.status == PersonalInfoStatus.initial) {
return const Center( return const Center(child: CircularProgressIndicator());
child: CircularProgressIndicator(),
);
} }
if (state.staff == null) { if (state.staff == null) {

View File

@@ -11,7 +11,6 @@ import 'package:krow_core/core.dart';
/// The Preferred Locations row navigates to a dedicated Uber-style page. /// The Preferred Locations row navigates to a dedicated Uber-style page.
/// Uses only design system tokens for colors, typography, and spacing. /// Uses only design system tokens for colors, typography, and spacing.
class PersonalInfoForm extends StatelessWidget { class PersonalInfoForm extends StatelessWidget {
/// Creates a [PersonalInfoForm]. /// Creates a [PersonalInfoForm].
const PersonalInfoForm({ const PersonalInfoForm({
super.key, super.key,
@@ -22,6 +21,7 @@ class PersonalInfoForm extends StatelessWidget {
required this.currentLocations, required this.currentLocations,
this.enabled = true, this.enabled = true,
}); });
/// The staff member's full name (read-only). /// The staff member's full name (read-only).
final String fullName; final String fullName;
@@ -42,7 +42,8 @@ class PersonalInfoForm extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final TranslationsStaffOnboardingPersonalInfoEn i18n = t.staff.onboarding.personal_info; final TranslationsStaffOnboardingPersonalInfoEn i18n =
t.staff.onboarding.personal_info;
final String locationSummary = currentLocations.isEmpty final String locationSummary = currentLocations.isEmpty
? i18n.locations_summary_none ? i18n.locations_summary_none
: currentLocations.join(', '); : currentLocations.join(', ');
@@ -84,17 +85,13 @@ class PersonalInfoForm extends StatelessWidget {
hint: i18n.locations_hint, hint: i18n.locations_hint,
icon: UiIcons.mapPin, icon: UiIcons.mapPin,
enabled: enabled, enabled: enabled,
onTap: enabled onTap: enabled ? () => Modular.to.toPreferredLocations() : null,
? () => Modular.to.pushNamed(StaffPaths.preferredLocations)
: null,
), ),
const SizedBox(height: UiConstants.space4), const SizedBox(height: UiConstants.space4),
const _FieldLabel(text: 'Language'), const _FieldLabel(text: 'Language'),
const SizedBox(height: UiConstants.space2), const SizedBox(height: UiConstants.space2),
_LanguageSelector( _LanguageSelector(enabled: enabled),
enabled: enabled,
),
], ],
); );
} }
@@ -132,7 +129,9 @@ class _TappableRow extends StatelessWidget {
color: enabled ? UiColors.bgPopup : UiColors.bgSecondary, color: enabled ? UiColors.bgPopup : UiColors.bgSecondary,
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue), borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
border: Border.all( border: Border.all(
color: enabled ? UiColors.border : UiColors.border.withValues(alpha: 0.5), color: enabled
? UiColors.border
: UiColors.border.withValues(alpha: 0.5),
), ),
), ),
child: Row( child: Row(
@@ -164,9 +163,7 @@ class _TappableRow extends StatelessWidget {
/// A language selector widget that displays the current language and navigates to language selection page. /// A language selector widget that displays the current language and navigates to language selection page.
class _LanguageSelector extends StatelessWidget { class _LanguageSelector extends StatelessWidget {
const _LanguageSelector({ const _LanguageSelector({this.enabled = true});
this.enabled = true,
});
final bool enabled; final bool enabled;
@@ -176,9 +173,7 @@ class _LanguageSelector extends StatelessWidget {
final String languageName = currentLocale == 'es' ? 'Español' : 'English'; final String languageName = currentLocale == 'es' ? 'Español' : 'English';
return GestureDetector( return GestureDetector(
onTap: enabled onTap: enabled ? () => Modular.to.toLanguageSelection() : null,
? () => Modular.to.pushNamed(StaffPaths.languageSelection)
: null,
child: Container( child: Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space3, horizontal: UiConstants.space3,
@@ -188,18 +183,21 @@ class _LanguageSelector extends StatelessWidget {
color: enabled ? UiColors.bgPopup : UiColors.bgSecondary, color: enabled ? UiColors.bgPopup : UiColors.bgSecondary,
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue), borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
border: Border.all( border: Border.all(
color: enabled ? UiColors.border : UiColors.border.withValues(alpha: 0.5), color: enabled
? UiColors.border
: UiColors.border.withValues(alpha: 0.5),
), ),
), ),
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
const Icon(UiIcons.settings, size: 18, color: UiColors.iconSecondary), const Icon(
UiIcons.settings,
size: 18,
color: UiColors.iconSecondary,
),
const SizedBox(width: UiConstants.space3), const SizedBox(width: UiConstants.space3),
Expanded( Expanded(
child: Text( child: Text(languageName, style: UiTypography.body2r.textPrimary),
languageName,
style: UiTypography.body2r.textPrimary,
),
), ),
if (enabled) if (enabled)
const Icon( const Icon(
@@ -220,10 +218,7 @@ class _FieldLabel extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Text( return Text(text, style: UiTypography.titleUppercase3m.textSecondary);
text,
style: UiTypography.titleUppercase3m.textSecondary,
);
} }
} }
@@ -244,10 +239,7 @@ class _ReadOnlyField extends StatelessWidget {
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue), borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
border: Border.all(color: UiColors.border), border: Border.all(color: UiColors.border),
), ),
child: Text( child: Text(value, style: UiTypography.body2r.textInactive),
value,
style: UiTypography.body2r.textInactive,
),
); );
} }
} }

View File

@@ -227,12 +227,12 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
content: Text(i18n.message), content: Text(i18n.message),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Modular.to.pop(), onPressed: () => Modular.to.popSafe(),
child: Text(Translations.of(context).common.cancel), child: Text(Translations.of(context).common.cancel),
), ),
TextButton( TextButton(
onPressed: () { onPressed: () {
Modular.to.pop(); Modular.to.popSafe();
_showApplyingDialog(context, shift); _showApplyingDialog(context, shift);
BlocProvider.of<ShiftDetailsBloc>(context).add( BlocProvider.of<ShiftDetailsBloc>(context).add(
BookShiftDetailsEvent( BookShiftDetailsEvent(
@@ -317,7 +317,11 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
children: [ children: [
const Icon(UiIcons.warning, color: UiColors.error), const Icon(UiIcons.warning, color: UiColors.error),
const SizedBox(width: UiConstants.space2), const SizedBox(width: UiConstants.space2),
Expanded(child: Text(context.t.staff_shifts.shift_details.eligibility_requirements)), Expanded(
child: Text(
context.t.staff_shifts.shift_details.eligibility_requirements,
),
),
], ],
), ),
content: Text( content: Text(
@@ -333,7 +337,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
text: "Go to Certificates", text: "Go to Certificates",
onPressed: () { onPressed: () {
Navigator.of(ctx).pop(); Navigator.of(ctx).pop();
Modular.to.pushNamed(StaffPaths.certificates); Modular.to.toCertificates();
}, },
), ),
], ],