From 34afaa9d4d729f3947c9e833e8464705297391cd Mon Sep 17 00:00:00 2001 From: llsajjad Date: Mon, 18 Aug 2025 08:23:05 +0330 Subject: [PATCH] Added data from api for recommended --- lib/core/constants/strings.dart | 2 + lib/data/repositories/home_repository.dart | 5 + lib/data/services/home_service.dart | 10 + .../recommended_tenders_data.dart | 23 ++ .../recommended_tenders_data.freezed.dart | 310 ++++++++++++++++ .../recommended_tenders_data.g.dart | 32 ++ .../recommended_tenders_response.dart | 25 ++ .../recommended_tenders_response.freezed.dart | 334 ++++++++++++++++++ .../recommended_tenders_response.g.dart | 33 ++ lib/view_models/home_view_model.dart | 26 ++ lib/views/home/pages/d_home_page.dart | 23 +- lib/views/home/pages/m_home_page.dart | 30 +- lib/views/home/pages/t_home_page.dart | 19 +- lib/views/home/tenders_list_item.dart | 143 +++++--- .../widgets/tender_card_progress_bar.dart | 49 +++ 15 files changed, 978 insertions(+), 86 deletions(-) create mode 100644 lib/data/services/model/recommended_tenders_data/recommended_tenders_data.dart create mode 100644 lib/data/services/model/recommended_tenders_data/recommended_tenders_data.freezed.dart create mode 100644 lib/data/services/model/recommended_tenders_data/recommended_tenders_data.g.dart create mode 100644 lib/data/services/model/recommended_tenders_response/recommended_tenders_response.dart create mode 100644 lib/data/services/model/recommended_tenders_response/recommended_tenders_response.freezed.dart create mode 100644 lib/data/services/model/recommended_tenders_response/recommended_tenders_response.g.dart create mode 100644 lib/views/home/widgets/tender_card_progress_bar.dart diff --git a/lib/core/constants/strings.dart b/lib/core/constants/strings.dart index be50e66..8d261bf 100644 --- a/lib/core/constants/strings.dart +++ b/lib/core/constants/strings.dart @@ -32,7 +32,9 @@ class AppStrings { static const String tenderValue = 'Tender Value'; static const String thunderStatus = 'Thunder status'; static const String yourTenders = 'Your tenders'; + static const String recommendation = 'Recommendation'; static const String welcome = 'Welcome'; + static const String tenderDeadline = 'Deadline'; //Tenders static const String tendersTitle = 'Tenders'; diff --git a/lib/data/repositories/home_repository.dart b/lib/data/repositories/home_repository.dart index e41e2b8..ad0e13d 100644 --- a/lib/data/repositories/home_repository.dart +++ b/lib/data/repositories/home_repository.dart @@ -2,6 +2,7 @@ import 'package:tm_app/core/utils/result.dart'; import 'package:tm_app/data/services/home_service.dart'; import 'package:tm_app/data/services/model/home/home_request/home_request_model.dart'; import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart'; +import 'package:tm_app/data/services/model/recommended_tenders_response/recommended_tenders_response.dart'; class HomeRepository { HomeRepository({required HomeService homeService}) @@ -13,4 +14,8 @@ class HomeRepository { }) async { return _homeService.getHome(homeRequestModel: homeRequestModel); } + + Future> getHomeRecommendedTenders() async { + return _homeService.getRecommendedTenders(); + } } diff --git a/lib/data/services/home_service.dart b/lib/data/services/home_service.dart index 86d2429..0dda149 100644 --- a/lib/data/services/home_service.dart +++ b/lib/data/services/home_service.dart @@ -6,6 +6,7 @@ import 'package:tm_app/core/utils/result.dart'; import 'package:tm_app/data/services/mock_data/home_mock_data.dart'; import 'package:tm_app/data/services/model/home/home_request/home_request_model.dart'; import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart'; +import 'package:tm_app/data/services/model/recommended_tenders_response/recommended_tenders_response.dart'; import '../../core/network/network_manager.dart'; @@ -37,4 +38,13 @@ class HomeService { return Result.error(Exception(e)); } } + + Future> getRecommendedTenders() async { + final result = await _networkManager.makeRequest( + '/api/v1/tenders/recommend', + method: 'GET', + (json) => RecommendedTendersResponse.fromJson(json), + ); + return result; + } } diff --git a/lib/data/services/model/recommended_tenders_data/recommended_tenders_data.dart b/lib/data/services/model/recommended_tenders_data/recommended_tenders_data.dart new file mode 100644 index 0000000..ed8c913 --- /dev/null +++ b/lib/data/services/model/recommended_tenders_data/recommended_tenders_data.dart @@ -0,0 +1,23 @@ +// ignore_for_file: invalid_annotation_target + +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:tm_app/data/services/model/meta/meta.dart'; +import 'package:tm_app/data/services/model/tender_data/tender_data.dart'; + +part 'recommended_tenders_data.freezed.dart'; +part 'recommended_tenders_data.g.dart'; + +@freezed +abstract class RecommendedTendersData with _$RecommendedTendersData { + @JsonSerializable(explicitToJson: true) + const factory +RecommendedTendersData({ + required List tenders, + required Meta? metadata, + + }) = _RecommendedTendersData; + + factory +RecommendedTendersData.fromJson(Map json) => + _$RecommendedTendersDataFromJson(json); +} diff --git a/lib/data/services/model/recommended_tenders_data/recommended_tenders_data.freezed.dart b/lib/data/services/model/recommended_tenders_data/recommended_tenders_data.freezed.dart new file mode 100644 index 0000000..71f4d1e --- /dev/null +++ b/lib/data/services/model/recommended_tenders_data/recommended_tenders_data.freezed.dart @@ -0,0 +1,310 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'recommended_tenders_data.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$RecommendedTendersData { + + List get tenders; Meta? get metadata; +/// Create a copy of RecommendedTendersData +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$RecommendedTendersDataCopyWith get copyWith => _$RecommendedTendersDataCopyWithImpl(this as RecommendedTendersData, _$identity); + + /// Serializes this RecommendedTendersData to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is RecommendedTendersData&&const DeepCollectionEquality().equals(other.tenders, tenders)&&(identical(other.metadata, metadata) || other.metadata == metadata)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(tenders),metadata); + +@override +String toString() { + return 'RecommendedTendersData(tenders: $tenders, metadata: $metadata)'; +} + + +} + +/// @nodoc +abstract mixin class $RecommendedTendersDataCopyWith<$Res> { + factory $RecommendedTendersDataCopyWith(RecommendedTendersData value, $Res Function(RecommendedTendersData) _then) = _$RecommendedTendersDataCopyWithImpl; +@useResult +$Res call({ + List tenders, Meta? metadata +}); + + +$MetaCopyWith<$Res>? get metadata; + +} +/// @nodoc +class _$RecommendedTendersDataCopyWithImpl<$Res> + implements $RecommendedTendersDataCopyWith<$Res> { + _$RecommendedTendersDataCopyWithImpl(this._self, this._then); + + final RecommendedTendersData _self; + final $Res Function(RecommendedTendersData) _then; + +/// Create a copy of RecommendedTendersData +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? tenders = null,Object? metadata = freezed,}) { + return _then(_self.copyWith( +tenders: null == tenders ? _self.tenders : tenders // ignore: cast_nullable_to_non_nullable +as List,metadata: freezed == metadata ? _self.metadata : metadata // ignore: cast_nullable_to_non_nullable +as Meta?, + )); +} +/// Create a copy of RecommendedTendersData +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$MetaCopyWith<$Res>? get metadata { + if (_self.metadata == null) { + return null; + } + + return $MetaCopyWith<$Res>(_self.metadata!, (value) { + return _then(_self.copyWith(metadata: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [RecommendedTendersData]. +extension RecommendedTendersDataPatterns on RecommendedTendersData { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _RecommendedTendersData value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _RecommendedTendersData() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _RecommendedTendersData value) $default,){ +final _that = this; +switch (_that) { +case _RecommendedTendersData(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _RecommendedTendersData value)? $default,){ +final _that = this; +switch (_that) { +case _RecommendedTendersData() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( List tenders, Meta? metadata)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _RecommendedTendersData() when $default != null: +return $default(_that.tenders,_that.metadata);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( List tenders, Meta? metadata) $default,) {final _that = this; +switch (_that) { +case _RecommendedTendersData(): +return $default(_that.tenders,_that.metadata);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( List tenders, Meta? metadata)? $default,) {final _that = this; +switch (_that) { +case _RecommendedTendersData() when $default != null: +return $default(_that.tenders,_that.metadata);case _: + return null; + +} +} + +} + +/// @nodoc + +@JsonSerializable(explicitToJson: true) +class _RecommendedTendersData implements RecommendedTendersData { + const _RecommendedTendersData({required final List tenders, required this.metadata}): _tenders = tenders; + factory _RecommendedTendersData.fromJson(Map json) => _$RecommendedTendersDataFromJson(json); + + final List _tenders; +@override List get tenders { + if (_tenders is EqualUnmodifiableListView) return _tenders; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_tenders); +} + +@override final Meta? metadata; + +/// Create a copy of RecommendedTendersData +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$RecommendedTendersDataCopyWith<_RecommendedTendersData> get copyWith => __$RecommendedTendersDataCopyWithImpl<_RecommendedTendersData>(this, _$identity); + +@override +Map toJson() { + return _$RecommendedTendersDataToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _RecommendedTendersData&&const DeepCollectionEquality().equals(other._tenders, _tenders)&&(identical(other.metadata, metadata) || other.metadata == metadata)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_tenders),metadata); + +@override +String toString() { + return 'RecommendedTendersData(tenders: $tenders, metadata: $metadata)'; +} + + +} + +/// @nodoc +abstract mixin class _$RecommendedTendersDataCopyWith<$Res> implements $RecommendedTendersDataCopyWith<$Res> { + factory _$RecommendedTendersDataCopyWith(_RecommendedTendersData value, $Res Function(_RecommendedTendersData) _then) = __$RecommendedTendersDataCopyWithImpl; +@override @useResult +$Res call({ + List tenders, Meta? metadata +}); + + +@override $MetaCopyWith<$Res>? get metadata; + +} +/// @nodoc +class __$RecommendedTendersDataCopyWithImpl<$Res> + implements _$RecommendedTendersDataCopyWith<$Res> { + __$RecommendedTendersDataCopyWithImpl(this._self, this._then); + + final _RecommendedTendersData _self; + final $Res Function(_RecommendedTendersData) _then; + +/// Create a copy of RecommendedTendersData +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? tenders = null,Object? metadata = freezed,}) { + return _then(_RecommendedTendersData( +tenders: null == tenders ? _self._tenders : tenders // ignore: cast_nullable_to_non_nullable +as List,metadata: freezed == metadata ? _self.metadata : metadata // ignore: cast_nullable_to_non_nullable +as Meta?, + )); +} + +/// Create a copy of RecommendedTendersData +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$MetaCopyWith<$Res>? get metadata { + if (_self.metadata == null) { + return null; + } + + return $MetaCopyWith<$Res>(_self.metadata!, (value) { + return _then(_self.copyWith(metadata: value)); + }); +} +} + +// dart format on diff --git a/lib/data/services/model/recommended_tenders_data/recommended_tenders_data.g.dart b/lib/data/services/model/recommended_tenders_data/recommended_tenders_data.g.dart new file mode 100644 index 0000000..6162317 --- /dev/null +++ b/lib/data/services/model/recommended_tenders_data/recommended_tenders_data.g.dart @@ -0,0 +1,32 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'recommended_tenders_data.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_RecommendedTendersData _$RecommendedTendersDataFromJson( + Map json, +) => _RecommendedTendersData( + tenders: + (json['tenders'] as List) + .map( + (e) => + e == null + ? null + : TenderData.fromJson(e as Map), + ) + .toList(), + metadata: + json['metadata'] == null + ? null + : Meta.fromJson(json['metadata'] as Map), +); + +Map _$RecommendedTendersDataToJson( + _RecommendedTendersData instance, +) => { + 'tenders': instance.tenders.map((e) => e?.toJson()).toList(), + 'metadata': instance.metadata?.toJson(), +}; diff --git a/lib/data/services/model/recommended_tenders_response/recommended_tenders_response.dart b/lib/data/services/model/recommended_tenders_response/recommended_tenders_response.dart new file mode 100644 index 0000000..20e79d5 --- /dev/null +++ b/lib/data/services/model/recommended_tenders_response/recommended_tenders_response.dart @@ -0,0 +1,25 @@ +// ignore_for_file: invalid_annotation_target + +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:tm_app/data/services/model/error/error_model.dart'; +import 'package:tm_app/data/services/model/recommended_tenders_data/recommended_tenders_data.dart'; + +part 'recommended_tenders_response.freezed.dart'; +part 'recommended_tenders_response.g.dart'; + +@freezed +abstract class RecommendedTendersResponse with _$RecommendedTendersResponse { + @JsonSerializable(explicitToJson: true) + const factory +RecommendedTendersResponse({ + required bool? succes, + required String? message, + required ErrorModel? error, + required RecommendedTendersData? data, + + }) = _RecommendedTendersResponse; + + factory +RecommendedTendersResponse.fromJson(Map json) => + _$RecommendedTendersResponseFromJson(json); +} diff --git a/lib/data/services/model/recommended_tenders_response/recommended_tenders_response.freezed.dart b/lib/data/services/model/recommended_tenders_response/recommended_tenders_response.freezed.dart new file mode 100644 index 0000000..02b0f29 --- /dev/null +++ b/lib/data/services/model/recommended_tenders_response/recommended_tenders_response.freezed.dart @@ -0,0 +1,334 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'recommended_tenders_response.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$RecommendedTendersResponse { + + bool? get succes; String? get message; ErrorModel? get error; RecommendedTendersData? get data; +/// Create a copy of RecommendedTendersResponse +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$RecommendedTendersResponseCopyWith get copyWith => _$RecommendedTendersResponseCopyWithImpl(this as RecommendedTendersResponse, _$identity); + + /// Serializes this RecommendedTendersResponse to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is RecommendedTendersResponse&&(identical(other.succes, succes) || other.succes == succes)&&(identical(other.message, message) || other.message == message)&&(identical(other.error, error) || other.error == error)&&(identical(other.data, data) || other.data == data)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,succes,message,error,data); + +@override +String toString() { + return 'RecommendedTendersResponse(succes: $succes, message: $message, error: $error, data: $data)'; +} + + +} + +/// @nodoc +abstract mixin class $RecommendedTendersResponseCopyWith<$Res> { + factory $RecommendedTendersResponseCopyWith(RecommendedTendersResponse value, $Res Function(RecommendedTendersResponse) _then) = _$RecommendedTendersResponseCopyWithImpl; +@useResult +$Res call({ + bool? succes, String? message, ErrorModel? error, RecommendedTendersData? data +}); + + +$ErrorModelCopyWith<$Res>? get error;$RecommendedTendersDataCopyWith<$Res>? get data; + +} +/// @nodoc +class _$RecommendedTendersResponseCopyWithImpl<$Res> + implements $RecommendedTendersResponseCopyWith<$Res> { + _$RecommendedTendersResponseCopyWithImpl(this._self, this._then); + + final RecommendedTendersResponse _self; + final $Res Function(RecommendedTendersResponse) _then; + +/// Create a copy of RecommendedTendersResponse +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? succes = freezed,Object? message = freezed,Object? error = freezed,Object? data = freezed,}) { + return _then(_self.copyWith( +succes: freezed == succes ? _self.succes : succes // ignore: cast_nullable_to_non_nullable +as bool?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable +as ErrorModel?,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable +as RecommendedTendersData?, + )); +} +/// Create a copy of RecommendedTendersResponse +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ErrorModelCopyWith<$Res>? get error { + if (_self.error == null) { + return null; + } + + return $ErrorModelCopyWith<$Res>(_self.error!, (value) { + return _then(_self.copyWith(error: value)); + }); +}/// Create a copy of RecommendedTendersResponse +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$RecommendedTendersDataCopyWith<$Res>? get data { + if (_self.data == null) { + return null; + } + + return $RecommendedTendersDataCopyWith<$Res>(_self.data!, (value) { + return _then(_self.copyWith(data: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [RecommendedTendersResponse]. +extension RecommendedTendersResponsePatterns on RecommendedTendersResponse { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _RecommendedTendersResponse value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _RecommendedTendersResponse() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _RecommendedTendersResponse value) $default,){ +final _that = this; +switch (_that) { +case _RecommendedTendersResponse(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _RecommendedTendersResponse value)? $default,){ +final _that = this; +switch (_that) { +case _RecommendedTendersResponse() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( bool? succes, String? message, ErrorModel? error, RecommendedTendersData? data)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _RecommendedTendersResponse() when $default != null: +return $default(_that.succes,_that.message,_that.error,_that.data);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( bool? succes, String? message, ErrorModel? error, RecommendedTendersData? data) $default,) {final _that = this; +switch (_that) { +case _RecommendedTendersResponse(): +return $default(_that.succes,_that.message,_that.error,_that.data);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool? succes, String? message, ErrorModel? error, RecommendedTendersData? data)? $default,) {final _that = this; +switch (_that) { +case _RecommendedTendersResponse() when $default != null: +return $default(_that.succes,_that.message,_that.error,_that.data);case _: + return null; + +} +} + +} + +/// @nodoc + +@JsonSerializable(explicitToJson: true) +class _RecommendedTendersResponse implements RecommendedTendersResponse { + const _RecommendedTendersResponse({required this.succes, required this.message, required this.error, required this.data}); + factory _RecommendedTendersResponse.fromJson(Map json) => _$RecommendedTendersResponseFromJson(json); + +@override final bool? succes; +@override final String? message; +@override final ErrorModel? error; +@override final RecommendedTendersData? data; + +/// Create a copy of RecommendedTendersResponse +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$RecommendedTendersResponseCopyWith<_RecommendedTendersResponse> get copyWith => __$RecommendedTendersResponseCopyWithImpl<_RecommendedTendersResponse>(this, _$identity); + +@override +Map toJson() { + return _$RecommendedTendersResponseToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _RecommendedTendersResponse&&(identical(other.succes, succes) || other.succes == succes)&&(identical(other.message, message) || other.message == message)&&(identical(other.error, error) || other.error == error)&&(identical(other.data, data) || other.data == data)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,succes,message,error,data); + +@override +String toString() { + return 'RecommendedTendersResponse(succes: $succes, message: $message, error: $error, data: $data)'; +} + + +} + +/// @nodoc +abstract mixin class _$RecommendedTendersResponseCopyWith<$Res> implements $RecommendedTendersResponseCopyWith<$Res> { + factory _$RecommendedTendersResponseCopyWith(_RecommendedTendersResponse value, $Res Function(_RecommendedTendersResponse) _then) = __$RecommendedTendersResponseCopyWithImpl; +@override @useResult +$Res call({ + bool? succes, String? message, ErrorModel? error, RecommendedTendersData? data +}); + + +@override $ErrorModelCopyWith<$Res>? get error;@override $RecommendedTendersDataCopyWith<$Res>? get data; + +} +/// @nodoc +class __$RecommendedTendersResponseCopyWithImpl<$Res> + implements _$RecommendedTendersResponseCopyWith<$Res> { + __$RecommendedTendersResponseCopyWithImpl(this._self, this._then); + + final _RecommendedTendersResponse _self; + final $Res Function(_RecommendedTendersResponse) _then; + +/// Create a copy of RecommendedTendersResponse +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? succes = freezed,Object? message = freezed,Object? error = freezed,Object? data = freezed,}) { + return _then(_RecommendedTendersResponse( +succes: freezed == succes ? _self.succes : succes // ignore: cast_nullable_to_non_nullable +as bool?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable +as String?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable +as ErrorModel?,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable +as RecommendedTendersData?, + )); +} + +/// Create a copy of RecommendedTendersResponse +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$ErrorModelCopyWith<$Res>? get error { + if (_self.error == null) { + return null; + } + + return $ErrorModelCopyWith<$Res>(_self.error!, (value) { + return _then(_self.copyWith(error: value)); + }); +}/// Create a copy of RecommendedTendersResponse +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$RecommendedTendersDataCopyWith<$Res>? get data { + if (_self.data == null) { + return null; + } + + return $RecommendedTendersDataCopyWith<$Res>(_self.data!, (value) { + return _then(_self.copyWith(data: value)); + }); +} +} + +// dart format on diff --git a/lib/data/services/model/recommended_tenders_response/recommended_tenders_response.g.dart b/lib/data/services/model/recommended_tenders_response/recommended_tenders_response.g.dart new file mode 100644 index 0000000..20c1d78 --- /dev/null +++ b/lib/data/services/model/recommended_tenders_response/recommended_tenders_response.g.dart @@ -0,0 +1,33 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'recommended_tenders_response.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_RecommendedTendersResponse _$RecommendedTendersResponseFromJson( + Map json, +) => _RecommendedTendersResponse( + succes: json['succes'] as bool?, + message: json['message'] as String?, + error: + json['error'] == null + ? null + : ErrorModel.fromJson(json['error'] as Map), + data: + json['data'] == null + ? null + : RecommendedTendersData.fromJson( + json['data'] as Map, + ), +); + +Map _$RecommendedTendersResponseToJson( + _RecommendedTendersResponse instance, +) => { + 'succes': instance.succes, + 'message': instance.message, + 'error': instance.error?.toJson(), + 'data': instance.data?.toJson(), +}; diff --git a/lib/view_models/home_view_model.dart b/lib/view_models/home_view_model.dart index 43d2334..7b6daff 100644 --- a/lib/view_models/home_view_model.dart +++ b/lib/view_models/home_view_model.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:tm_app/core/utils/result.dart'; import 'package:tm_app/data/services/model/home/home_request/home_request_model.dart'; +import 'package:tm_app/data/services/model/recommended_tenders_response/recommended_tenders_response.dart'; import '../data/repositories/home_repository.dart'; import '../data/services/model/home/home_response/home_response_model.dart'; @@ -11,6 +12,7 @@ class HomeViewModel with ChangeNotifier { HomeViewModel({required HomeRepository homeRepository}) : _homeRepository = homeRepository { getHome(homeRequestModel: HomeRequestModel(id: '1')); + getHomeRecommandTenders(); } bool _isLoading = false; @@ -21,6 +23,10 @@ class HomeViewModel with ChangeNotifier { String? get errorMessage => _errorMessage; HomeResponseModel? get homeResponse => _homeResponse; + RecommendedTendersResponse? _recommendedTendersResponse; + RecommendedTendersResponse? get recommendedTendersResponse => + _recommendedTendersResponse; + Future getHome({HomeRequestModel? homeRequestModel}) async { _isLoading = true; _errorMessage = null; @@ -42,4 +48,24 @@ class HomeViewModel with ChangeNotifier { _isLoading = false; notifyListeners(); } + + Future getHomeRecommandTenders() async { + _isLoading = true; + _errorMessage = null; + notifyListeners(); + + final result = await _homeRepository.getHomeRecommendedTenders(); + + switch (result) { + case Ok(): + _recommendedTendersResponse = result.value; + break; + case Error(): + _errorMessage = result.error.toString(); + break; + } + + _isLoading = false; + notifyListeners(); + } } diff --git a/lib/views/home/pages/d_home_page.dart b/lib/views/home/pages/d_home_page.dart index ead3d1c..84ae07a 100644 --- a/lib/views/home/pages/d_home_page.dart +++ b/lib/views/home/pages/d_home_page.dart @@ -5,6 +5,7 @@ import 'package:tm_app/core/routes/app_routes.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart'; +import 'package:tm_app/data/services/model/recommended_tenders_response/recommended_tenders_response.dart'; import 'package:tm_app/view_models/home_view_model.dart'; import 'package:tm_app/views/home/widgets/notification_card.dart'; @@ -41,7 +42,7 @@ class DesktopHomePage extends StatelessWidget { SizedBox(height: 32.0.h()), _yourTenderText(), SizedBox(height: 8.0.h()), - _bottomListView(homeViewModel.homeResponse!), + _bottomListView(homeViewModel.recommendedTendersResponse!), ], ), ); @@ -150,27 +151,31 @@ class DesktopHomePage extends StatelessWidget { child: Align( alignment: Alignment.centerLeft, child: Text( - AppStrings.yourTenders, + AppStrings.recommendation, style: TextStyle( - fontSize: 20.0.sp(), - fontWeight: FontWeight.w600, - color: AppColors.grey60, + fontSize: 18.0.sp(), + fontWeight: FontWeight.w700, + color: AppColors.grey80, ), ), ), ); } - Widget _bottomListView(HomeResponseModel homeResponse) { + Widget _bottomListView( + RecommendedTendersResponse recommendedTendersResponse, + ) { return SizedBox( width: 740, - height: 290.0, + height: 387.0, child: ListView.builder( padding: EdgeInsets.only(bottom: 20), - itemCount: homeResponse.yourTenders?.length ?? 0, + itemCount: recommendedTendersResponse.data!.tenders.length, scrollDirection: Axis.horizontal, itemBuilder: (context, index) { - return TendersListItem(tender: homeResponse.yourTenders![index]); + return TendersListItem( + tender: recommendedTendersResponse.data!.tenders[index]!, + ); }, ), ); diff --git a/lib/views/home/pages/m_home_page.dart b/lib/views/home/pages/m_home_page.dart index b740d42..35d1658 100644 --- a/lib/views/home/pages/m_home_page.dart +++ b/lib/views/home/pages/m_home_page.dart @@ -6,6 +6,7 @@ import 'package:tm_app/core/routes/app_routes.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart'; +import 'package:tm_app/data/services/model/recommended_tenders_response/recommended_tenders_response.dart'; import 'package:tm_app/view_models/home_view_model.dart'; import 'package:tm_app/views/home/widgets/notification_card.dart'; import 'package:tm_app/views/shared/tender_app_bar.dart'; @@ -43,10 +44,9 @@ class MobileHomePage extends StatelessWidget { _firstTenderCardsRow(context, homeViewModel.homeResponse!), SizedBox(height: 8.0.h()), _secondTenderCardsRow(homeViewModel.homeResponse!), - SizedBox(height: 32.0.h()), + SizedBox(height: 40.0.h()), _yourTenderText(), - SizedBox(height: 8.0.h()), - _bottomListView(homeViewModel.homeResponse!), + _bottomListView(homeViewModel.recommendedTendersResponse!), ], ), ); @@ -150,26 +150,30 @@ class MobileHomePage extends StatelessWidget { return Padding( padding: EdgeInsets.symmetric(horizontal: 24.0.w()), child: Text( - AppStrings.yourTenders, + AppStrings.recommendation, style: TextStyle( - fontSize: 20.0.sp(), - fontWeight: FontWeight.w600, - color: AppColors.grey60, + fontSize: 18.0.sp(), + fontWeight: FontWeight.w700, + color: AppColors.grey80, ), ), ); } - Widget _bottomListView(HomeResponseModel homeResponse) { - return SizedBox( + Widget _bottomListView( + RecommendedTendersResponse recommendedTendersResponse, + ) { + return Container( + constraints: BoxConstraints(maxHeight: 387.0.h()), width: double.infinity, - height: 286.0.h(), child: ListView.builder( - padding: EdgeInsets.symmetric(horizontal: 24.0.w(), vertical: 16.0.h()), - itemCount: homeResponse.yourTenders?.length ?? 0, + padding: EdgeInsets.symmetric(horizontal: 24.0.w(), vertical: 15.0.h()), + itemCount: recommendedTendersResponse.data!.tenders.length, scrollDirection: Axis.horizontal, itemBuilder: (context, index) { - return TendersListItem(tender: homeResponse.yourTenders![index]); + return TendersListItem( + tender: recommendedTendersResponse.data!.tenders[index]!, + ); }, ), ); diff --git a/lib/views/home/pages/t_home_page.dart b/lib/views/home/pages/t_home_page.dart index cfc13b6..8c75bfd 100644 --- a/lib/views/home/pages/t_home_page.dart +++ b/lib/views/home/pages/t_home_page.dart @@ -5,6 +5,7 @@ import 'package:tm_app/core/routes/app_routes.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart'; +import 'package:tm_app/data/services/model/recommended_tenders_response/recommended_tenders_response.dart'; import 'package:tm_app/view_models/home_view_model.dart'; import 'package:tm_app/views/home/widgets/notification_card.dart'; @@ -49,7 +50,7 @@ class TabletHomePage extends StatelessWidget { SizedBox(height: 32.0.h()), _yourTenderText(), SizedBox(height: 8.0.h()), - _bottomListView(homeViewModel.homeResponse!), + _bottomListView(homeViewModel.recommendedTendersResponse!), ], ), ), @@ -170,24 +171,24 @@ class TabletHomePage extends StatelessWidget { Widget _yourTenderText() { return Text( - AppStrings.yourTenders, + AppStrings.recommendation, style: TextStyle( - fontSize: 20.0.sp(), - fontWeight: FontWeight.w600, - color: AppColors.grey60, + fontSize: 18.0.sp(), + fontWeight: FontWeight.w700, + color: AppColors.grey80, ), ); } - Widget _bottomListView(HomeResponseModel homeResponse) { + Widget _bottomListView(RecommendedTendersResponse recommendedTendersResponse) { return SizedBox( width: double.infinity, - height: 310.0.h(), + height: 387.0.h(), child: ListView.builder( - itemCount: homeResponse.yourTenders?.length ?? 0, + itemCount: recommendedTendersResponse.data!.tenders.length, scrollDirection: Axis.horizontal, itemBuilder: (context, index) { - return TendersListItem(tender: homeResponse.yourTenders![index]); + return TendersListItem(tender: recommendedTendersResponse.data!.tenders[index]!); }, ), ); diff --git a/lib/views/home/tenders_list_item.dart b/lib/views/home/tenders_list_item.dart index 914ce3d..18c49ec 100644 --- a/lib/views/home/tenders_list_item.dart +++ b/lib/views/home/tenders_list_item.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:tm_app/data/services/model/tender/tender_model.dart'; +import 'package:tm_app/core/constants/strings.dart'; +import 'package:tm_app/data/services/model/tender_data/tender_data.dart'; +import 'package:tm_app/views/home/widgets/tender_card_progress_bar.dart'; import '../../core/constants/assets.dart'; import '../../core/theme/colors.dart'; @@ -8,7 +10,7 @@ import '../../core/utils/size_config.dart'; class TendersListItem extends StatelessWidget { const TendersListItem({required this.tender, super.key}); - final TenderModel tender; + final TenderData tender; @override Widget build(BuildContext context) { @@ -16,71 +18,93 @@ class TendersListItem extends StatelessWidget { decoration: BoxDecoration( color: AppColors.cardBackground, borderRadius: BorderRadius.circular(4), + border: Border.all(color: AppColors.grey30), ), margin: EdgeInsetsDirectional.only(end: 16.0.w()), width: 320.0.w(), - height: 280.0.h(), child: Padding( padding: EdgeInsets.symmetric(horizontal: 16.0.w(), vertical: 16.0.h()), child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - tender.createdTime ?? '', - style: TextStyle( - fontSize: 12.0.sp(), - fontWeight: FontWeight.w400, - color: AppColors.grey60, - ), + SizedBox( + width: double.infinity, + child: Text( + timeConvertor(tender.publicationDate!), + style: TextStyle( + fontSize: 12.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey60, ), - Container( - width: 91.0.w(), - height: 24.0.h(), - decoration: BoxDecoration( - color: AppColors.green20, - borderRadius: BorderRadius.circular(8), - ), - alignment: Alignment.center, - child: Text( - tender.status ?? '', + ), + ), + SizedBox(height: 10.0.h()), + Container( + padding: EdgeInsets.symmetric( + horizontal: 10.0.w(), + vertical: 5.0.h(), + ), + decoration: BoxDecoration( + color: AppColors.primary2, + borderRadius: BorderRadius.circular(4), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '${AppStrings.tenderDeadline} :', style: TextStyle( - fontSize: 12.0.sp(), + fontSize: 15.0.sp(), fontWeight: FontWeight.w500, - color: AppColors.green30, + color: AppColors.textBlue, ), ), - ), - ], + Text( + timeConvertor(tender.tenderDeadline!), + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w500, + color: AppColors.grey80, + ), + ), + ], + ), ), SizedBox(height: 12.0.h()), Text( - tender.title ?? '', + tender.title!, style: TextStyle( + overflow: TextOverflow.ellipsis, fontSize: 16.0.sp(), fontWeight: FontWeight.w600, color: AppColors.grey80, ), ), SizedBox(height: 8.0.h()), - Text( - tender.description ?? '', - maxLines: 3, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 14.0.sp(), - fontWeight: FontWeight.w400, - color: AppColors.grey70, + SizedBox( + width: double.infinity, + child: Text( + tender.description!, + textAlign: TextAlign.left, + maxLines: 6, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey70, + ), ), ), - SizedBox(height: 24.0.h()), + SizedBox(height: 18.0.h()), + TenderCardProgressBar(), + SizedBox(height: 15.0.h()), + Row( children: [ SvgPicture.asset(AssetsManager.location), SizedBox(width: 1.0.w()), Text( - tender.location ?? '', + tender.countryCode!, style: TextStyle( fontSize: 12.0.sp(), fontWeight: FontWeight.w400, @@ -94,23 +118,23 @@ class TendersListItem extends StatelessWidget { child: Image.asset(AssetsManager.seFlag, fit: BoxFit.cover), ), Spacer(), - Container( - width: 96.0.w(), - height: 24.0.h(), - decoration: BoxDecoration( - color: AppColors.grey30, - borderRadius: BorderRadius.circular(88), - ), - alignment: Alignment.center, - child: Text( - tender.type ?? '', - style: TextStyle( - fontSize: 12.0.sp(), - fontWeight: FontWeight.w500, - color: AppColors.grey60, - ), - ), - ), + // Container( + // width: 96.0.w(), + // height: 24.0.h(), + // decoration: BoxDecoration( + // color: AppColors.grey30, + // borderRadius: BorderRadius.circular(88), + // ), + // alignment: Alignment.center, + // child: Text( + // 'tender.type', + // style: TextStyle( + // fontSize: 12.0.sp(), + // fontWeight: FontWeight.w500, + // color: AppColors.grey60, + // ), + // ), + // ), ], ), ], @@ -119,3 +143,12 @@ class TendersListItem extends StatelessWidget { ); } } + +String timeConvertor(int time) { + DateTime date = DateTime.fromMillisecondsSinceEpoch(time); + + String formattedDate = + "${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}"; + + return formattedDate; +} diff --git a/lib/views/home/widgets/tender_card_progress_bar.dart b/lib/views/home/widgets/tender_card_progress_bar.dart new file mode 100644 index 0000000..a7460bb --- /dev/null +++ b/lib/views/home/widgets/tender_card_progress_bar.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:tm_app/core/constants/strings.dart'; +import 'package:tm_app/core/theme/colors.dart'; +import 'package:tm_app/core/utils/size_config.dart'; + +class TenderCardProgressBar extends StatelessWidget { + const TenderCardProgressBar({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + AppStrings.tenderMatchProfile, + style: TextStyle( + fontSize: 12.0.sp(), + color: AppColors.grey80, + fontWeight: FontWeight.w400, + ), + ), + Text( + AppStrings.tenderMatchPercentageExample, + style: TextStyle( + fontSize: 12.0.sp(), + fontWeight: FontWeight.w600, + color: AppColors.secondaryTextColor, + ), + ), + ], + ), + SizedBox(height: 6.0.h()), + + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: LinearProgressIndicator( + value: 0.75, + backgroundColor: AppColors.grey20, + valueColor: AlwaysStoppedAnimation(AppColors.secondary50), + minHeight: 6.0.h(), + ), + ), + ], + ); + } +}