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:
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
Reference in New Issue
Block a user