feat(core_localization): add error translation utility and new error messages

feat(client_auth): implement error handling with localized messages
feat(client_hubs): implement error handling with localized messages
feat(client_billing): navigate to home after billing
feat(client_coverage): navigate to home after coverage
feat(client_create_order): navigate to home after create order
feat(client_settings): navigate to home after settings
feat(client_view_orders): show hub name in order card
fix(client_auth): handle existing firebase accounts during sign-up

This commit introduces a new utility function, `translateErrorKey`,
to translate error message keys to localized strings. It also adds
new error messages to the localization files for both English and
Spanish.

The commit also implements error handling with localized messages in
the client authentication and hubs features. This makes it easier for
users to understand what went wrong and how to fix it.

Additionally, the commit updates the navigation flow for the billing,
coverage, create order, and settings features to navigate to the home
page after the user completes the action.

Finally, the commit fixes a bug where the hub name was not being
displayed in the order card.
This commit is contained in:
bwnyasse
2026-01-31 18:56:48 -05:00
parent 9517606e7a
commit caac050ac9
30 changed files with 1396 additions and 105 deletions

View File

@@ -94,3 +94,6 @@ export 'src/entities/profile/experience_skill.dart';
export 'src/adapters/profile/bank_account_adapter.dart';
export 'src/adapters/profile/tax_form_adapter.dart';
export 'src/adapters/financial/payment_adapter.dart';
// Exceptions
export 'src/exceptions/app_exception.dart';

View File

