Merge pull request 'your_tenders_changes' (#91) from your_tenders_changes into main

Reviewed-on: https://repo.ravanertebat.com/TM/tm_app/pulls/91
This commit is contained in:
a.ghabeli
2025-09-03 09:21:15 +03:30
9 changed files with 172 additions and 15 deletions
@@ -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'; import 'package:tm_app/data/services/your_tenders_service.dart';
class YourTendersRepository { class YourTendersRepository {
final YourTendersService yourTendersService; final YourTendersService _yourTendersService;
YourTendersRepository({required this.yourTendersService}); YourTendersRepository({required YourTendersService yourTendersService})
: _yourTendersService = yourTendersService;
Future<Result<TenderApprovalsResponse>> getApprovedTenders() { Future<Result<TenderApprovalsResponse>> getApprovedTenders() {
return yourTendersService.getApprovedTenders(); return _yourTendersService.getApprovedTenders();
}
Future<Result<TenderApprovalsResponse>> getTenders({
required int limit,
required int offset,
required String createdFrom,
required String createdTo,
}) {
return _yourTendersService.getTenders(
limit: limit,
offset: offset,
createdFrom: createdFrom,
createdTo: createdTo,
);
} }
} }
@@ -1,4 +1,18 @@
class YourTendersApi { class YourTendersApi {
static const String tenderApprovalsStatus = static const String tenderApprovalsStatus =
'/api/v1/tender-approvals?status=submitted'; '/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';
}
} }
@@ -19,4 +19,23 @@ class YourTendersService {
return result; return result;
} }
Future<Result<TenderApprovalsResponse>> 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;
}
} }
+65 -5
View File
@@ -6,9 +6,7 @@ import 'package:tm_app/data/services/model/tender_approvals_response/tender_appr
class YourTendersViewModel with ChangeNotifier { class YourTendersViewModel with ChangeNotifier {
final YourTendersRepository yourTendersRepository; final YourTendersRepository yourTendersRepository;
YourTendersViewModel({required this.yourTendersRepository}) { YourTendersViewModel({required this.yourTendersRepository});
getApprovedTenders();
}
bool isLoading = false; bool isLoading = false;
String? errorMessage; String? errorMessage;
@@ -20,10 +18,24 @@ class YourTendersViewModel with ChangeNotifier {
final _fmt = DateFormat('yyyy/MM/dd'); final _fmt = DateFormat('yyyy/MM/dd');
String get startDateFormatted => 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 => String get endDateFormatted =>
selectedDateRange?.end != null ? _fmt.format(selectedDateRange!.end) : 'YYYY/MM/DD'; selectedDateRange?.end != null
? _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<void> getApprovedTenders() async { Future<void> getApprovedTenders() async {
isLoading = true; isLoading = true;
@@ -45,6 +57,35 @@ class YourTendersViewModel with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
Future<void> 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<TenderApprovalsResponse>():
data = result.value;
errorMessage = null;
break;
case Error<TenderApprovalsResponse>():
errorMessage = result.error.toString();
break;
}
isLoading = false;
notifyListeners();
}
// --- Filter Actions --- // --- Filter Actions ---
void setDateRange(DateTimeRange? range) { void setDateRange(DateTimeRange? range) {
selectedDateRange = range; selectedDateRange = range;
@@ -55,4 +96,23 @@ class YourTendersViewModel with ChangeNotifier {
selectedStatus = status; selectedStatus = status;
notifyListeners(); notifyListeners();
} }
void clearFilter() {
selectedDateRange = null;
selectedStatus = 'All';
notifyListeners();
}
Future<void> callWithFilter() async {
if (selectedStatus == 'All') {
await getTenders(
limit: 10,
offset: 0,
createdFrom: startDateUnix.toString(),
createdTo: endDateUnix.toString(),
);
} else if (selectedStatus == 'Tenders submitted') {
await getApprovedTenders();
}
}
} }
+2 -1
View File
@@ -169,7 +169,8 @@ class TenderCard extends StatelessWidget {
Widget _tenderDescription() { Widget _tenderDescription() {
return Text( return Text(
tender.description ?? '', tender.description ?? '',
maxLines: 7, maxLines: 4,
overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(
color: AppColors.grey70, color: AppColors.grey70,
fontSize: 16.0.sp(), fontSize: 16.0.sp(),
@@ -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/filter_button.dart';
import 'package:tm_app/views/your_tenders/widgets/tenders_submitted.dart'; import 'package:tm_app/views/your_tenders/widgets/tenders_submitted.dart';
class YourTendersMobilePage extends StatelessWidget { class YourTendersMobilePage extends StatefulWidget {
const YourTendersMobilePage({super.key}); const YourTendersMobilePage({super.key});
@override
State<YourTendersMobilePage> createState() => _YourTendersMobilePageState();
}
class _YourTendersMobilePageState extends State<YourTendersMobilePage> {
late final YourTendersViewModel viewModel;
@override
void initState() {
viewModel = context.read<YourTendersViewModel>();
super.initState();
}
@override
void dispose() {
WidgetsBinding.instance.addPostFrameCallback((_) {
viewModel.clearFilter();
});
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@@ -1,13 +1,35 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tm_app/core/utils/size_config.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/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_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_mobile_page.dart';
import 'package:tm_app/views/your_tenders/pages/your_tenders_tablet_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}); const YourTendersScreen({super.key});
@override
State<YourTendersScreen> createState() => _YourTendersScreenState();
}
class _YourTendersScreenState extends State<YourTendersScreen> {
late final YourTendersViewModel viewModel;
@override
void initState() {
super.initState();
viewModel = context.read<YourTendersViewModel>();
WidgetsBinding.instance.addPostFrameCallback((_) {
viewModel.getTenders(
limit: 10,
offset: 0,
createdFrom: '',
createdTo: '',
);
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
SizeConfig.init(context); SizeConfig.init(context);
@@ -1,6 +1,7 @@
import 'package:country_flags/country_flags.dart'; import 'package:country_flags/country_flags.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:tm_app/core/utils/date_utils.dart';
import '../../../core/constants/assets.dart'; import '../../../core/constants/assets.dart';
import '../../../core/theme/colors.dart'; import '../../../core/theme/colors.dart';
@@ -63,7 +64,7 @@ class TenderCard extends StatelessWidget {
children: [ children: [
// Date // Date
Text( Text(
date, unixToDate(int.parse(date)),
style: TextStyle( style: TextStyle(
color: AppColors.grey60, color: AppColors.grey60,
fontSize: 12.0.sp(), fontSize: 12.0.sp(),
@@ -64,7 +64,9 @@ class TendersFilterDialog extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
start == null ? YourTendersStrings.startDate : viewModel.startDateFormatted, start == null
? YourTendersStrings.startDate
: viewModel.startDateFormatted,
style: TextStyle( style: TextStyle(
fontSize: 16.0.sp(), fontSize: 16.0.sp(),
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
@@ -72,7 +74,9 @@ class TendersFilterDialog extends StatelessWidget {
), ),
), ),
Text( Text(
end == null ? YourTendersStrings.endDate : viewModel.endDateFormatted, end == null
? YourTendersStrings.endDate
: viewModel.endDateFormatted,
style: TextStyle( style: TextStyle(
fontSize: 16.0.sp(), fontSize: 16.0.sp(),
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
@@ -158,6 +162,7 @@ class TendersFilterDialog extends StatelessWidget {
isEnabled: true, isEnabled: true,
onPressed: () { onPressed: () {
context.pop(); context.pop();
vm.callWithFilter();
}, },
text: YourTendersStrings.confirm, text: YourTendersStrings.confirm,
textColor: AppColors.mainBlue, textColor: AppColors.mainBlue,