Merge pull request 'show message when there is error in submit or remove approvals' (#102) from like_tenders_error_approval into main

Reviewed-on: https://repo.ravanertebat.com/TM/tm_app/pulls/102
This commit is contained in:
a.ghabeli
2025-09-06 14:11:19 +03:30
6 changed files with 137 additions and 88 deletions
+9 -9
View File
@@ -21,7 +21,7 @@ class FetchDataException extends AppException {
FetchDataException([String? message, int? statusCode, originalError]) FetchDataException([String? message, int? statusCode, originalError])
: super( : super(
message ?? 'Error During Communication: ', message ?? 'Error During Communication: ',
'Network Error', 'Network Error : ',
statusCode, statusCode,
originalError, originalError,
); );
@@ -31,7 +31,7 @@ class AuthenticationException extends AppException {
AuthenticationException([String? message, int? statusCode, originalError]) AuthenticationException([String? message, int? statusCode, originalError])
: super( : super(
message ?? 'Authentication Failed.', message ?? 'Authentication Failed.',
'Auth Error: ', 'Auth Error : ',
statusCode, statusCode,
originalError, originalError,
); );
@@ -41,7 +41,7 @@ class NetworkConnectionException extends AppException {
NetworkConnectionException([String? message, originalError]) NetworkConnectionException([String? message, originalError])
: super( : super(
message ?? 'No internet connection available.', message ?? 'No internet connection available.',
'Connection Error: ', 'Connection Error : ',
null, null,
originalError, originalError,
); );
@@ -51,7 +51,7 @@ class TimeoutException extends AppException {
TimeoutException([String? message, originalError]) TimeoutException([String? message, originalError])
: super( : super(
message ?? 'Request timed out.', message ?? 'Request timed out.',
'Timeout Error: ', 'Timeout Error : ',
null, null,
originalError, originalError,
); );
@@ -61,7 +61,7 @@ class ServerException extends AppException {
ServerException([String? message, int? statusCode, originalError]) ServerException([String? message, int? statusCode, originalError])
: super( : super(
message ?? 'Server error occurred.', message ?? 'Server error occurred.',
'Server Error: ', 'Server Error : ',
statusCode, statusCode,
originalError, originalError,
); );
@@ -71,7 +71,7 @@ class BadRequestException extends AppException {
BadRequestException([String? message, int? statusCode, originalError]) BadRequestException([String? message, int? statusCode, originalError])
: super( : super(
message ?? 'Bad request.', message ?? 'Bad request.',
'Bad Request Error: ', 'Bad Request Error : ',
statusCode, statusCode,
originalError, originalError,
); );
@@ -81,7 +81,7 @@ class NotFoundException extends AppException {
NotFoundException([String? message, int? statusCode, originalError]) NotFoundException([String? message, int? statusCode, originalError])
: super( : super(
message ?? 'Resource not found.', message ?? 'Resource not found.',
'Not Found Error: ', 'Not Found Error : ',
statusCode, statusCode,
originalError, originalError,
); );
@@ -91,7 +91,7 @@ class ForbiddenException extends AppException {
ForbiddenException([String? message, int? statusCode, originalError]) ForbiddenException([String? message, int? statusCode, originalError])
: super( : super(
message ?? 'Access forbidden.', message ?? 'Access forbidden.',
'Forbidden Error: ', 'Forbidden Error : ',
statusCode, statusCode,
originalError, originalError,
); );
@@ -101,7 +101,7 @@ class RateLimitException extends AppException {
RateLimitException([String? message, int? statusCode, originalError]) RateLimitException([String? message, int? statusCode, originalError])
: super( : super(
message ?? 'Rate limit exceeded.', message ?? 'Rate limit exceeded.',
'Rate Limit Error: ', 'Rate Limit Error : ',
statusCode, statusCode,
originalError, originalError,
); );
+13 -13
View File
@@ -6,22 +6,22 @@ import '../../../core/network/network_manager.dart';
class LikedTendersService { class LikedTendersService {
LikedTendersService({required NetworkManager networkManager}) LikedTendersService({required NetworkManager networkManager})
: _networkManager = networkManager; : _networkManager = networkManager;
final NetworkManager _networkManager; final NetworkManager _networkManager;
Future<Result<LikedTendersResponse>> getLikedTenders({ Future<Result<LikedTendersResponse>> getLikedTenders({
int offset = 0, int offset = 0,
int limit = 20, int limit = 20,
}) async { }) async {
final uri = '${LikedTendersApi.likedTenders}&limit=$limit&offset=$offset'; final uri = '${LikedTendersApi.likedTenders}&limit=$limit&offset=$offset';
final result = await _networkManager.makeRequest( final result = await _networkManager.makeRequest(
uri, uri,
(json) => LikedTendersResponse.fromJson(json), (json) => LikedTendersResponse.fromJson(json),
method: 'GET', method: 'GET',
); );
return result; return result;
} }
} }
@@ -55,6 +55,11 @@ class LikedTendersViewModel with ChangeNotifier {
_homeViewModel.init(); _homeViewModel.init();
} }
void clearApprovalErrorMessage() {
_errorMessage = null;
notifyListeners();
}
Future<void> getLikedTenders({bool reset = false}) async { Future<void> getLikedTenders({bool reset = false}) async {
if (!reset && (isLoadingMore || !hasMore)) { if (!reset && (isLoadingMore || !hasMore)) {
return; return;
@@ -303,7 +308,7 @@ class LikedTendersViewModel with ChangeNotifier {
approvalStatus = false; approvalStatus = false;
break; break;
} }
approvalStatus = false;
_isSubmitApprovalLoading = false; _isSubmitApprovalLoading = false;
notifyListeners(); notifyListeners();
} }
@@ -370,6 +375,7 @@ class LikedTendersViewModel with ChangeNotifier {
break; break;
} }
approvalStatus = false;
_isRejectApprovalLoading = false; _isRejectApprovalLoading = false;
notifyListeners(); notifyListeners();
} }
@@ -24,9 +24,26 @@ class _LikedTendersDesktopPageState extends State<LikedTendersDesktopPage> {
@override @override
void initState() { void initState() {
viewModel = context.read<LikedTendersViewModel>(); viewModel = context.read<LikedTendersViewModel>();
viewModel.addListener(_viewModelListener);
super.initState(); super.initState();
} }
void _viewModelListener() {
if (viewModel.approvalErrorMessage?.isNotEmpty == true &&
viewModel.approvalStatus == false) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(viewModel.approvalErrorMessage!)));
viewModel.clearApprovalErrorMessage();
}
}
@override
void dispose() {
viewModel.removeListener(_viewModelListener);
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PopScope( return PopScope(
@@ -27,6 +27,18 @@ class _LikedTendersMobilePageState extends State<LikedTendersMobilePage> {
viewModel = context.read<LikedTendersViewModel>(); viewModel = context.read<LikedTendersViewModel>();
super.initState(); super.initState();
_scrollController = ScrollController()..addListener(_onScroll); _scrollController = ScrollController()..addListener(_onScroll);
viewModel.addListener(_viewModelListener);
}
void _viewModelListener() {
if (mounted &&
viewModel.approvalErrorMessage?.isNotEmpty == true &&
viewModel.approvalStatus == false) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(viewModel.approvalErrorMessage!)));
viewModel.clearApprovalErrorMessage();
}
} }
void _onScroll() { void _onScroll() {
@@ -46,6 +58,7 @@ class _LikedTendersMobilePageState extends State<LikedTendersMobilePage> {
void dispose() { void dispose() {
_scrollController.removeListener(_onScroll); _scrollController.removeListener(_onScroll);
_scrollController.dispose(); _scrollController.dispose();
viewModel.removeListener(_viewModelListener);
super.dispose(); super.dispose();
} }
@@ -94,75 +107,71 @@ class _LikedTendersMobilePageState extends State<LikedTendersMobilePage> {
final itemCount = final itemCount =
feedback.length + (viewModel.isLoadingMore ? 1 : 0); feedback.length + (viewModel.isLoadingMore ? 1 : 0);
return RefreshIndicator( return ListView.builder(
onRefresh: () async { controller: _scrollController,
await viewModel.getLikedTenders(reset: true); padding: EdgeInsets.symmetric(
}, horizontal: 24.0.w(),
child: ListView.builder( vertical: 15.0.h(),
controller: _scrollController, ),
padding: EdgeInsets.symmetric( itemCount: itemCount,
horizontal: 24.0.w(), itemBuilder: (context, index) {
vertical: 15.0.h(), if (index < feedback.length) {
), final item = feedback[index];
itemCount: itemCount, TenderApprovalsData? approval;
itemBuilder: (context, index) { bool hasApproval = viewModel.hasTenderApprovalForTender(
if (index < feedback.length) { item.tenderId!,
final item = feedback[index]; );
TenderApprovalsData? approval; if (hasApproval) {
bool hasApproval = viewModel approval = viewModel.getApprovalStatusForTender(
.hasTenderApprovalForTender(item.tenderId!); item.tenderId!,
if (hasApproval) {
approval = viewModel.getApprovalStatusForTender(
item.tenderId!,
);
} else {
approval = null;
}
return Padding(
padding: EdgeInsets.only(bottom: 20.0.h()),
child: Dismissible(
key: ValueKey(item.tenderId ?? index),
direction: DismissDirection.endToStart,
background: Container(
alignment: Alignment.centerRight,
padding: EdgeInsets.symmetric(
horizontal: 20.0.w(),
),
child: SvgPicture.asset(
AssetsManager.trash,
width: 32.0.w(),
height: 32.0.h(),
),
),
onDismissed: (_) {
if (item.tenderId != null) {
viewModel.removeTenderById(item.tenderId!);
}
},
child: LikedListItem(
tender: item.tender!,
approval: approval,
),
),
); );
} else { } else {
return Padding( approval = null;
padding: EdgeInsets.symmetric(vertical: 20.0.h()), }
child: Center(
child: SizedBox( return Padding(
width: 24.0.w(), padding: EdgeInsets.only(bottom: 20.0.h()),
height: 24.0.h(), child: Dismissible(
child: CircularProgressIndicator( key: ValueKey(item.tenderId ?? index),
color: AppColors.secondary50, direction: DismissDirection.endToStart,
strokeWidth: 2, background: Container(
), alignment: Alignment.centerRight,
padding: EdgeInsets.symmetric(
horizontal: 20.0.w(),
),
child: SvgPicture.asset(
AssetsManager.trash,
width: 32.0.w(),
height: 32.0.h(),
), ),
), ),
); onDismissed: (_) {
} if (item.tenderId != null) {
}, viewModel.removeTenderById(item.tenderId!);
), }
},
child: LikedListItem(
tender: item.tender!,
approval: approval,
),
),
);
} else {
return Padding(
padding: EdgeInsets.symmetric(vertical: 20.0.h()),
child: Center(
child: SizedBox(
width: 24.0.w(),
height: 24.0.h(),
child: CircularProgressIndicator(
color: AppColors.secondary50,
strokeWidth: 2,
),
),
),
);
}
},
); );
}, },
), ),
@@ -26,6 +26,23 @@ class _LikedTendersTabletPageState extends State<LikedTendersTabletPage> {
void initState() { void initState() {
viewModel = context.read<LikedTendersViewModel>(); viewModel = context.read<LikedTendersViewModel>();
super.initState(); super.initState();
viewModel.addListener(_viewModelListener);
}
void _viewModelListener() {
if (viewModel.approvalErrorMessage?.isNotEmpty == true &&
viewModel.approvalStatus == false) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(viewModel.approvalErrorMessage!)));
viewModel.clearApprovalErrorMessage();
}
}
@override
void dispose() {
viewModel.removeListener(_viewModelListener);
super.dispose();
} }
@override @override