@@ -0,0 +1,314 @@
/// Base sealed class for all application exceptions.
///
/// Provides type-safe error handling with user-friendly message keys.
/// Technical details are captured for logging but never shown to users.
sealed class AppException implements Exception {
const AppException({
required this.code,
this.technicalMessage,
});
/// Unique error code for logging/tracking (e.g., "AUTH_001")
final String code;
/// Technical details for developers (never shown to users)
final String? technicalMessage;
/// Returns the localization key for user-friendly message
String get messageKey;
@override
String toString() => 'AppException($code): $technicalMessage';
}
// ============================================================
// AUTH EXCEPTIONS
// ============================================================
/// Base class for authentication-related exceptions.
sealed class AuthException extends AppException {
const AuthException({required super.code, super.technicalMessage});
}
/// Thrown when email/password combination is incorrect.
class InvalidCredentialsException extends AuthException {
const InvalidCredentialsException({String? technicalMessage})
: super(code: 'AUTH_001', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.auth.invalid_credentials';
}
/// Thrown when attempting to register with an email that already exists.
class AccountExistsException extends AuthException {
const AccountExistsException({String? technicalMessage})
: super(code: 'AUTH_002', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.auth.account_exists';
}
/// Thrown when the user session has expired.
class SessionExpiredException extends AuthException {
const SessionExpiredException({String? technicalMessage})
: super(code: 'AUTH_003', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.auth.session_expired';
}
/// Thrown when user profile is not found in database after Firebase auth.
class UserNotFoundException extends AuthException {
const UserNotFoundException({String? technicalMessage})
: super(code: 'AUTH_004', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.auth.user_not_found';
}
/// Thrown when user is not authorized for the current app (wrong role).
class UnauthorizedAppException extends AuthException {
const UnauthorizedAppException({String? technicalMessage})
: super(code: 'AUTH_005', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.auth.unauthorized_app';
}
/// Thrown when password doesn't meet security requirements.
class WeakPasswordException extends AuthException {
const WeakPasswordException({String? technicalMessage})
: super(code: 'AUTH_006', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.auth.weak_password';
}
/// Thrown when sign-up process fails.
class SignUpFailedException extends AuthException {
const SignUpFailedException({String? technicalMessage})
: super(code: 'AUTH_007', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.auth.sign_up_failed';
}
/// Thrown when sign-in process fails.
class SignInFailedException extends AuthException {
const SignInFailedException({String? technicalMessage})
: super(code: 'AUTH_008', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.auth.sign_in_failed';
}
/// Thrown when email exists but password doesn't match.
class PasswordMismatchException extends AuthException {
const PasswordMismatchException({String? technicalMessage})
: super(code: 'AUTH_009', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.auth.password_mismatch';
}
/// Thrown when account exists only with Google provider (no password).
class GoogleOnlyAccountException extends AuthException {
const GoogleOnlyAccountException({String? technicalMessage})
: super(code: 'AUTH_010', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.auth.google_only_account';
}
// ============================================================
// HUB EXCEPTIONS
// ============================================================
/// Base class for hub-related exceptions.
sealed class HubException extends AppException {
const HubException({required super.code, super.technicalMessage});
}
/// Thrown when attempting to delete a hub that has active orders.
class HubHasOrdersException extends HubException {
const HubHasOrdersException({String? technicalMessage})
: super(code: 'HUB_001', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.hub.has_orders';
}
/// Thrown when hub is not found.
class HubNotFoundException extends HubException {
const HubNotFoundException({String? technicalMessage})
: super(code: 'HUB_002', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.hub.not_found';
}
/// Thrown when hub creation fails.
class HubCreationFailedException extends HubException {
const HubCreationFailedException({String? technicalMessage})
: super(code: 'HUB_003', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.hub.creation_failed';
}
// ============================================================
// ORDER EXCEPTIONS
// ============================================================
/// Base class for order-related exceptions.
sealed class OrderException extends AppException {
const OrderException({required super.code, super.technicalMessage});
}
/// Thrown when order creation is attempted without a hub.
class OrderMissingHubException extends OrderException {
const OrderMissingHubException({String? technicalMessage})
: super(code: 'ORDER_001', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.order.missing_hub';
}
/// Thrown when order creation is attempted without a vendor.
class OrderMissingVendorException extends OrderException {
const OrderMissingVendorException({String? technicalMessage})
: super(code: 'ORDER_002', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.order.missing_vendor';
}
/// Thrown when order creation fails.
class OrderCreationFailedException extends OrderException {
const OrderCreationFailedException({String? technicalMessage})
: super(code: 'ORDER_003', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.order.creation_failed';
}
/// Thrown when shift creation fails.
class ShiftCreationFailedException extends OrderException {
const ShiftCreationFailedException({String? technicalMessage})
: super(code: 'ORDER_004', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.order.shift_creation_failed';
}
/// Thrown when order is missing required business context.
class OrderMissingBusinessException extends OrderException {
const OrderMissingBusinessException({String? technicalMessage})
: super(code: 'ORDER_005', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.order.missing_business';
}
// ============================================================
// PROFILE EXCEPTIONS
// ============================================================
/// Base class for profile-related exceptions.
sealed class ProfileException extends AppException {
const ProfileException({required super.code, super.technicalMessage});
}
/// Thrown when staff profile is not found.
class StaffProfileNotFoundException extends ProfileException {
const StaffProfileNotFoundException({String? technicalMessage})
: super(code: 'PROFILE_001', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.profile.staff_not_found';
}
/// Thrown when business profile is not found.
class BusinessNotFoundException extends ProfileException {
const BusinessNotFoundException({String? technicalMessage})
: super(code: 'PROFILE_002', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.profile.business_not_found';
}
/// Thrown when profile update fails.
class ProfileUpdateFailedException extends ProfileException {
const ProfileUpdateFailedException({String? technicalMessage})
: super(code: 'PROFILE_003', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.profile.update_failed';
}
// ============================================================
// SHIFT EXCEPTIONS
// ============================================================
/// Base class for shift-related exceptions.
sealed class ShiftException extends AppException {
const ShiftException({required super.code, super.technicalMessage});
}
/// Thrown when no open roles are available for a shift.
class NoOpenRolesException extends ShiftException {
const NoOpenRolesException({String? technicalMessage})
: super(code: 'SHIFT_001', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.shift.no_open_roles';
}
/// Thrown when application for shift is not found.
class ApplicationNotFoundException extends ShiftException {
const ApplicationNotFoundException({String? technicalMessage})
: super(code: 'SHIFT_002', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.shift.application_not_found';
}
/// Thrown when no active shift is found for clock out.
class NoActiveShiftException extends ShiftException {
const NoActiveShiftException({String? technicalMessage})
: super(code: 'SHIFT_003', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.shift.no_active_shift';
}
// ============================================================
// NETWORK/GENERIC EXCEPTIONS
// ============================================================
/// Thrown when there is no network connection.
class NetworkException extends AppException {
const NetworkException({String? technicalMessage})
: super(code: 'NET_001', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.generic.no_connection';
}
/// Thrown when an unexpected error occurs.
class UnknownException extends AppException {
const UnknownException({String? technicalMessage})
: super(code: 'UNKNOWN', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.generic.unknown';
}
/// Thrown when user is not authenticated.
class NotAuthenticatedException extends AppException {
const NotAuthenticatedException({String? technicalMessage})
: super(code: 'AUTH_NOT_LOGGED', technicalMessage: technicalMessage);
@override
String get messageKey => 'errors.auth.not_authenticated';
}