refactor: enhance navigation robustness by introducing popSafe and safePushNamedAndRemoveUntil methods and updating their usage.
This commit is contained in:
@@ -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'),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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'),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user