Merge pull request 'feedback stats added' (#73) from feedback_stat into main

Reviewed-on: https://repo.ravanertebat.com/TM/tm_app/pulls/73
This commit is contained in:
a.ghabeli
2025-08-27 09:41:10 +03:30
15 changed files with 793 additions and 18 deletions
+4
View File
@@ -0,0 +1,4 @@
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="15.666" cy="17" r="12" fill="#24848E"/>
<path d="M29.666 14C29.666 20.0766 24.6983 25 18.666 25C12.5894 25 7.66602 20.0766 7.66602 14C7.66602 7.96774 12.5894 3 18.666 3C24.6983 3 29.666 7.96774 29.666 14ZM17.3797 19.8548L25.541 11.6935C25.8071 11.4274 25.8071 10.9395 25.541 10.6734L24.5209 9.69758C24.2547 9.3871 23.8112 9.3871 23.545 9.69758L16.8918 16.3508L13.7426 13.246C13.4765 12.9355 13.033 12.9355 12.7668 13.246L11.7467 14.2218C11.4805 14.4879 11.4805 14.9758 11.7467 15.2419L16.3596 19.8548C16.6257 20.121 17.1136 20.121 17.3797 19.8548Z" fill="#99DDE5"/>
</svg>

After

Width:  |  Height:  |  Size: 681 B

+4
View File
@@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.4031 2.66996C15.9698 2.71724 16.636 2.80781 17.2782 2.97854C18.6545 3.34439 19.3715 4.71231 19.4579 6.13972C19.5666 7.9374 19.5007 9.76346 19.4111 11.1126C21.5493 10.9208 23.6703 10.7691 25.5795 10.6776C27.5952 10.5811 29.2814 12.1077 29.3225 14.1343C29.3901 17.4706 29.179 22.5503 27.652 26.6647C27.0525 28.2801 25.4638 29.2333 23.7483 29.2893C15.72 29.5517 8 28.4821 8 28.4821V12.5701C8.89627 12.4013 9.93513 12.2325 11.0698 12.0685C12.0847 9.2222 13.0445 5.88882 13.5565 4.04104C13.7897 3.19912 14.5361 2.59761 15.4031 2.66996Z" fill="#9E9E9E"/>
<path d="M2.76118 28.2027C2.77899 28.9068 3.17598 29.5463 3.85621 29.7292C4.39368 29.8737 5.11976 30.0026 5.99935 30.0026C6.87895 30.0026 7.60502 29.8737 8.14248 29.7292C8.82275 29.5463 9.21968 28.9068 9.23755 28.2027C9.27515 26.7155 9.33268 23.9106 9.33268 20.6693C9.33268 17.4279 9.27515 14.623 9.23755 13.1359C9.21968 12.4317 8.82275 11.7922 8.14248 11.6093C7.60502 11.4649 6.87895 11.3359 5.99935 11.3359C5.11976 11.3359 4.39368 11.4649 3.85621 11.6093C3.17598 11.7922 2.77899 12.4317 2.76118 13.1359C2.72356 14.623 2.66602 17.4279 2.66602 20.6693C2.66602 23.9106 2.72356 26.7155 2.76118 28.2027Z" fill="#444444"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+2
View File
@@ -44,6 +44,8 @@ class AssetsManager {
static const notification = 'assets/icons/notification.svg';
static const calendar = 'assets/icons/calendar.svg';
static const menu = 'assets/icons/menu.svg';
static const thumbLike = 'assets/icons/thumb_like.svg';
static const approve = 'assets/icons/approve.svg';
//your tenders page
static const tickCircle = 'assets/icons/tick-circle.svg';
@@ -6,6 +6,8 @@ import 'package:tm_app/data/services/model/recommended_tenders_response/recommen
import 'package:tm_app/data/services/model/tender_approvals_response/tender_approvals_response.dart';
import 'package:tm_app/data/services/model/tender_approvals_state_response/tender_approvals_state_response.dart';
import '../services/model/feedback_stats_response/feedback_stat_response.dart';
class HomeRepository {
HomeRepository({required HomeService homeService})
: _homeService = homeService;
@@ -23,10 +25,16 @@ class HomeRepository {
}) async {
return _homeService.getRecommendedTenders(limit: limit, offset: offset);
}
Future<Result<TenderApprovalsResponse>> getYourTenders() {
return _homeService.getYourTenders();
}
Future<Result<TenderApprovalsStateResponse>> getTendersApprovalsState() {
return _homeService.getTendersApprovalsState();
}
Future<Result<FeedbackStatResponse>> getFeedbackStats() {
return _homeService.getFeedbackStats();
}
}
+10
View File
@@ -4,6 +4,7 @@ import 'package:dio/dio.dart';
import 'package:tm_app/core/utils/logger.dart';
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/feedback_stats_response/feedback_stat_response.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';
@@ -73,4 +74,13 @@ class HomeService {
return result;
}
Future<Result<FeedbackStatResponse>> getFeedbackStats() async {
final result = await _networkManager.makeRequest(
'/api/v1/feedback/stats/company',
(json) => FeedbackStatResponse.fromJson(json),
method: 'GET',
);
return result;
}
}
@@ -0,0 +1,20 @@
// ignore_for_file: invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart';
part 'feedback_stats.freezed.dart';
part 'feedback_stats.g.dart';
@freezed
abstract class FeedbackStats with _$FeedbackStats {
const factory FeedbackStats({
@JsonKey(name: 'company_id') required String? companyId,
@JsonKey(name: 'last_updated') required int? lastUpdated,
@JsonKey(name: 'total_dislikes') required int? totalDislikes,
@JsonKey(name: 'total_feedback') required int? totalFeedback,
@JsonKey(name: 'total_likes') required int? totalLikes,
}) = _FeedbackStats;
factory FeedbackStats.fromJson(Map<String, Object?> json) =>
_$FeedbackStatsFromJson(json);
}
@@ -0,0 +1,289 @@
// 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 'feedback_stats.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$FeedbackStats {
@JsonKey(name: 'company_id') String? get companyId;@JsonKey(name: 'last_updated') int? get lastUpdated;@JsonKey(name: 'total_dislikes') int? get totalDislikes;@JsonKey(name: 'total_feedback') int? get totalFeedback;@JsonKey(name: 'total_likes') int? get totalLikes;
/// Create a copy of FeedbackStats
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$FeedbackStatsCopyWith<FeedbackStats> get copyWith => _$FeedbackStatsCopyWithImpl<FeedbackStats>(this as FeedbackStats, _$identity);
/// Serializes this FeedbackStats to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is FeedbackStats&&(identical(other.companyId, companyId) || other.companyId == companyId)&&(identical(other.lastUpdated, lastUpdated) || other.lastUpdated == lastUpdated)&&(identical(other.totalDislikes, totalDislikes) || other.totalDislikes == totalDislikes)&&(identical(other.totalFeedback, totalFeedback) || other.totalFeedback == totalFeedback)&&(identical(other.totalLikes, totalLikes) || other.totalLikes == totalLikes));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,companyId,lastUpdated,totalDislikes,totalFeedback,totalLikes);
@override
String toString() {
return 'FeedbackStats(companyId: $companyId, lastUpdated: $lastUpdated, totalDislikes: $totalDislikes, totalFeedback: $totalFeedback, totalLikes: $totalLikes)';
}
}
/// @nodoc
abstract mixin class $FeedbackStatsCopyWith<$Res> {
factory $FeedbackStatsCopyWith(FeedbackStats value, $Res Function(FeedbackStats) _then) = _$FeedbackStatsCopyWithImpl;
@useResult
$Res call({
@JsonKey(name: 'company_id') String? companyId,@JsonKey(name: 'last_updated') int? lastUpdated,@JsonKey(name: 'total_dislikes') int? totalDislikes,@JsonKey(name: 'total_feedback') int? totalFeedback,@JsonKey(name: 'total_likes') int? totalLikes
});
}
/// @nodoc
class _$FeedbackStatsCopyWithImpl<$Res>
implements $FeedbackStatsCopyWith<$Res> {
_$FeedbackStatsCopyWithImpl(this._self, this._then);
final FeedbackStats _self;
final $Res Function(FeedbackStats) _then;
/// Create a copy of FeedbackStats
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? companyId = freezed,Object? lastUpdated = freezed,Object? totalDislikes = freezed,Object? totalFeedback = freezed,Object? totalLikes = freezed,}) {
return _then(_self.copyWith(
companyId: freezed == companyId ? _self.companyId : companyId // ignore: cast_nullable_to_non_nullable
as String?,lastUpdated: freezed == lastUpdated ? _self.lastUpdated : lastUpdated // ignore: cast_nullable_to_non_nullable
as int?,totalDislikes: freezed == totalDislikes ? _self.totalDislikes : totalDislikes // ignore: cast_nullable_to_non_nullable
as int?,totalFeedback: freezed == totalFeedback ? _self.totalFeedback : totalFeedback // ignore: cast_nullable_to_non_nullable
as int?,totalLikes: freezed == totalLikes ? _self.totalLikes : totalLikes // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
/// Adds pattern-matching-related methods to [FeedbackStats].
extension FeedbackStatsPatterns on FeedbackStats {
/// 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 extends Object?>(TResult Function( _FeedbackStats value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _FeedbackStats() 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 extends Object?>(TResult Function( _FeedbackStats value) $default,){
final _that = this;
switch (_that) {
case _FeedbackStats():
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 extends Object?>(TResult? Function( _FeedbackStats value)? $default,){
final _that = this;
switch (_that) {
case _FeedbackStats() 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 extends Object?>(TResult Function(@JsonKey(name: 'company_id') String? companyId, @JsonKey(name: 'last_updated') int? lastUpdated, @JsonKey(name: 'total_dislikes') int? totalDislikes, @JsonKey(name: 'total_feedback') int? totalFeedback, @JsonKey(name: 'total_likes') int? totalLikes)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _FeedbackStats() when $default != null:
return $default(_that.companyId,_that.lastUpdated,_that.totalDislikes,_that.totalFeedback,_that.totalLikes);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 extends Object?>(TResult Function(@JsonKey(name: 'company_id') String? companyId, @JsonKey(name: 'last_updated') int? lastUpdated, @JsonKey(name: 'total_dislikes') int? totalDislikes, @JsonKey(name: 'total_feedback') int? totalFeedback, @JsonKey(name: 'total_likes') int? totalLikes) $default,) {final _that = this;
switch (_that) {
case _FeedbackStats():
return $default(_that.companyId,_that.lastUpdated,_that.totalDislikes,_that.totalFeedback,_that.totalLikes);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 extends Object?>(TResult? Function(@JsonKey(name: 'company_id') String? companyId, @JsonKey(name: 'last_updated') int? lastUpdated, @JsonKey(name: 'total_dislikes') int? totalDislikes, @JsonKey(name: 'total_feedback') int? totalFeedback, @JsonKey(name: 'total_likes') int? totalLikes)? $default,) {final _that = this;
switch (_that) {
case _FeedbackStats() when $default != null:
return $default(_that.companyId,_that.lastUpdated,_that.totalDislikes,_that.totalFeedback,_that.totalLikes);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _FeedbackStats implements FeedbackStats {
const _FeedbackStats({@JsonKey(name: 'company_id') required this.companyId, @JsonKey(name: 'last_updated') required this.lastUpdated, @JsonKey(name: 'total_dislikes') required this.totalDislikes, @JsonKey(name: 'total_feedback') required this.totalFeedback, @JsonKey(name: 'total_likes') required this.totalLikes});
factory _FeedbackStats.fromJson(Map<String, dynamic> json) => _$FeedbackStatsFromJson(json);
@override@JsonKey(name: 'company_id') final String? companyId;
@override@JsonKey(name: 'last_updated') final int? lastUpdated;
@override@JsonKey(name: 'total_dislikes') final int? totalDislikes;
@override@JsonKey(name: 'total_feedback') final int? totalFeedback;
@override@JsonKey(name: 'total_likes') final int? totalLikes;
/// Create a copy of FeedbackStats
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$FeedbackStatsCopyWith<_FeedbackStats> get copyWith => __$FeedbackStatsCopyWithImpl<_FeedbackStats>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$FeedbackStatsToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _FeedbackStats&&(identical(other.companyId, companyId) || other.companyId == companyId)&&(identical(other.lastUpdated, lastUpdated) || other.lastUpdated == lastUpdated)&&(identical(other.totalDislikes, totalDislikes) || other.totalDislikes == totalDislikes)&&(identical(other.totalFeedback, totalFeedback) || other.totalFeedback == totalFeedback)&&(identical(other.totalLikes, totalLikes) || other.totalLikes == totalLikes));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,companyId,lastUpdated,totalDislikes,totalFeedback,totalLikes);
@override
String toString() {
return 'FeedbackStats(companyId: $companyId, lastUpdated: $lastUpdated, totalDislikes: $totalDislikes, totalFeedback: $totalFeedback, totalLikes: $totalLikes)';
}
}
/// @nodoc
abstract mixin class _$FeedbackStatsCopyWith<$Res> implements $FeedbackStatsCopyWith<$Res> {
factory _$FeedbackStatsCopyWith(_FeedbackStats value, $Res Function(_FeedbackStats) _then) = __$FeedbackStatsCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(name: 'company_id') String? companyId,@JsonKey(name: 'last_updated') int? lastUpdated,@JsonKey(name: 'total_dislikes') int? totalDislikes,@JsonKey(name: 'total_feedback') int? totalFeedback,@JsonKey(name: 'total_likes') int? totalLikes
});
}
/// @nodoc
class __$FeedbackStatsCopyWithImpl<$Res>
implements _$FeedbackStatsCopyWith<$Res> {
__$FeedbackStatsCopyWithImpl(this._self, this._then);
final _FeedbackStats _self;
final $Res Function(_FeedbackStats) _then;
/// Create a copy of FeedbackStats
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? companyId = freezed,Object? lastUpdated = freezed,Object? totalDislikes = freezed,Object? totalFeedback = freezed,Object? totalLikes = freezed,}) {
return _then(_FeedbackStats(
companyId: freezed == companyId ? _self.companyId : companyId // ignore: cast_nullable_to_non_nullable
as String?,lastUpdated: freezed == lastUpdated ? _self.lastUpdated : lastUpdated // ignore: cast_nullable_to_non_nullable
as int?,totalDislikes: freezed == totalDislikes ? _self.totalDislikes : totalDislikes // ignore: cast_nullable_to_non_nullable
as int?,totalFeedback: freezed == totalFeedback ? _self.totalFeedback : totalFeedback // ignore: cast_nullable_to_non_nullable
as int?,totalLikes: freezed == totalLikes ? _self.totalLikes : totalLikes // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
// dart format on
@@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'feedback_stats.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_FeedbackStats _$FeedbackStatsFromJson(Map<String, dynamic> json) =>
_FeedbackStats(
companyId: json['company_id'] as String?,
lastUpdated: (json['last_updated'] as num?)?.toInt(),
totalDislikes: (json['total_dislikes'] as num?)?.toInt(),
totalFeedback: (json['total_feedback'] as num?)?.toInt(),
totalLikes: (json['total_likes'] as num?)?.toInt(),
);
Map<String, dynamic> _$FeedbackStatsToJson(_FeedbackStats instance) =>
<String, dynamic>{
'company_id': instance.companyId,
'last_updated': instance.lastUpdated,
'total_dislikes': instance.totalDislikes,
'total_feedback': instance.totalFeedback,
'total_likes': instance.totalLikes,
};
@@ -0,0 +1,22 @@
// ignore_for_file: invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart';
import '../error/error_model.dart';
import '../feedback_stats/feedback_stats.dart';
part 'feedback_stat_response.freezed.dart';
part 'feedback_stat_response.g.dart';
@freezed
abstract class FeedbackStatResponse with _$FeedbackStatResponse {
const factory FeedbackStatResponse({
required String? message,
required bool? success,
required ErrorModel? error,
required FeedbackStats? data,
}) = _FeedbackStatResponse;
factory FeedbackStatResponse.fromJson(Map<String, Object?> json) =>
_$FeedbackStatResponseFromJson(json);
}
@@ -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 'feedback_stat_response.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$FeedbackStatResponse {
String? get message; bool? get success; ErrorModel? get error; FeedbackStats? get data;
/// Create a copy of FeedbackStatResponse
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$FeedbackStatResponseCopyWith<FeedbackStatResponse> get copyWith => _$FeedbackStatResponseCopyWithImpl<FeedbackStatResponse>(this as FeedbackStatResponse, _$identity);
/// Serializes this FeedbackStatResponse to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is FeedbackStatResponse&&(identical(other.message, message) || other.message == message)&&(identical(other.success, success) || other.success == success)&&(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,message,success,error,data);
@override
String toString() {
return 'FeedbackStatResponse(message: $message, success: $success, error: $error, data: $data)';
}
}
/// @nodoc
abstract mixin class $FeedbackStatResponseCopyWith<$Res> {
factory $FeedbackStatResponseCopyWith(FeedbackStatResponse value, $Res Function(FeedbackStatResponse) _then) = _$FeedbackStatResponseCopyWithImpl;
@useResult
$Res call({
String? message, bool? success, ErrorModel? error, FeedbackStats? data
});
$ErrorModelCopyWith<$Res>? get error;$FeedbackStatsCopyWith<$Res>? get data;
}
/// @nodoc
class _$FeedbackStatResponseCopyWithImpl<$Res>
implements $FeedbackStatResponseCopyWith<$Res> {
_$FeedbackStatResponseCopyWithImpl(this._self, this._then);
final FeedbackStatResponse _self;
final $Res Function(FeedbackStatResponse) _then;
/// Create a copy of FeedbackStatResponse
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? message = freezed,Object? success = freezed,Object? error = freezed,Object? data = freezed,}) {
return _then(_self.copyWith(
message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
as String?,success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as bool?,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 FeedbackStats?,
));
}
/// Create a copy of FeedbackStatResponse
/// 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 FeedbackStatResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$FeedbackStatsCopyWith<$Res>? get data {
if (_self.data == null) {
return null;
}
return $FeedbackStatsCopyWith<$Res>(_self.data!, (value) {
return _then(_self.copyWith(data: value));
});
}
}
/// Adds pattern-matching-related methods to [FeedbackStatResponse].
extension FeedbackStatResponsePatterns on FeedbackStatResponse {
/// 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 extends Object?>(TResult Function( _FeedbackStatResponse value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _FeedbackStatResponse() 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 extends Object?>(TResult Function( _FeedbackStatResponse value) $default,){
final _that = this;
switch (_that) {
case _FeedbackStatResponse():
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 extends Object?>(TResult? Function( _FeedbackStatResponse value)? $default,){
final _that = this;
switch (_that) {
case _FeedbackStatResponse() 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 extends Object?>(TResult Function( String? message, bool? success, ErrorModel? error, FeedbackStats? data)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _FeedbackStatResponse() when $default != null:
return $default(_that.message,_that.success,_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 extends Object?>(TResult Function( String? message, bool? success, ErrorModel? error, FeedbackStats? data) $default,) {final _that = this;
switch (_that) {
case _FeedbackStatResponse():
return $default(_that.message,_that.success,_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 extends Object?>(TResult? Function( String? message, bool? success, ErrorModel? error, FeedbackStats? data)? $default,) {final _that = this;
switch (_that) {
case _FeedbackStatResponse() when $default != null:
return $default(_that.message,_that.success,_that.error,_that.data);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _FeedbackStatResponse implements FeedbackStatResponse {
const _FeedbackStatResponse({required this.message, required this.success, required this.error, required this.data});
factory _FeedbackStatResponse.fromJson(Map<String, dynamic> json) => _$FeedbackStatResponseFromJson(json);
@override final String? message;
@override final bool? success;
@override final ErrorModel? error;
@override final FeedbackStats? data;
/// Create a copy of FeedbackStatResponse
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$FeedbackStatResponseCopyWith<_FeedbackStatResponse> get copyWith => __$FeedbackStatResponseCopyWithImpl<_FeedbackStatResponse>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$FeedbackStatResponseToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _FeedbackStatResponse&&(identical(other.message, message) || other.message == message)&&(identical(other.success, success) || other.success == success)&&(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,message,success,error,data);
@override
String toString() {
return 'FeedbackStatResponse(message: $message, success: $success, error: $error, data: $data)';
}
}
/// @nodoc
abstract mixin class _$FeedbackStatResponseCopyWith<$Res> implements $FeedbackStatResponseCopyWith<$Res> {
factory _$FeedbackStatResponseCopyWith(_FeedbackStatResponse value, $Res Function(_FeedbackStatResponse) _then) = __$FeedbackStatResponseCopyWithImpl;
@override @useResult
$Res call({
String? message, bool? success, ErrorModel? error, FeedbackStats? data
});
@override $ErrorModelCopyWith<$Res>? get error;@override $FeedbackStatsCopyWith<$Res>? get data;
}
/// @nodoc
class __$FeedbackStatResponseCopyWithImpl<$Res>
implements _$FeedbackStatResponseCopyWith<$Res> {
__$FeedbackStatResponseCopyWithImpl(this._self, this._then);
final _FeedbackStatResponse _self;
final $Res Function(_FeedbackStatResponse) _then;
/// Create a copy of FeedbackStatResponse
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? message = freezed,Object? success = freezed,Object? error = freezed,Object? data = freezed,}) {
return _then(_FeedbackStatResponse(
message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
as String?,success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as bool?,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 FeedbackStats?,
));
}
/// Create a copy of FeedbackStatResponse
/// 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 FeedbackStatResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$FeedbackStatsCopyWith<$Res>? get data {
if (_self.data == null) {
return null;
}
return $FeedbackStatsCopyWith<$Res>(_self.data!, (value) {
return _then(_self.copyWith(data: value));
});
}
}
// dart format on
@@ -0,0 +1,31 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'feedback_stat_response.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_FeedbackStatResponse _$FeedbackStatResponseFromJson(
Map<String, dynamic> json,
) => _FeedbackStatResponse(
message: json['message'] as String?,
success: json['success'] as bool?,
error:
json['error'] == null
? null
: ErrorModel.fromJson(json['error'] as Map<String, dynamic>),
data:
json['data'] == null
? null
: FeedbackStats.fromJson(json['data'] as Map<String, dynamic>),
);
Map<String, dynamic> _$FeedbackStatResponseToJson(
_FeedbackStatResponse instance,
) => <String, dynamic>{
'message': instance.message,
'success': instance.success,
'error': instance.error,
'data': instance.data,
};
+24 -1
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/data/services/model/feedback_stats/feedback_stats.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 'package:tm_app/data/services/model/tender_approvals_response/tender_approvals_response.dart';
@@ -7,6 +8,7 @@ import 'package:tm_app/data/services/model/tender_approvals_state_response/tende
import 'package:tm_app/data/services/model/tender_data/tender_data.dart';
import '../data/repositories/home_repository.dart';
import '../data/services/model/feedback_stats_response/feedback_stat_response.dart';
import '../data/services/model/home/home_response/home_response_model.dart';
class HomeViewModel with ChangeNotifier {
@@ -28,7 +30,7 @@ class HomeViewModel with ChangeNotifier {
RecommendedTendersResponse? _recommendedTendersResponse;
TenderApprovalsStateResponse? tenderApprovalsStateResponse;
FeedbackStats? feedbackStats;
final List<TenderData> _tenders = [];
final int _pageSize = 20;
int _currentPage = 0;
@@ -46,6 +48,7 @@ class HomeViewModel with ChangeNotifier {
void init() async {
await getApprovedStates();
await getYourTenders();
await getFeedbackStats();
}
Future<void> getHome({HomeRequestModel? homeRequestModel}) async {
@@ -163,4 +166,24 @@ class HomeViewModel with ChangeNotifier {
_isLoading = false;
notifyListeners();
}
Future<void> getFeedbackStats() async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
final result = await _homeRepository.getFeedbackStats();
switch (result) {
case Ok<FeedbackStatResponse>():
feedbackStats = result.value.data;
break;
case Error<FeedbackStatResponse>():
_errorMessage = result.error.toString();
break;
}
_isLoading = false;
notifyListeners();
}
}
+6 -3
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'package:tm_app/core/constants/assets.dart';
import 'package:tm_app/core/constants/strings.dart';
import 'package:tm_app/core/routes/app_routes.dart';
import 'package:tm_app/core/theme/colors.dart';
@@ -70,6 +71,7 @@ class DesktopHomePage extends StatelessWidget {
child: _firstTenderCardsRow(
context,
homeViewModel.tenderApprovalsStateResponse!,
homeViewModel,
),
),
SizedBox(height: 32.0.h()),
@@ -133,6 +135,7 @@ class DesktopHomePage extends StatelessWidget {
Widget _firstTenderCardsRow(
BuildContext context,
TenderApprovalsStateResponse tenderApprovalsStateResponse,
HomeViewModel homeViewModel,
) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
@@ -162,7 +165,7 @@ class DesktopHomePage extends StatelessWidget {
Expanded(
child: TenderCard(
backgroundColor: AppColors.primary10,
iconPath: 'assets/icons/thumb.svg',
iconPath: AssetsManager.approve,
title: AppStrings.approvedTenders,
amount: '0',
textColor: Color(0xFF24848E),
@@ -189,9 +192,9 @@ class DesktopHomePage extends StatelessWidget {
SizedBox(width: 10),
TenderCard(
backgroundColor: AppColors.grey10,
iconPath: 'assets/icons/edit.svg',
iconPath: AssetsManager.thumbLike,
title: AppStrings.likedTenders,
amount: '2',
amount: homeViewModel.feedbackStats?.totalLikes.toString() ?? '0',
textColor: Color(0xFF9E9E9E),
enableTap: false,
width: 178,
+5 -5
View File
@@ -46,7 +46,7 @@ class MobileHomePage extends StatelessWidget {
homeViewModel.tenderApprovalsStateResponse!,
),
SizedBox(height: 8.0.h()),
_secondTenderCardsRow(),
_secondTenderCardsRow(homeViewModel),
SizedBox(height: 40.0.h()),
_yourTenderText(homeViewModel),
_bottomListView(homeViewModel),
@@ -130,7 +130,7 @@ class MobileHomePage extends StatelessWidget {
),
TenderCard(
backgroundColor: AppColors.primary10,
iconPath: AssetsManager.thumb,
iconPath: AssetsManager.approve,
title: AppStrings.approvedTenders,
amount: '0',
@@ -146,7 +146,7 @@ class MobileHomePage extends StatelessWidget {
}
// ignore: unused_element
Widget _secondTenderCardsRow() {
Widget _secondTenderCardsRow(HomeViewModel homeViewModel) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
child: Row(
@@ -163,9 +163,9 @@ class MobileHomePage extends StatelessWidget {
),
TenderCard(
backgroundColor: AppColors.grey10,
iconPath: AssetsManager.edit,
iconPath: AssetsManager.thumbLike,
title: AppStrings.likedTenders,
amount: '2',
amount: homeViewModel.feedbackStats?.totalLikes.toString() ?? '0',
textColor: Color(0xFF9E9E9E),
enableTap: false,
onTap: () {},
+5 -5
View File
@@ -101,7 +101,7 @@ class TabletHomePage extends StatelessWidget {
homeViewModel.tenderApprovalsStateResponse!,
),
SizedBox(height: 8.0.h()),
_secondTenderCardsRow(),
_secondTenderCardsRow(homeViewModel),
SizedBox(height: 32.0.h()),
_yourTenderText(homeViewModel),
SizedBox(height: 8.0.h()),
@@ -192,7 +192,7 @@ class TabletHomePage extends StatelessWidget {
Expanded(
child: TenderCard(
backgroundColor: AppColors.primary10,
iconPath: 'assets/icons/thumb.svg',
iconPath: AssetsManager.approve,
title: AppStrings.approvedTenders,
amount: '0',
@@ -210,7 +210,7 @@ class TabletHomePage extends StatelessWidget {
}
// ignore: unused_element
Widget _secondTenderCardsRow() {
Widget _secondTenderCardsRow(HomeViewModel homeViewModel) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@@ -231,9 +231,9 @@ class TabletHomePage extends StatelessWidget {
Expanded(
child: TenderCard(
backgroundColor: AppColors.grey10,
iconPath: 'assets/icons/edit.svg',
iconPath: AssetsManager.thumbLike,
title: AppStrings.likedTenders,
amount: '2',
amount: homeViewModel.feedbackStats?.totalLikes.toString() ?? '0',
textColor: Color(0xFF9E9E9E),
enableTap: false,
width: double.infinity,