From 513475cfb60a3885afc7313af3d9d374dc5ee394 Mon Sep 17 00:00:00 2001 From: amirrezaghabeli Date: Tue, 2 Sep 2025 15:33:07 +0330 Subject: [PATCH 1/3] initial your tenders --- lib/core/config/app_config.dart | 2 +- .../repositories/your_tenders_repository.dart | 21 ++++++++-- lib/data/services/api/your_tenders_api.dart | 14 +++++++ lib/data/services/your_tenders_service.dart | 19 +++++++++ lib/view_models/your_tenders_view_model.dart | 41 ++++++++++++++++--- lib/views/tenders/widgets/tender_card.dart | 3 +- .../pages/your_tenders_screen.dart | 26 +++++++++++- .../widgets/tenders_filter_dialog.dart | 20 ++++++++- 8 files changed, 132 insertions(+), 14 deletions(-) diff --git a/lib/core/config/app_config.dart b/lib/core/config/app_config.dart index e740c3d..7b8a654 100644 --- a/lib/core/config/app_config.dart +++ b/lib/core/config/app_config.dart @@ -16,7 +16,7 @@ class AppConfig { // // Handle different platforms for local development if (kIsWeb) { // For web, use localhost - return 'http://localhost:8081'; + return 'http://localhost:8090'; } else { // For Android emulator, use 10.0.2.2 (special IP for host machine) return 'http://10.0.2.2:8081'; diff --git a/lib/data/repositories/your_tenders_repository.dart b/lib/data/repositories/your_tenders_repository.dart index 007d5f7..7432d78 100644 --- a/lib/data/repositories/your_tenders_repository.dart +++ b/lib/data/repositories/your_tenders_repository.dart @@ -3,10 +3,25 @@ import 'package:tm_app/data/services/model/tender_approvals_response/tender_appr import 'package:tm_app/data/services/your_tenders_service.dart'; class YourTendersRepository { - final YourTendersService yourTendersService; - YourTendersRepository({required this.yourTendersService}); + final YourTendersService _yourTendersService; + YourTendersRepository({required YourTendersService yourTendersService}) + : _yourTendersService = yourTendersService; Future> getApprovedTenders() { - return yourTendersService.getApprovedTenders(); + return _yourTendersService.getApprovedTenders(); + } + + Future> getTenders({ + required int limit, + required int offset, + required String createdFrom, + required String createdTo, + }) { + return _yourTendersService.getTenders( + limit: limit, + offset: offset, + createdFrom: createdFrom, + createdTo: createdTo, + ); } } diff --git a/lib/data/services/api/your_tenders_api.dart b/lib/data/services/api/your_tenders_api.dart index 99ad134..ff30be2 100644 --- a/lib/data/services/api/your_tenders_api.dart +++ b/lib/data/services/api/your_tenders_api.dart @@ -1,4 +1,18 @@ class YourTendersApi { static const String tenderApprovalsStatus = '/api/v1/tender-approvals?status=submitted'; + + static const String tenderApproval = '/api/v1/tender-approvals'; + + static String getTenders({ + required int limit, + required int offset, + required String createdFrom, + required String createdTo, + }) { + if (createdFrom.isNotEmpty && createdTo.isNotEmpty) { + return '$tenderApproval?limit=$limit&offset=$offset&created_from=$createdFrom&created_to=$createdTo'; + } + return '$tenderApproval?limit=$limit&offset=$offset'; + } } diff --git a/lib/data/services/your_tenders_service.dart b/lib/data/services/your_tenders_service.dart index bb36bbe..b1e1c54 100644 --- a/lib/data/services/your_tenders_service.dart +++ b/lib/data/services/your_tenders_service.dart @@ -19,4 +19,23 @@ class YourTendersService { return result; } + + Future> getTenders({ + required int limit, + required int offset, + required String createdFrom, + required String createdTo, + }) async { + final result = await _networkManager.makeRequest( + YourTendersApi.getTenders( + limit: limit, + offset: offset, + createdFrom: createdFrom, + createdTo: createdTo, + ), + (json) => TenderApprovalsResponse.fromJson(json), + method: 'GET', + ); + return result; + } } diff --git a/lib/view_models/your_tenders_view_model.dart b/lib/view_models/your_tenders_view_model.dart index fb49e5e..a92d0e8 100644 --- a/lib/view_models/your_tenders_view_model.dart +++ b/lib/view_models/your_tenders_view_model.dart @@ -6,9 +6,7 @@ import 'package:tm_app/data/services/model/tender_approvals_response/tender_appr class YourTendersViewModel with ChangeNotifier { final YourTendersRepository yourTendersRepository; - YourTendersViewModel({required this.yourTendersRepository}) { - getApprovedTenders(); - } + YourTendersViewModel({required this.yourTendersRepository}); bool isLoading = false; String? errorMessage; @@ -20,10 +18,14 @@ class YourTendersViewModel with ChangeNotifier { final _fmt = DateFormat('yyyy/MM/dd'); String get startDateFormatted => - selectedDateRange?.start != null ? _fmt.format(selectedDateRange!.start) : 'YYYY/MM/DD'; + selectedDateRange?.start != null + ? _fmt.format(selectedDateRange!.start) + : 'YYYY/MM/DD'; String get endDateFormatted => - selectedDateRange?.end != null ? _fmt.format(selectedDateRange!.end) : 'YYYY/MM/DD'; + selectedDateRange?.end != null + ? _fmt.format(selectedDateRange!.end) + : 'YYYY/MM/DD'; Future getApprovedTenders() async { isLoading = true; @@ -45,6 +47,35 @@ class YourTendersViewModel with ChangeNotifier { notifyListeners(); } + Future getTenders({ + required int limit, + required int offset, + required String createdFrom, + required String createdTo, + }) async { + isLoading = true; + notifyListeners(); + + final result = await yourTendersRepository.getTenders( + limit: limit, + offset: offset, + createdFrom: createdFrom, + createdTo: createdTo, + ); + switch (result) { + case Ok(): + data = result.value; + errorMessage = null; + break; + case Error(): + errorMessage = result.error.toString(); + break; + } + + isLoading = false; + notifyListeners(); + } + // --- Filter Actions --- void setDateRange(DateTimeRange? range) { selectedDateRange = range; diff --git a/lib/views/tenders/widgets/tender_card.dart b/lib/views/tenders/widgets/tender_card.dart index 13f5ce9..4b366f8 100644 --- a/lib/views/tenders/widgets/tender_card.dart +++ b/lib/views/tenders/widgets/tender_card.dart @@ -169,7 +169,8 @@ class TenderCard extends StatelessWidget { Widget _tenderDescription() { return Text( tender.description ?? '', - maxLines: 7, + maxLines: 4, + overflow: TextOverflow.ellipsis, style: TextStyle( color: AppColors.grey70, fontSize: 16.0.sp(), diff --git a/lib/views/your_tenders/pages/your_tenders_screen.dart b/lib/views/your_tenders/pages/your_tenders_screen.dart index 2554d3e..fbe2ecb 100644 --- a/lib/views/your_tenders/pages/your_tenders_screen.dart +++ b/lib/views/your_tenders/pages/your_tenders_screen.dart @@ -1,13 +1,35 @@ import 'package:flutter/material.dart'; -import 'package:tm_app/core/utils/size_config.dart'; +import 'package:provider/provider.dart'; +import 'package:tm_app/core/utils/size_config.dart'; +import 'package:tm_app/view_models/your_tenders_view_model.dart'; import 'package:tm_app/views/shared/responsive_builder.dart'; import 'package:tm_app/views/your_tenders/pages/your_tenders_desktop_page.dart'; import 'package:tm_app/views/your_tenders/pages/your_tenders_mobile_page.dart'; import 'package:tm_app/views/your_tenders/pages/your_tenders_tablet_page.dart'; -class YourTendersScreen extends StatelessWidget { +class YourTendersScreen extends StatefulWidget { const YourTendersScreen({super.key}); + @override + State createState() => _YourTendersScreenState(); +} + +class _YourTendersScreenState extends State { + late final YourTendersViewModel viewModel; + @override + void initState() { + super.initState(); + viewModel = context.read(); + WidgetsBinding.instance.addPostFrameCallback((_) { + viewModel.getTenders( + limit: 10, + offset: 0, + createdFrom: '', + createdTo: '', + ); + }); + } + @override Widget build(BuildContext context) { SizeConfig.init(context); diff --git a/lib/views/your_tenders/widgets/tenders_filter_dialog.dart b/lib/views/your_tenders/widgets/tenders_filter_dialog.dart index c3de0d8..6005777 100644 --- a/lib/views/your_tenders/widgets/tenders_filter_dialog.dart +++ b/lib/views/your_tenders/widgets/tenders_filter_dialog.dart @@ -64,7 +64,9 @@ class TendersFilterDialog extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - start == null ? YourTendersStrings.startDate : viewModel.startDateFormatted, + start == null + ? YourTendersStrings.startDate + : viewModel.startDateFormatted, style: TextStyle( fontSize: 16.0.sp(), fontWeight: FontWeight.w400, @@ -72,7 +74,9 @@ class TendersFilterDialog extends StatelessWidget { ), ), Text( - end == null ? YourTendersStrings.endDate : viewModel.endDateFormatted, + end == null + ? YourTendersStrings.endDate + : viewModel.endDateFormatted, style: TextStyle( fontSize: 16.0.sp(), fontWeight: FontWeight.w400, @@ -158,6 +162,18 @@ class TendersFilterDialog extends StatelessWidget { isEnabled: true, onPressed: () { context.pop(); + if (vm.selectedStatus == + YourTendersStrings.tendersSubmitted) { + vm.getApprovedTenders(); + } + if (vm.selectedStatus == YourTendersStrings.all) { + vm.getTenders( + limit: 10, + offset: 0, + createdFrom: vm.startDateFormatted, + createdTo: vm.endDateFormatted, + ); + } }, text: YourTendersStrings.confirm, textColor: AppColors.mainBlue, From 7b6f7d6dbd310f8978c7b6407ba1e422c0bf00eb Mon Sep 17 00:00:00 2001 From: amirrezaghabeli Date: Wed, 3 Sep 2025 09:19:39 +0330 Subject: [PATCH 2/3] your tenders apprvals filter --- lib/view_models/your_tenders_view_model.dart | 29 +++++++++++++++++++ .../pages/your_tenders_mobile_page.dart | 22 +++++++++++++- .../your_tenders/widgets/tender_card.dart | 3 +- .../widgets/tenders_filter_dialog.dart | 13 +-------- 4 files changed, 53 insertions(+), 14 deletions(-) diff --git a/lib/view_models/your_tenders_view_model.dart b/lib/view_models/your_tenders_view_model.dart index a92d0e8..e381234 100644 --- a/lib/view_models/your_tenders_view_model.dart +++ b/lib/view_models/your_tenders_view_model.dart @@ -27,6 +27,16 @@ class YourTendersViewModel with ChangeNotifier { ? _fmt.format(selectedDateRange!.end) : 'YYYY/MM/DD'; + int get startDateUnix => + selectedDateRange?.start != null + ? selectedDateRange!.start.millisecondsSinceEpoch ~/ 1000 + : 0; + + int get endDateUnix => + selectedDateRange?.end != null + ? selectedDateRange!.end.millisecondsSinceEpoch ~/ 1000 + : 0; + Future getApprovedTenders() async { isLoading = true; notifyListeners(); @@ -86,4 +96,23 @@ class YourTendersViewModel with ChangeNotifier { selectedStatus = status; notifyListeners(); } + + void clearFilter() { + selectedDateRange = null; + selectedStatus = 'All'; + notifyListeners(); + } + + Future callWithFilter() async { + if (selectedStatus == 'All') { + await getTenders( + limit: 10, + offset: 0, + createdFrom: startDateUnix.toString(), + createdTo: endDateUnix.toString(), + ); + } else if (selectedStatus == 'Tenders submitted') { + await getApprovedTenders(); + } + } } diff --git a/lib/views/your_tenders/pages/your_tenders_mobile_page.dart b/lib/views/your_tenders/pages/your_tenders_mobile_page.dart index 858f193..9395b56 100644 --- a/lib/views/your_tenders/pages/your_tenders_mobile_page.dart +++ b/lib/views/your_tenders/pages/your_tenders_mobile_page.dart @@ -7,9 +7,29 @@ import 'package:tm_app/views/your_tenders/strings/your_tenders_strings.dart'; import 'package:tm_app/views/your_tenders/widgets/filter_button.dart'; import 'package:tm_app/views/your_tenders/widgets/tenders_submitted.dart'; -class YourTendersMobilePage extends StatelessWidget { +class YourTendersMobilePage extends StatefulWidget { const YourTendersMobilePage({super.key}); + @override + State createState() => _YourTendersMobilePageState(); +} + +class _YourTendersMobilePageState extends State { + late final YourTendersViewModel viewModel; + @override + void initState() { + viewModel = context.read(); + super.initState(); + } + + @override + void dispose() { + WidgetsBinding.instance.addPostFrameCallback((_) { + viewModel.clearFilter(); + }); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/views/your_tenders/widgets/tender_card.dart b/lib/views/your_tenders/widgets/tender_card.dart index c8dee35..8beade1 100644 --- a/lib/views/your_tenders/widgets/tender_card.dart +++ b/lib/views/your_tenders/widgets/tender_card.dart @@ -1,6 +1,7 @@ import 'package:country_flags/country_flags.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:tm_app/core/utils/date_utils.dart'; import '../../../core/constants/assets.dart'; import '../../../core/theme/colors.dart'; @@ -63,7 +64,7 @@ class TenderCard extends StatelessWidget { children: [ // Date Text( - date, + unixToDate(int.parse(date)), style: TextStyle( color: AppColors.grey60, fontSize: 12.0.sp(), diff --git a/lib/views/your_tenders/widgets/tenders_filter_dialog.dart b/lib/views/your_tenders/widgets/tenders_filter_dialog.dart index 6005777..a0aa696 100644 --- a/lib/views/your_tenders/widgets/tenders_filter_dialog.dart +++ b/lib/views/your_tenders/widgets/tenders_filter_dialog.dart @@ -162,18 +162,7 @@ class TendersFilterDialog extends StatelessWidget { isEnabled: true, onPressed: () { context.pop(); - if (vm.selectedStatus == - YourTendersStrings.tendersSubmitted) { - vm.getApprovedTenders(); - } - if (vm.selectedStatus == YourTendersStrings.all) { - vm.getTenders( - limit: 10, - offset: 0, - createdFrom: vm.startDateFormatted, - createdTo: vm.endDateFormatted, - ); - } + vm.callWithFilter(); }, text: YourTendersStrings.confirm, textColor: AppColors.mainBlue, From 5cd0fcc3212a2f72e55691f5943a9ea5338d6755 Mon Sep 17 00:00:00 2001 From: amirrezaghabeli Date: Wed, 3 Sep 2025 09:20:54 +0330 Subject: [PATCH 3/3] fixed config --- lib/core/config/app_config.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/config/app_config.dart b/lib/core/config/app_config.dart index 7b8a654..77efb55 100644 --- a/lib/core/config/app_config.dart +++ b/lib/core/config/app_config.dart @@ -16,7 +16,7 @@ class AppConfig { // // Handle different platforms for local development if (kIsWeb) { // For web, use localhost - return 'http://localhost:8090'; + return 'https://app.opplens.com'; } else { // For Android emulator, use 10.0.2.2 (special IP for host machine) return 'http://10.0.2.2:8081';