Fix build errors: localization syntax, key paths, and ViewOrderCard widget

This commit is contained in:
2026-02-14 16:26:10 +05:30
parent 097481d26a
commit 4e1a41ebff
25 changed files with 420 additions and 207 deletions

View File

@@ -156,24 +156,30 @@ class AuthRepositoryImpl
.userRole('STAFF')
.execute());
} else {
if (user.userRole != 'STAFF') {
await firebaseAuth.signOut();
throw const domain.UnauthorizedAppException(
technicalMessage: 'User is not authorized for this app.',
);
}
// User exists in PostgreSQL. Check if they have a STAFF profile.
final QueryResult<GetStaffByUserIdData, GetStaffByUserIdVariables>
staffResponse = await executeProtected(() => dataConnect
.getStaffByUserId(
userId: firebaseUser.uid,
)
.execute());
if (staffResponse.data.staffs.isNotEmpty) {
// If profile exists, they should use Login mode.
await firebaseAuth.signOut();
throw const domain.AccountExistsException(
technicalMessage: 'This user already has a staff profile. Please log in.',
technicalMessage:
'This user already has a staff profile. Please log in.',
);
}
// If they don't have a staff profile but they exist as BUSINESS,
// they are allowed to "Sign Up" for Staff.
// We update their userRole to 'BOTH'.
if (user.userRole == 'BUSINESS') {
await executeProtected(() =>
dataConnect.updateUser(id: firebaseUser.uid).userRole('BOTH').execute());
}
}
} else {
if (user == null) {
@@ -182,7 +188,8 @@ class AuthRepositoryImpl
technicalMessage: 'Authenticated user profile not found in database.',
);
}
if (user.userRole != 'STAFF') {
// Allow STAFF or BOTH roles to log in to the Staff App
if (user.userRole != 'STAFF' && user.userRole != 'BOTH') {
await firebaseAuth.signOut();
throw const domain.UnauthorizedAppException(
technicalMessage: 'User is not authorized for this app.',

View File

@@ -5,7 +5,7 @@ import 'package:staff_authentication/src/domain/ui_entities/auth_mode.dart';
abstract class AuthEvent extends Equatable {
const AuthEvent();
@override
List<Object> get props => <Object>[];
List<Object?> get props => <Object?>[];
}
/// Event for requesting a sign-in with a phone number.
@@ -19,7 +19,7 @@ class AuthSignInRequested extends AuthEvent {
const AuthSignInRequested({this.phoneNumber, required this.mode});
@override
List<Object> get props => <Object>[mode];
List<Object?> get props => <Object?>[phoneNumber, mode];
}
/// Event for submitting an OTP (One-Time Password) for verification.
@@ -43,7 +43,7 @@ class AuthOtpSubmitted extends AuthEvent {
});
@override
List<Object> get props => <Object>[verificationId, smsCode, mode];
List<Object?> get props => <Object?>[verificationId, smsCode, mode];
}
/// Event for clearing any authentication error in the state.
@@ -57,7 +57,7 @@ class AuthResetRequested extends AuthEvent {
const AuthResetRequested({required this.mode});
@override
List<Object> get props => <Object>[mode];
List<Object?> get props => <Object?>[mode];
}
/// Event for ticking down the resend cooldown.
@@ -67,7 +67,7 @@ class AuthCooldownTicked extends AuthEvent {
const AuthCooldownTicked(this.secondsRemaining);
@override
List<Object> get props => <Object>[secondsRemaining];
List<Object?> get props => <Object?>[secondsRemaining];
}
/// Event for updating the current draft OTP in the state.
@@ -78,7 +78,7 @@ class AuthOtpUpdated extends AuthEvent {
const AuthOtpUpdated(this.otp);
@override
List<Object> get props => <Object>[otp];
List<Object?> get props => <Object?>[otp];
}
/// Event for updating the current draft phone number in the state.
@@ -89,5 +89,5 @@ class AuthPhoneUpdated extends AuthEvent {
const AuthPhoneUpdated(this.phoneNumber);
@override
List<Object> get props => <Object>[phoneNumber];
List<Object?> get props => <Object?>[phoneNumber];
}

View File

@@ -50,7 +50,13 @@ class _PhoneVerificationPageState extends State<PhoneVerificationPage> {
required BuildContext context,
required String phoneNumber,
}) {
final String normalized = phoneNumber.replaceAll(RegExp(r'\\D'), '');
String normalized = phoneNumber.replaceAll(RegExp(r'\D'), '');
// Handle US numbers entered with a leading 1
if (normalized.length == 11 && normalized.startsWith('1')) {
normalized = normalized.substring(1);
}
if (normalized.length == 10) {
BlocProvider.of<AuthBloc>(
context,

View File

@@ -88,7 +88,7 @@ class _PhoneInputFormFieldState extends State<PhoneInputFormField> {
keyboardType: TextInputType.phone,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(10),
LengthLimitingTextInputFormatter(11),
],
decoration: InputDecoration(
hintText: t.staff_authentication.phone_input.hint,

View File

@@ -1,3 +1,4 @@
import 'package:core_localization/core_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
@@ -58,9 +59,9 @@ class _ShiftCardState extends State<ShiftCard> {
try {
final date = DateTime.parse(dateStr);
final diff = DateTime.now().difference(date);
if (diff.inHours < 1) return 'Just now';
if (diff.inHours < 24) return 'Pending ${diff.inHours}h ago';
return 'Pending ${diff.inDays}d ago';
if (diff.inHours < 1) return t.staff_shifts.card.just_now;
if (diff.inHours < 24) return t.staff_shifts.details.pending_time(time: '${diff.inHours}h');
return t.staff_shifts.details.pending_time(time: '${diff.inDays}d');
} catch (e) {
return '';
}
@@ -220,7 +221,11 @@ class _ShiftCardState extends State<ShiftCard> {
borderRadius: UiConstants.radiusFull,
),
child: Text(
'Assigned ${_getTimeAgo(widget.shift.createdDate).replaceAll('Pending ', '')}',
t.staff_shifts.card.assigned(
time: _getTimeAgo(widget.shift.createdDate)
.replaceAll('Pending ', '')
.replaceAll('Just now', 'just now'),
),
style: UiTypography.body3m.white,
),
),
@@ -302,13 +307,13 @@ class _ShiftCardState extends State<ShiftCard> {
children: [
_buildTag(
UiIcons.zap,
'Immediate start',
t.staff_shifts.tags.immediate_start,
UiColors.accent.withValues(alpha: 0.3),
UiColors.foreground,
),
_buildTag(
UiIcons.timer,
'No experience',
t.staff_shifts.tags.no_experience,
UiColors.tagError,
UiColors.textError,
),
@@ -342,7 +347,7 @@ class _ShiftCardState extends State<ShiftCard> {
BorderRadius.circular(UiConstants.radiusBase),
),
),
child: const Text('Accept shift'),
child: Text(t.staff_shifts.card.accept_shift),
),
),
const SizedBox(height: UiConstants.space2),
@@ -361,7 +366,7 @@ class _ShiftCardState extends State<ShiftCard> {
BorderRadius.circular(UiConstants.radiusBase),
),
),
child: const Text('Decline shift'),
child: Text(t.staff_shifts.card.decline_shift),
),
),
const SizedBox(height: UiConstants.space5),

