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:
@@ -5,6 +5,12 @@ import 'package:firebase_data_connect/firebase_data_connect.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
import 'package:krow_domain/krow_domain.dart' as domain;
|
||||
import 'package:krow_domain/krow_domain.dart'
|
||||
show
|
||||
HubHasOrdersException,
|
||||
HubCreationFailedException,
|
||||
BusinessNotFoundException,
|
||||
NotAuthenticatedException;
|
||||
|
||||
import '../../domain/repositories/hub_repository_interface.dart';
|
||||
import '../../util/hubs_constants.dart';
|
||||
@@ -67,7 +73,9 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
.execute();
|
||||
final String? createdId = result.data?.teamHub_insert.id;
|
||||
if (createdId == null) {
|
||||
throw Exception('Hub creation failed.');
|
||||
throw HubCreationFailedException(
|
||||
technicalMessage: 'teamHub_insert returned null for hub: $name',
|
||||
);
|
||||
}
|
||||
|
||||
final List<domain.Hub> hubs = await _fetchHubsForTeam(
|
||||
@@ -97,7 +105,9 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
await _firebaseAuth.signOut();
|
||||
throw Exception('Business is missing. Please sign in again.');
|
||||
throw const BusinessNotFoundException(
|
||||
technicalMessage: 'Business ID missing from session',
|
||||
);
|
||||
}
|
||||
|
||||
final QueryResult<
|
||||
@@ -110,7 +120,9 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
.execute();
|
||||
|
||||
if (result.data.orders.isNotEmpty) {
|
||||
throw Exception("Sorry this hub has orders, it can't be deleted.");
|
||||
throw HubHasOrdersException(
|
||||
technicalMessage: 'Hub $id has ${result.data.orders.length} orders',
|
||||
);
|
||||
}
|
||||
|
||||
await _dataConnect.deleteTeamHub(id: id).execute();
|
||||
@@ -151,7 +163,9 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
|
||||
final firebase.User? user = _firebaseAuth.currentUser;
|
||||
if (user == null) {
|
||||
throw Exception('User is not authenticated.');
|
||||
throw const NotAuthenticatedException(
|
||||
technicalMessage: 'No Firebase user in currentUser',
|
||||
);
|
||||
}
|
||||
|
||||
final QueryResult<dc.GetBusinessesByUserIdData, dc.GetBusinessesByUserIdVariables> result = await _dataConnect.getBusinessesByUserId(
|
||||
@@ -159,7 +173,9 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
).execute();
|
||||
if (result.data.businesses.isEmpty) {
|
||||
await _firebaseAuth.signOut();
|
||||
throw Exception('No business found for this user. Please sign in again.');
|
||||
throw BusinessNotFoundException(
|
||||
technicalMessage: 'No business found for user ${user.uid}',
|
||||
);
|
||||
}
|
||||
|
||||
final dc.GetBusinessesByUserIdBusinesses business = result.data.businesses.first;
|
||||
@@ -206,7 +222,9 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
final OperationResult<dc.CreateTeamData, dc.CreateTeamVariables> createTeamResult = await createTeamBuilder.execute();
|
||||
final String? teamId = createTeamResult.data?.team_insert.id;
|
||||
if (teamId == null) {
|
||||
throw Exception('Team creation failed.');
|
||||
throw HubCreationFailedException(
|
||||
technicalMessage: 'Team creation failed for business ${business.id}',
|
||||
);
|
||||
}
|
||||
|
||||
return teamId;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:developer' as developer;
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
@@ -67,11 +69,20 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
||||
try {
|
||||
final List<Hub> hubs = await _getHubsUseCase();
|
||||
emit(state.copyWith(status: ClientHubsStatus.success, hubs: hubs));
|
||||
} catch (e) {
|
||||
} on AppException catch (e) {
|
||||
developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
errorMessage: e.messageKey,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
developer.log('Unexpected error: $e', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.failure,
|
||||
errorMessage: 'errors.generic.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -106,11 +117,20 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
||||
showAddHubDialog: false,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
} on AppException catch (e) {
|
||||
developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.actionFailure,
|
||||
errorMessage: e.toString(),
|
||||
errorMessage: e.messageKey,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
developer.log('Unexpected error: $e', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.actionFailure,
|
||||
errorMessage: 'errors.generic.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -131,11 +151,20 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
||||
successMessage: 'Hub deleted successfully',
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
} on AppException catch (e) {
|
||||
developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.actionFailure,
|
||||
errorMessage: e.toString(),
|
||||
errorMessage: e.messageKey,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
developer.log('Unexpected error: $e', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.actionFailure,
|
||||
errorMessage: 'errors.generic.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -159,11 +188,20 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
||||
clearHubToIdentify: true,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
} on AppException catch (e) {
|
||||
developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.actionFailure,
|
||||
errorMessage: e.toString(),
|
||||
errorMessage: e.messageKey,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
developer.log('Unexpected error: $e', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.actionFailure,
|
||||
errorMessage: 'errors.generic.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -175,8 +213,8 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
||||
) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
errorMessage: null,
|
||||
successMessage: null,
|
||||
clearErrorMessage: true,
|
||||
clearSuccessMessage: true,
|
||||
status:
|
||||
state.status == ClientHubsStatus.actionSuccess ||
|
||||
state.status == ClientHubsStatus.actionFailure
|
||||
|
||||
@@ -43,12 +43,18 @@ class ClientHubsState extends Equatable {
|
||||
bool? showAddHubDialog,
|
||||
Hub? hubToIdentify,
|
||||
bool clearHubToIdentify = false,
|
||||
bool clearErrorMessage = false,
|
||||
bool clearSuccessMessage = false,
|
||||
}) {
|
||||
return ClientHubsState(
|
||||
status: status ?? this.status,
|
||||
hubs: hubs ?? this.hubs,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
successMessage: successMessage ?? this.successMessage,
|
||||
errorMessage: clearErrorMessage
|
||||
? null
|
||||
: (errorMessage ?? this.errorMessage),
|
||||
successMessage: clearSuccessMessage
|
||||
? null
|
||||
: (successMessage ?? this.successMessage),
|
||||
showAddHubDialog: showAddHubDialog ?? this.showAddHubDialog,
|
||||
hubToIdentify: clearHubToIdentify
|
||||
? null
|
||||
|
||||
@@ -33,9 +33,10 @@ class ClientHubsPage extends StatelessWidget {
|
||||
},
|
||||
listener: (BuildContext context, ClientHubsState state) {
|
||||
if (state.errorMessage != null && state.errorMessage!.isNotEmpty) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(state.errorMessage!)));
|
||||
final String errorMessage = translateErrorKey(state.errorMessage!);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(errorMessage)),
|
||||
);
|
||||
BlocProvider.of<ClientHubsBloc>(
|
||||
context,
|
||||
).add(const ClientHubsMessageCleared());
|
||||
@@ -178,7 +179,7 @@ class ClientHubsPage extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: () => Modular.to.pop(),
|
||||
onTap: () => Modular.to.navigate('/client-main/home/'),
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
|
||||
Reference in New Issue
Block a user