View File

@@ -35,14 +35,14 @@ class CertificatesPage extends StatelessWidget {
if (state.status == CertificatesStatus.failure) {
return Scaffold(
appBar: AppBar(title: const Text('Certificates')),
appBar: AppBar(title: Text(t.staff_certificates.title)),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
state.errorMessage != null
? translateErrorKey(state.errorMessage!)
: 'Error loading certificates',
child: Text(
state.errorMessage != null
? translateErrorKey(state.errorMessage!)
: t.staff_certificates.error_loading,
textAlign: TextAlign.center,
style: UiTypography.body2r.textSecondary,
),

View File

@@ -1,3 +1,4 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -18,7 +19,7 @@ class EmergencyContactSaveButton extends StatelessWidget {
if (state.status == EmergencyContactStatus.saved) {
UiSnackbar.show(
context,
message: 'Emergency contacts saved successfully',
message: t.staff.profile.menu_items.emergency_contact_page.save_success,
type: UiSnackbarType.success,
margin: const EdgeInsets.only(bottom: 150, left: 16, right: 16),
);
@@ -48,7 +49,7 @@ class EmergencyContactSaveButton extends StatelessWidget {
),
),
)
: const Text('Save & Continue'),
: Text(t.staff.profile.menu_items.emergency_contact_page.save_continue),
),
),
);

View File

@@ -105,12 +105,12 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Accept Shift'),
content: const Text('Are you sure you want to accept this shift?'),
title: Text(t.staff_shifts.my_shifts_tab.confirm_dialog.title),
content: Text(t.staff_shifts.my_shifts_tab.confirm_dialog.message),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel'),
child: Text(t.common.cancel),
),
TextButton(
onPressed: () {
@@ -118,14 +118,14 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
context.read<ShiftsBloc>().add(AcceptShiftEvent(id));
UiSnackbar.show(
context,
message: 'Shift confirmed!',
message: t.staff_shifts.my_shifts_tab.confirm_dialog.success,
type: UiSnackbarType.success,
);
},
style: TextButton.styleFrom(
foregroundColor: UiColors.success,
),
child: const Text('Accept'),
child: Text(t.staff_shifts.shift_details.accept_shift),
),
],
),
@@ -136,14 +136,14 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Decline Shift'),
content: const Text(
'Are you sure you want to decline this shift? This action cannot be undone.',
title: Text(t.staff_shifts.my_shifts_tab.decline_dialog.title),
content: Text(
t.staff_shifts.my_shifts_tab.decline_dialog.message,
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel'),
child: Text(t.common.cancel),
),
TextButton(
onPressed: () {
@@ -151,14 +151,14 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
context.read<ShiftsBloc>().add(DeclineShiftEvent(id));
UiSnackbar.show(
context,
message: 'Shift declined.',
message: t.staff_shifts.my_shifts_tab.decline_dialog.success,
type: UiSnackbarType.error,
);
},
style: TextButton.styleFrom(
foregroundColor: UiColors.destructive,
),
child: const Text('Decline'),
child: Text(t.staff_shifts.shift_details.decline),
),
],
),
@@ -169,9 +169,9 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
try {
final date = DateTime.parse(dateStr);
final now = DateTime.now();
if (_isSameDay(date, now)) return "Today";
if (_isSameDay(date, now)) return t.staff_shifts.my_shifts_tab.date.today;
final tomorrow = now.add(const Duration(days: 1));
if (_isSameDay(date, tomorrow)) return "Tomorrow";
if (_isSameDay(date, tomorrow)) return t.staff_shifts.my_shifts_tab.date.tomorrow;
return DateFormat('EEE, MMM d').format(date);
} catch (_) {
return dateStr;
@@ -338,7 +338,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
const SizedBox(height: UiConstants.space5),
if (widget.pendingAssignments.isNotEmpty) ...[
_buildSectionHeader(
"Awaiting Confirmation",
t.staff_shifts.my_shifts_tab.sections.awaiting,
UiColors.textWarning,
),
...widget.pendingAssignments.map(
@@ -356,7 +356,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
],
if (visibleCancelledShifts.isNotEmpty) ...[
_buildSectionHeader("Cancelled Shifts", UiColors.textSecondary),
_buildSectionHeader(t.staff_shifts.my_shifts_tab.sections.cancelled, UiColors.textSecondary),
...visibleCancelledShifts.map(
(shift) => Padding(
padding: const EdgeInsets.only(bottom: UiConstants.space4),
@@ -378,7 +378,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
// Confirmed Shifts
if (visibleMyShifts.isNotEmpty) ...[
_buildSectionHeader("Confirmed Shifts", UiColors.textSecondary),
_buildSectionHeader(t.staff_shifts.my_shifts_tab.sections.confirmed, UiColors.textSecondary),
...visibleMyShifts.map(
(shift) => Padding(
padding: const EdgeInsets.only(bottom: UiConstants.space3),
@@ -390,10 +390,10 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
if (visibleMyShifts.isEmpty &&
widget.pendingAssignments.isEmpty &&
widget.cancelledShifts.isEmpty)
const EmptyStateView(
EmptyStateView(
icon: UiIcons.calendar,
title: "No shifts this week",
subtitle: "Try finding new jobs in the Find tab",
title: t.staff_shifts.my_shifts_tab.empty.title,
subtitle: t.staff_shifts.my_shifts_tab.empty.subtitle,
),
const SizedBox(height: UiConstants.space32),
@@ -462,13 +462,13 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
),
const SizedBox(width: 6),
Text(
"CANCELLED",
t.staff_shifts.my_shifts_tab.card.cancelled,
style: UiTypography.footnote2b.textError,
),
if (isLastMinute) ...[
const SizedBox(width: 4),
Text(
"• 4hr compensation",
t.staff_shifts.my_shifts_tab.card.compensation,
style: UiTypography.footnote2m.textSuccess,
),
],