Merge pull request 'Add Docker support and enhance data models for tender details' (#226) from milliseconds-timestamps into develop

Reviewed-on: https://repo.ravanertebat.com/TM/tm_app/pulls/226
This commit is contained in:
a.jamali
2026-06-06 14:37:42 +03:30
36 changed files with 1318 additions and 958 deletions
+16
View File
@@ -0,0 +1,16 @@
build
.dart_tool
.git
.gitignore
.drone.yml
.vscode
.idea
.flutter-plugins-dependencies
README.md
*.md
Dockerfile
Dockerfile-old
.dockerignore
artifacts
ios
android
+22 -2
View File
@@ -1,3 +1,23 @@
FROM docker-mirror.ravanertebat.ir/hub/nginx:1.25.2-alpine3.18-slim FROM docker-mirror.ravanertebat.ir/ghcr/cirruslabs/flutter:3.38.10 AS base
COPY ./artifacts/flutter/tm_pwa /usr/share/nginx/html WORKDIR /app
RUN flutter config --enable-web
FROM base AS deps
COPY pubspec.yaml pubspec.lock ./
RUN flutter pub get
FROM base AS builder
# Pull in the resolved package config from deps so `flutter pub get` is not
# re-run when only source files change.
COPY --from=deps /app/.dart_tool ./.dart_tool
COPY . .
RUN flutter build web --release --output=build/tm_pwa
FROM docker-mirror.ravanertebat.ir/hub/nginx:1.25.2-alpine3.18-slim AS runner
COPY --from=builder /app/build/tm_pwa /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
+1 -19
View File
@@ -47,7 +47,6 @@ mixin _$LoginScreenRoute on GoRouteData {
RouteBase get $splashScreenRoute => GoRouteData.$route( RouteBase get $splashScreenRoute => GoRouteData.$route(
path: '/splash', path: '/splash',
factory: _$SplashScreenRoute._fromState, factory: _$SplashScreenRoute._fromState,
); );
@@ -111,40 +110,33 @@ RouteBase get $appShellRouteData => StatefulShellRouteData.$route(
branches: [ branches: [
StatefulShellBranchData.$branch( StatefulShellBranchData.$branch(
navigatorKey: HomeBranch.$navigatorKey, navigatorKey: HomeBranch.$navigatorKey,
routes: [ routes: [
GoRouteData.$route(path: '/home', factory: _$HomeRouteData._fromState), GoRouteData.$route(path: '/home', factory: _$HomeRouteData._fromState),
], ],
), ),
StatefulShellBranchData.$branch( StatefulShellBranchData.$branch(
navigatorKey: TendersBranch.$navigatorKey, navigatorKey: TendersBranch.$navigatorKey,
routes: [ routes: [
GoRouteData.$route( GoRouteData.$route(
path: '/tenders', path: '/tenders',
factory: _$TendersRouteData._fromState, factory: _$TendersRouteData._fromState,
), ),
], ],
), ),
StatefulShellBranchData.$branch( StatefulShellBranchData.$branch(
navigatorKey: NotificationBranch.$navigatorKey, navigatorKey: NotificationBranch.$navigatorKey,
routes: [ routes: [
GoRouteData.$route( GoRouteData.$route(
path: '/notification', path: '/notification',
factory: _$NotificationRouteData._fromState, factory: _$NotificationRouteData._fromState,
), ),
], ],
), ),
StatefulShellBranchData.$branch( StatefulShellBranchData.$branch(
navigatorKey: ProfileBranch.$navigatorKey, navigatorKey: ProfileBranch.$navigatorKey,
routes: [ routes: [
GoRouteData.$route( GoRouteData.$route(
path: '/profile', path: '/profile',
factory: _$ProfileRouteData._fromState, factory: _$ProfileRouteData._fromState,
), ),
], ],
@@ -242,7 +234,6 @@ mixin _$ProfileRouteData on GoRouteData {
RouteBase get $yourTendersRouteData => GoRouteData.$route( RouteBase get $yourTendersRouteData => GoRouteData.$route(
path: '/your-tenders', path: '/your-tenders',
factory: _$YourTendersRouteData._fromState, factory: _$YourTendersRouteData._fromState,
); );
@@ -274,7 +265,6 @@ mixin _$YourTendersRouteData on GoRouteData {
RouteBase get $finalCompletionOfDocumentsRouteData => GoRouteData.$route( RouteBase get $finalCompletionOfDocumentsRouteData => GoRouteData.$route(
path: '/final_completion_of_documents', path: '/final_completion_of_documents',
factory: _$FinalCompletionOfDocumentsRouteData._fromState, factory: _$FinalCompletionOfDocumentsRouteData._fromState,
); );
@@ -302,7 +292,6 @@ mixin _$FinalCompletionOfDocumentsRouteData on GoRouteData {
RouteBase get $completionOfDocumentsRouteData => GoRouteData.$route( RouteBase get $completionOfDocumentsRouteData => GoRouteData.$route(
path: '/completion_of_documents', path: '/completion_of_documents',
factory: _$CompletionOfDocumentsRouteData._fromState, factory: _$CompletionOfDocumentsRouteData._fromState,
); );
@@ -318,9 +307,7 @@ mixin _$CompletionOfDocumentsRouteData on GoRouteData {
@override @override
String get location => GoRouteData.$location( String get location => GoRouteData.$location(
'/completion_of_documents', '/completion_of_documents',
queryParams: { queryParams: {if (_self.tenderId != null) 'tender-id': _self.tenderId},
if (_self.tenderId != null) 'tender-id': _self.tenderId!,
},
); );
@override @override
@@ -339,7 +326,6 @@ mixin _$CompletionOfDocumentsRouteData on GoRouteData {
RouteBase get $likedTendersRouteData => GoRouteData.$route( RouteBase get $likedTendersRouteData => GoRouteData.$route(
path: '/like-tenders', path: '/like-tenders',
factory: _$LikedTendersRouteData._fromState, factory: _$LikedTendersRouteData._fromState,
); );
@@ -390,7 +376,6 @@ mixin _$BoardRouteData on GoRouteData {
RouteBase get $tenderDetailRouteData => GoRouteData.$route( RouteBase get $tenderDetailRouteData => GoRouteData.$route(
path: '/tender-detail', path: '/tender-detail',
factory: _$TenderDetailRouteData._fromState, factory: _$TenderDetailRouteData._fromState,
); );
@@ -422,7 +407,6 @@ mixin _$TenderDetailRouteData on GoRouteData {
RouteBase get $forgotPasswordRouteData => GoRouteData.$route( RouteBase get $forgotPasswordRouteData => GoRouteData.$route(
path: '/forgot-password', path: '/forgot-password',
factory: _$ForgotPasswordRouteData._fromState, factory: _$ForgotPasswordRouteData._fromState,
); );
@@ -449,7 +433,6 @@ mixin _$ForgotPasswordRouteData on GoRouteData {
RouteBase get $forgotPasswordOtpRouteData => GoRouteData.$route( RouteBase get $forgotPasswordOtpRouteData => GoRouteData.$route(
path: '/forgot-password-otp', path: '/forgot-password-otp',
factory: _$ForgotPasswordOtpRouteData._fromState, factory: _$ForgotPasswordOtpRouteData._fromState,
); );
@@ -481,7 +464,6 @@ mixin _$ForgotPasswordOtpRouteData on GoRouteData {
RouteBase get $forgotPasswordCreateRouteData => GoRouteData.$route( RouteBase get $forgotPasswordCreateRouteData => GoRouteData.$route(
path: '/forgot-password-create', path: '/forgot-password-create',
factory: _$ForgotPasswordCreateRouteData._fromState, factory: _$ForgotPasswordCreateRouteData._fromState,
); );
+20
View File
@@ -0,0 +1,20 @@
// ignore_for_file: invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart';
part 'lot.freezed.dart';
part 'lot.g.dart';
// Model for entries in the tender-details `lots` array. Only lot_id, title and
// description are mapped — the fields the tender-details "Lots" section renders.
// Add more fields here if the UI grows.
@freezed
abstract class Lot with _$Lot {
const factory Lot({
@JsonKey(name: 'lot_id') required String? lotId,
required String? title,
required String? description,
}) = _Lot;
factory Lot.fromJson(Map<String, Object?> json) => _$LotFromJson(json);
}
@@ -0,0 +1,283 @@
// 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 'lot.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$Lot {
@JsonKey(name: 'lot_id') String? get lotId; String? get title; String? get description;
/// Create a copy of Lot
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$LotCopyWith<Lot> get copyWith => _$LotCopyWithImpl<Lot>(this as Lot, _$identity);
/// Serializes this Lot to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is Lot&&(identical(other.lotId, lotId) || other.lotId == lotId)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,lotId,title,description);
@override
String toString() {
return 'Lot(lotId: $lotId, title: $title, description: $description)';
}
}
/// @nodoc
abstract mixin class $LotCopyWith<$Res> {
factory $LotCopyWith(Lot value, $Res Function(Lot) _then) = _$LotCopyWithImpl;
@useResult
$Res call({
@JsonKey(name: 'lot_id') String? lotId, String? title, String? description
});
}
/// @nodoc
class _$LotCopyWithImpl<$Res>
implements $LotCopyWith<$Res> {
_$LotCopyWithImpl(this._self, this._then);
final Lot _self;
final $Res Function(Lot) _then;
/// Create a copy of Lot
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? lotId = freezed,Object? title = freezed,Object? description = freezed,}) {
return _then(_self.copyWith(
lotId: freezed == lotId ? _self.lotId : lotId // ignore: cast_nullable_to_non_nullable
as String?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// Adds pattern-matching-related methods to [Lot].
extension LotPatterns on Lot {
/// 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( _Lot value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _Lot() 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( _Lot value) $default,){
final _that = this;
switch (_that) {
case _Lot():
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( _Lot value)? $default,){
final _that = this;
switch (_that) {
case _Lot() 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: 'lot_id') String? lotId, String? title, String? description)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _Lot() when $default != null:
return $default(_that.lotId,_that.title,_that.description);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: 'lot_id') String? lotId, String? title, String? description) $default,) {final _that = this;
switch (_that) {
case _Lot():
return $default(_that.lotId,_that.title,_that.description);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: 'lot_id') String? lotId, String? title, String? description)? $default,) {final _that = this;
switch (_that) {
case _Lot() when $default != null:
return $default(_that.lotId,_that.title,_that.description);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _Lot implements Lot {
const _Lot({@JsonKey(name: 'lot_id') required this.lotId, required this.title, required this.description});
factory _Lot.fromJson(Map<String, dynamic> json) => _$LotFromJson(json);
@override@JsonKey(name: 'lot_id') final String? lotId;
@override final String? title;
@override final String? description;
/// Create a copy of Lot
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$LotCopyWith<_Lot> get copyWith => __$LotCopyWithImpl<_Lot>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$LotToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Lot&&(identical(other.lotId, lotId) || other.lotId == lotId)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,lotId,title,description);
@override
String toString() {
return 'Lot(lotId: $lotId, title: $title, description: $description)';
}
}
/// @nodoc
abstract mixin class _$LotCopyWith<$Res> implements $LotCopyWith<$Res> {
factory _$LotCopyWith(_Lot value, $Res Function(_Lot) _then) = __$LotCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(name: 'lot_id') String? lotId, String? title, String? description
});
}
/// @nodoc
class __$LotCopyWithImpl<$Res>
implements _$LotCopyWith<$Res> {
__$LotCopyWithImpl(this._self, this._then);
final _Lot _self;
final $Res Function(_Lot) _then;
/// Create a copy of Lot
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? lotId = freezed,Object? title = freezed,Object? description = freezed,}) {
return _then(_Lot(
lotId: freezed == lotId ? _self.lotId : lotId // ignore: cast_nullable_to_non_nullable
as String?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
// dart format on
+19
View File
@@ -0,0 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'lot.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_Lot _$LotFromJson(Map<String, dynamic> json) => _Lot(
lotId: json['lot_id'] as String?,
title: json['title'] as String?,
description: json['description'] as String?,
);
Map<String, dynamic> _$LotToJson(_Lot instance) => <String, dynamic>{
'lot_id': instance.lotId,
'title': instance.title,
'description': instance.description,
};
@@ -1,15 +1,22 @@
// ignore_for_file: invalid_annotation_target // ignore_for_file: invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:tm_app/data/services/model/organization/organization_address.dart';
part 'organization.freezed.dart'; part 'organization.freezed.dart';
part 'organization.g.dart'; part 'organization.g.dart';
// company_id and a nested address (OrganizationAddress) surface buyer details
// on the tender-details page. A dedicated OrganizationAddress type is used
// instead of the existing `Address` model because the buyer address shape
// differs (city_name + country_code only) and is unrelated to the user-profile
// Address.
@freezed @freezed
abstract class Organization with _$Organization { abstract class Organization with _$Organization {
const factory Organization({ const factory Organization({
required String? name, required String? name,
@JsonKey(name: 'company_id') required String? companyId,
required OrganizationAddress? address,
}) = _Organization; }) = _Organization;
factory Organization.fromJson(Map<String, Object?> json) => factory Organization.fromJson(Map<String, Object?> json) =>
@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$Organization { mixin _$Organization {
String? get name; String? get name;@JsonKey(name: 'company_id') String? get companyId; OrganizationAddress? get address;
/// Create a copy of Organization /// Create a copy of Organization
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $OrganizationCopyWith<Organization> get copyWith => _$OrganizationCopyWithImpl<O
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is Organization&&(identical(other.name, name) || other.name == name)); return identical(this, other) || (other.runtimeType == runtimeType&&other is Organization&&(identical(other.name, name) || other.name == name)&&(identical(other.companyId, companyId) || other.companyId == companyId)&&(identical(other.address, address) || other.address == address));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,name); int get hashCode => Object.hash(runtimeType,name,companyId,address);
@override @override
String toString() { String toString() {
return 'Organization(name: $name)'; return 'Organization(name: $name, companyId: $companyId, address: $address)';
} }
@@ -48,11 +48,11 @@ abstract mixin class $OrganizationCopyWith<$Res> {
factory $OrganizationCopyWith(Organization value, $Res Function(Organization) _then) = _$OrganizationCopyWithImpl; factory $OrganizationCopyWith(Organization value, $Res Function(Organization) _then) = _$OrganizationCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String? name String? name,@JsonKey(name: 'company_id') String? companyId, OrganizationAddress? address
}); });
$OrganizationAddressCopyWith<$Res>? get address;
} }
/// @nodoc /// @nodoc
@@ -65,13 +65,27 @@ class _$OrganizationCopyWithImpl<$Res>
/// Create a copy of Organization /// Create a copy of Organization
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? name = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? name = freezed,Object? companyId = freezed,Object? address = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String?, as String?,companyId: freezed == companyId ? _self.companyId : companyId // ignore: cast_nullable_to_non_nullable
as String?,address: freezed == address ? _self.address : address // ignore: cast_nullable_to_non_nullable
as OrganizationAddress?,
)); ));
} }
/// Create a copy of Organization
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$OrganizationAddressCopyWith<$Res>? get address {
if (_self.address == null) {
return null;
}
return $OrganizationAddressCopyWith<$Res>(_self.address!, (value) {
return _then(_self.copyWith(address: value));
});
}
} }
@@ -153,10 +167,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? name)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? name, @JsonKey(name: 'company_id') String? companyId, OrganizationAddress? address)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _Organization() when $default != null: case _Organization() when $default != null:
return $default(_that.name);case _: return $default(_that.name,_that.companyId,_that.address);case _:
return orElse(); return orElse();
} }
@@ -174,10 +188,10 @@ return $default(_that.name);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? name) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? name, @JsonKey(name: 'company_id') String? companyId, OrganizationAddress? address) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _Organization(): case _Organization():
return $default(_that.name);case _: return $default(_that.name,_that.companyId,_that.address);case _:
throw StateError('Unexpected subclass'); throw StateError('Unexpected subclass');
} }
@@ -194,10 +208,10 @@ return $default(_that.name);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? name)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? name, @JsonKey(name: 'company_id') String? companyId, OrganizationAddress? address)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _Organization() when $default != null: case _Organization() when $default != null:
return $default(_that.name);case _: return $default(_that.name,_that.companyId,_that.address);case _:
return null; return null;
} }
@@ -209,10 +223,12 @@ return $default(_that.name);case _:
@JsonSerializable() @JsonSerializable()
class _Organization implements Organization { class _Organization implements Organization {
const _Organization({required this.name}); const _Organization({required this.name, @JsonKey(name: 'company_id') required this.companyId, required this.address});
factory _Organization.fromJson(Map<String, dynamic> json) => _$OrganizationFromJson(json); factory _Organization.fromJson(Map<String, dynamic> json) => _$OrganizationFromJson(json);
@override final String? name; @override final String? name;
@override@JsonKey(name: 'company_id') final String? companyId;
@override final OrganizationAddress? address;
/// Create a copy of Organization /// Create a copy of Organization
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -227,16 +243,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Organization&&(identical(other.name, name) || other.name == name)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _Organization&&(identical(other.name, name) || other.name == name)&&(identical(other.companyId, companyId) || other.companyId == companyId)&&(identical(other.address, address) || other.address == address));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,name); int get hashCode => Object.hash(runtimeType,name,companyId,address);
@override @override
String toString() { String toString() {
return 'Organization(name: $name)'; return 'Organization(name: $name, companyId: $companyId, address: $address)';
} }
@@ -247,11 +263,11 @@ abstract mixin class _$OrganizationCopyWith<$Res> implements $OrganizationCopyWi
factory _$OrganizationCopyWith(_Organization value, $Res Function(_Organization) _then) = __$OrganizationCopyWithImpl; factory _$OrganizationCopyWith(_Organization value, $Res Function(_Organization) _then) = __$OrganizationCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String? name String? name,@JsonKey(name: 'company_id') String? companyId, OrganizationAddress? address
}); });
@override $OrganizationAddressCopyWith<$Res>? get address;
} }
/// @nodoc /// @nodoc
@@ -264,14 +280,28 @@ class __$OrganizationCopyWithImpl<$Res>
/// Create a copy of Organization /// Create a copy of Organization
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? name = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? name = freezed,Object? companyId = freezed,Object? address = freezed,}) {
return _then(_Organization( return _then(_Organization(
name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String?, as String?,companyId: freezed == companyId ? _self.companyId : companyId // ignore: cast_nullable_to_non_nullable
as String?,address: freezed == address ? _self.address : address // ignore: cast_nullable_to_non_nullable
as OrganizationAddress?,
)); ));
} }
/// Create a copy of Organization
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$OrganizationAddressCopyWith<$Res>? get address {
if (_self.address == null) {
return null;
}
return $OrganizationAddressCopyWith<$Res>(_self.address!, (value) {
return _then(_self.copyWith(address: value));
});
}
} }
// dart format on // dart format on
@@ -7,7 +7,20 @@ part of 'organization.dart';
// ************************************************************************** // **************************************************************************
_Organization _$OrganizationFromJson(Map<String, dynamic> json) => _Organization _$OrganizationFromJson(Map<String, dynamic> json) =>
_Organization(name: json['name'] as String?); _Organization(
name: json['name'] as String?,
companyId: json['company_id'] as String?,
address:
json['address'] == null
? null
: OrganizationAddress.fromJson(
json['address'] as Map<String, dynamic>,
),
);
Map<String, dynamic> _$OrganizationToJson(_Organization instance) => Map<String, dynamic> _$OrganizationToJson(_Organization instance) =>
<String, dynamic>{'name': instance.name}; <String, dynamic>{
'name': instance.name,
'company_id': instance.companyId,
'address': instance.address,
};
@@ -0,0 +1,21 @@
// ignore_for_file: invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart';
part 'organization_address.freezed.dart';
part 'organization_address.g.dart';
// Model for the buyer_organization.address object from the tender-details API.
// Kept separate from the existing profile `Address` model because the shapes
// differ (this one only needs city_name and country_code). Only the fields the
// UI shows are mapped.
@freezed
abstract class OrganizationAddress with _$OrganizationAddress {
const factory OrganizationAddress({
@JsonKey(name: 'city_name') required String? cityName,
@JsonKey(name: 'country_code') required String? countryCode,
}) = _OrganizationAddress;
factory OrganizationAddress.fromJson(Map<String, Object?> json) =>
_$OrganizationAddressFromJson(json);
}
@@ -0,0 +1,280 @@
// 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 'organization_address.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$OrganizationAddress {
@JsonKey(name: 'city_name') String? get cityName;@JsonKey(name: 'country_code') String? get countryCode;
/// Create a copy of OrganizationAddress
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$OrganizationAddressCopyWith<OrganizationAddress> get copyWith => _$OrganizationAddressCopyWithImpl<OrganizationAddress>(this as OrganizationAddress, _$identity);
/// Serializes this OrganizationAddress to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is OrganizationAddress&&(identical(other.cityName, cityName) || other.cityName == cityName)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,cityName,countryCode);
@override
String toString() {
return 'OrganizationAddress(cityName: $cityName, countryCode: $countryCode)';
}
}
/// @nodoc
abstract mixin class $OrganizationAddressCopyWith<$Res> {
factory $OrganizationAddressCopyWith(OrganizationAddress value, $Res Function(OrganizationAddress) _then) = _$OrganizationAddressCopyWithImpl;
@useResult
$Res call({
@JsonKey(name: 'city_name') String? cityName,@JsonKey(name: 'country_code') String? countryCode
});
}
/// @nodoc
class _$OrganizationAddressCopyWithImpl<$Res>
implements $OrganizationAddressCopyWith<$Res> {
_$OrganizationAddressCopyWithImpl(this._self, this._then);
final OrganizationAddress _self;
final $Res Function(OrganizationAddress) _then;
/// Create a copy of OrganizationAddress
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? cityName = freezed,Object? countryCode = freezed,}) {
return _then(_self.copyWith(
cityName: freezed == cityName ? _self.cityName : cityName // ignore: cast_nullable_to_non_nullable
as String?,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// Adds pattern-matching-related methods to [OrganizationAddress].
extension OrganizationAddressPatterns on OrganizationAddress {
/// 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( _OrganizationAddress value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _OrganizationAddress() 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( _OrganizationAddress value) $default,){
final _that = this;
switch (_that) {
case _OrganizationAddress():
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( _OrganizationAddress value)? $default,){
final _that = this;
switch (_that) {
case _OrganizationAddress() 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: 'city_name') String? cityName, @JsonKey(name: 'country_code') String? countryCode)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _OrganizationAddress() when $default != null:
return $default(_that.cityName,_that.countryCode);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: 'city_name') String? cityName, @JsonKey(name: 'country_code') String? countryCode) $default,) {final _that = this;
switch (_that) {
case _OrganizationAddress():
return $default(_that.cityName,_that.countryCode);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: 'city_name') String? cityName, @JsonKey(name: 'country_code') String? countryCode)? $default,) {final _that = this;
switch (_that) {
case _OrganizationAddress() when $default != null:
return $default(_that.cityName,_that.countryCode);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _OrganizationAddress implements OrganizationAddress {
const _OrganizationAddress({@JsonKey(name: 'city_name') required this.cityName, @JsonKey(name: 'country_code') required this.countryCode});
factory _OrganizationAddress.fromJson(Map<String, dynamic> json) => _$OrganizationAddressFromJson(json);
@override@JsonKey(name: 'city_name') final String? cityName;
@override@JsonKey(name: 'country_code') final String? countryCode;
/// Create a copy of OrganizationAddress
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$OrganizationAddressCopyWith<_OrganizationAddress> get copyWith => __$OrganizationAddressCopyWithImpl<_OrganizationAddress>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$OrganizationAddressToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _OrganizationAddress&&(identical(other.cityName, cityName) || other.cityName == cityName)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,cityName,countryCode);
@override
String toString() {
return 'OrganizationAddress(cityName: $cityName, countryCode: $countryCode)';
}
}
/// @nodoc
abstract mixin class _$OrganizationAddressCopyWith<$Res> implements $OrganizationAddressCopyWith<$Res> {
factory _$OrganizationAddressCopyWith(_OrganizationAddress value, $Res Function(_OrganizationAddress) _then) = __$OrganizationAddressCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(name: 'city_name') String? cityName,@JsonKey(name: 'country_code') String? countryCode
});
}
/// @nodoc
class __$OrganizationAddressCopyWithImpl<$Res>
implements _$OrganizationAddressCopyWith<$Res> {
__$OrganizationAddressCopyWithImpl(this._self, this._then);
final _OrganizationAddress _self;
final $Res Function(_OrganizationAddress) _then;
/// Create a copy of OrganizationAddress
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? cityName = freezed,Object? countryCode = freezed,}) {
return _then(_OrganizationAddress(
cityName: freezed == cityName ? _self.cityName : cityName // ignore: cast_nullable_to_non_nullable
as String?,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
// dart format on
@@ -0,0 +1,20 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'organization_address.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_OrganizationAddress _$OrganizationAddressFromJson(Map<String, dynamic> json) =>
_OrganizationAddress(
cityName: json['city_name'] as String?,
countryCode: json['country_code'] as String?,
);
Map<String, dynamic> _$OrganizationAddressToJson(
_OrganizationAddress instance,
) => <String, dynamic>{
'city_name': instance.cityName,
'country_code': instance.countryCode,
};
@@ -2,11 +2,18 @@
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:tm_app/core/utils/date_utils.dart'; import 'package:tm_app/core/utils/date_utils.dart';
import 'package:tm_app/data/services/model/lot/lot.dart';
import 'package:tm_app/data/services/model/organization/organization.dart'; import 'package:tm_app/data/services/model/organization/organization.dart';
part 'tender_data.freezed.dart'; part 'tender_data.freezed.dart';
part 'tender_data.g.dart'; part 'tender_data.g.dart';
// Fields mirror the /tenders/details/:id response so the tender-details page
// can show them: notice_type_code, form_type, notice_language_code, issue_date,
// issue_time, lots, official_languages. issue_date/issue_time reuse
// unixTimestampFromJson (epoch seconds, tolerant of ms/string) like the other
// timestamp fields. All fields are nullable to stay resilient to partial API
// payloads.
@freezed @freezed
abstract class TenderData with _$TenderData { abstract class TenderData with _$TenderData {
const factory TenderData({ const factory TenderData({
@@ -16,6 +23,13 @@ abstract class TenderData with _$TenderData {
@JsonKey(name: 'tender_id') required String? tenderId, @JsonKey(name: 'tender_id') required String? tenderId,
@JsonKey(name: 'notice_publication_id') @JsonKey(name: 'notice_publication_id')
required String? noticePublicationId, required String? noticePublicationId,
@JsonKey(name: 'notice_type_code') required String? noticeTypeCode,
@JsonKey(name: 'form_type') required String? formType,
@JsonKey(name: 'notice_language_code') required String? noticeLanguageCode,
@JsonKey(name: 'issue_date', fromJson: unixTimestampFromJson)
required int? issueDate,
@JsonKey(name: 'issue_time', fromJson: unixTimestampFromJson)
required int? issueTime,
@JsonKey(name: 'publication_date', fromJson: unixTimestampFromJson) @JsonKey(name: 'publication_date', fromJson: unixTimestampFromJson)
required int? publicationDate, required int? publicationDate,
required String? title, required String? title,
@@ -37,6 +51,9 @@ abstract class TenderData with _$TenderData {
@JsonKey(name: 'duration_unit') required String? durationUnit, @JsonKey(name: 'duration_unit') required String? durationUnit,
@JsonKey(name: 'buyer_organization') @JsonKey(name: 'buyer_organization')
required Organization? buyerOrganization, required Organization? buyerOrganization,
required List<Lot>? lots,
@JsonKey(name: 'official_languages')
required List<String>? officialLanguages,
required String? status, required String? status,
}) = _TenderData; }) = _TenderData;
@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$TenderData { mixin _$TenderData {
bool? get success; String? get message; String? get id;@JsonKey(name: 'tender_id') String? get tenderId;@JsonKey(name: 'notice_publication_id') String? get noticePublicationId;@JsonKey(name: 'publication_date') int? get publicationDate; String? get title; String? get description;@JsonKey(name: 'procurement_type_code') String? get procurementTypeCode;@JsonKey(name: 'procedure_code') String? get procedureCode;@JsonKey(name: 'estimated_value') int? get estimatedValue; String? get currency; String? get duration;@JsonKey(name: 'tender_deadline') int? get tenderDeadline;@JsonKey(name: 'submission_deadline') int? get submissionDeadline;@JsonKey(name: 'application_deadline') int? get applicationDeadline;@JsonKey(name: 'submission_url') String? get submissionUrl;@JsonKey(name: 'country_code') String? get countryCode;@JsonKey(name: 'duration_unit') String? get durationUnit;@JsonKey(name: 'buyer_organization') Organization? get buyerOrganization; String? get status; bool? get success; String? get message; String? get id;@JsonKey(name: 'tender_id') String? get tenderId;@JsonKey(name: 'notice_publication_id') String? get noticePublicationId;@JsonKey(name: 'notice_type_code') String? get noticeTypeCode;@JsonKey(name: 'form_type') String? get formType;@JsonKey(name: 'notice_language_code') String? get noticeLanguageCode;@JsonKey(name: 'issue_date', fromJson: unixTimestampFromJson) int? get issueDate;@JsonKey(name: 'issue_time', fromJson: unixTimestampFromJson) int? get issueTime;@JsonKey(name: 'publication_date', fromJson: unixTimestampFromJson) int? get publicationDate; String? get title; String? get description;@JsonKey(name: 'procurement_type_code') String? get procurementTypeCode;@JsonKey(name: 'procedure_code') String? get procedureCode;@JsonKey(name: 'estimated_value') int? get estimatedValue; String? get currency; String? get duration;@JsonKey(name: 'tender_deadline', fromJson: unixTimestampFromJson) int? get tenderDeadline;@JsonKey(name: 'submission_deadline', fromJson: unixTimestampFromJson) int? get submissionDeadline;@JsonKey(name: 'application_deadline', fromJson: unixTimestampFromJson) int? get applicationDeadline;@JsonKey(name: 'submission_url') String? get submissionUrl;@JsonKey(name: 'country_code') String? get countryCode;@JsonKey(name: 'duration_unit') String? get durationUnit;@JsonKey(name: 'buyer_organization') Organization? get buyerOrganization; List<Lot>? get lots;@JsonKey(name: 'official_languages') List<String>? get officialLanguages; String? get status;
/// Create a copy of TenderData /// Create a copy of TenderData
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $TenderDataCopyWith<TenderData> get copyWith => _$TenderDataCopyWithImpl<TenderD
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TenderData&&(identical(other.success, success) || other.success == success)&&(identical(other.message, message) || other.message == message)&&(identical(other.id, id) || other.id == id)&&(identical(other.tenderId, tenderId) || other.tenderId == tenderId)&&(identical(other.noticePublicationId, noticePublicationId) || other.noticePublicationId == noticePublicationId)&&(identical(other.publicationDate, publicationDate) || other.publicationDate == publicationDate)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.procurementTypeCode, procurementTypeCode) || other.procurementTypeCode == procurementTypeCode)&&(identical(other.procedureCode, procedureCode) || other.procedureCode == procedureCode)&&(identical(other.estimatedValue, estimatedValue) || other.estimatedValue == estimatedValue)&&(identical(other.currency, currency) || other.currency == currency)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.tenderDeadline, tenderDeadline) || other.tenderDeadline == tenderDeadline)&&(identical(other.submissionDeadline, submissionDeadline) || other.submissionDeadline == submissionDeadline)&&(identical(other.applicationDeadline, applicationDeadline) || other.applicationDeadline == applicationDeadline)&&(identical(other.submissionUrl, submissionUrl) || other.submissionUrl == submissionUrl)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)&&(identical(other.durationUnit, durationUnit) || other.durationUnit == durationUnit)&&(identical(other.buyerOrganization, buyerOrganization) || other.buyerOrganization == buyerOrganization)&&(identical(other.status, status) || other.status == status)); return identical(this, other) || (other.runtimeType == runtimeType&&other is TenderData&&(identical(other.success, success) || other.success == success)&&(identical(other.message, message) || other.message == message)&&(identical(other.id, id) || other.id == id)&&(identical(other.tenderId, tenderId) || other.tenderId == tenderId)&&(identical(other.noticePublicationId, noticePublicationId) || other.noticePublicationId == noticePublicationId)&&(identical(other.noticeTypeCode, noticeTypeCode) || other.noticeTypeCode == noticeTypeCode)&&(identical(other.formType, formType) || other.formType == formType)&&(identical(other.noticeLanguageCode, noticeLanguageCode) || other.noticeLanguageCode == noticeLanguageCode)&&(identical(other.issueDate, issueDate) || other.issueDate == issueDate)&&(identical(other.issueTime, issueTime) || other.issueTime == issueTime)&&(identical(other.publicationDate, publicationDate) || other.publicationDate == publicationDate)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.procurementTypeCode, procurementTypeCode) || other.procurementTypeCode == procurementTypeCode)&&(identical(other.procedureCode, procedureCode) || other.procedureCode == procedureCode)&&(identical(other.estimatedValue, estimatedValue) || other.estimatedValue == estimatedValue)&&(identical(other.currency, currency) || other.currency == currency)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.tenderDeadline, tenderDeadline) || other.tenderDeadline == tenderDeadline)&&(identical(other.submissionDeadline, submissionDeadline) || other.submissionDeadline == submissionDeadline)&&(identical(other.applicationDeadline, applicationDeadline) || other.applicationDeadline == applicationDeadline)&&(identical(other.submissionUrl, submissionUrl) || other.submissionUrl == submissionUrl)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)&&(identical(other.durationUnit, durationUnit) || other.durationUnit == durationUnit)&&(identical(other.buyerOrganization, buyerOrganization) || other.buyerOrganization == buyerOrganization)&&const DeepCollectionEquality().equals(other.lots, lots)&&const DeepCollectionEquality().equals(other.officialLanguages, officialLanguages)&&(identical(other.status, status) || other.status == status));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hashAll([runtimeType,success,message,id,tenderId,noticePublicationId,publicationDate,title,description,procurementTypeCode,procedureCode,estimatedValue,currency,duration,tenderDeadline,submissionDeadline,applicationDeadline,submissionUrl,countryCode,durationUnit,buyerOrganization,status]); int get hashCode => Object.hashAll([runtimeType,success,message,id,tenderId,noticePublicationId,noticeTypeCode,formType,noticeLanguageCode,issueDate,issueTime,publicationDate,title,description,procurementTypeCode,procedureCode,estimatedValue,currency,duration,tenderDeadline,submissionDeadline,applicationDeadline,submissionUrl,countryCode,durationUnit,buyerOrganization,const DeepCollectionEquality().hash(lots),const DeepCollectionEquality().hash(officialLanguages),status]);
@override @override
String toString() { String toString() {
return 'TenderData(success: $success, message: $message, id: $id, tenderId: $tenderId, noticePublicationId: $noticePublicationId, publicationDate: $publicationDate, title: $title, description: $description, procurementTypeCode: $procurementTypeCode, procedureCode: $procedureCode, estimatedValue: $estimatedValue, currency: $currency, duration: $duration, tenderDeadline: $tenderDeadline, submissionDeadline: $submissionDeadline, applicationDeadline: $applicationDeadline, submissionUrl: $submissionUrl, countryCode: $countryCode, durationUnit: $durationUnit, buyerOrganization: $buyerOrganization, status: $status)'; return 'TenderData(success: $success, message: $message, id: $id, tenderId: $tenderId, noticePublicationId: $noticePublicationId, noticeTypeCode: $noticeTypeCode, formType: $formType, noticeLanguageCode: $noticeLanguageCode, issueDate: $issueDate, issueTime: $issueTime, publicationDate: $publicationDate, title: $title, description: $description, procurementTypeCode: $procurementTypeCode, procedureCode: $procedureCode, estimatedValue: $estimatedValue, currency: $currency, duration: $duration, tenderDeadline: $tenderDeadline, submissionDeadline: $submissionDeadline, applicationDeadline: $applicationDeadline, submissionUrl: $submissionUrl, countryCode: $countryCode, durationUnit: $durationUnit, buyerOrganization: $buyerOrganization, lots: $lots, officialLanguages: $officialLanguages, status: $status)';
} }
@@ -48,7 +48,7 @@ abstract mixin class $TenderDataCopyWith<$Res> {
factory $TenderDataCopyWith(TenderData value, $Res Function(TenderData) _then) = _$TenderDataCopyWithImpl; factory $TenderDataCopyWith(TenderData value, $Res Function(TenderData) _then) = _$TenderDataCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
bool? success, String? message, String? id,@JsonKey(name: 'tender_id') String? tenderId,@JsonKey(name: 'notice_publication_id') String? noticePublicationId,@JsonKey(name: 'publication_date') int? publicationDate, String? title, String? description,@JsonKey(name: 'procurement_type_code') String? procurementTypeCode,@JsonKey(name: 'procedure_code') String? procedureCode,@JsonKey(name: 'estimated_value') int? estimatedValue, String? currency, String? duration,@JsonKey(name: 'tender_deadline') int? tenderDeadline,@JsonKey(name: 'submission_deadline') int? submissionDeadline,@JsonKey(name: 'application_deadline') int? applicationDeadline,@JsonKey(name: 'submission_url') String? submissionUrl,@JsonKey(name: 'country_code') String? countryCode,@JsonKey(name: 'duration_unit') String? durationUnit,@JsonKey(name: 'buyer_organization') Organization? buyerOrganization, String? status bool? success, String? message, String? id,@JsonKey(name: 'tender_id') String? tenderId,@JsonKey(name: 'notice_publication_id') String? noticePublicationId,@JsonKey(name: 'notice_type_code') String? noticeTypeCode,@JsonKey(name: 'form_type') String? formType,@JsonKey(name: 'notice_language_code') String? noticeLanguageCode,@JsonKey(name: 'issue_date', fromJson: unixTimestampFromJson) int? issueDate,@JsonKey(name: 'issue_time', fromJson: unixTimestampFromJson) int? issueTime,@JsonKey(name: 'publication_date', fromJson: unixTimestampFromJson) int? publicationDate, String? title, String? description,@JsonKey(name: 'procurement_type_code') String? procurementTypeCode,@JsonKey(name: 'procedure_code') String? procedureCode,@JsonKey(name: 'estimated_value') int? estimatedValue, String? currency, String? duration,@JsonKey(name: 'tender_deadline', fromJson: unixTimestampFromJson) int? tenderDeadline,@JsonKey(name: 'submission_deadline', fromJson: unixTimestampFromJson) int? submissionDeadline,@JsonKey(name: 'application_deadline', fromJson: unixTimestampFromJson) int? applicationDeadline,@JsonKey(name: 'submission_url') String? submissionUrl,@JsonKey(name: 'country_code') String? countryCode,@JsonKey(name: 'duration_unit') String? durationUnit,@JsonKey(name: 'buyer_organization') Organization? buyerOrganization, List<Lot>? lots,@JsonKey(name: 'official_languages') List<String>? officialLanguages, String? status
}); });
@@ -65,14 +65,19 @@ class _$TenderDataCopyWithImpl<$Res>
/// Create a copy of TenderData /// Create a copy of TenderData
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? success = freezed,Object? message = freezed,Object? id = freezed,Object? tenderId = freezed,Object? noticePublicationId = freezed,Object? publicationDate = freezed,Object? title = freezed,Object? description = freezed,Object? procurementTypeCode = freezed,Object? procedureCode = freezed,Object? estimatedValue = freezed,Object? currency = freezed,Object? duration = freezed,Object? tenderDeadline = freezed,Object? submissionDeadline = freezed,Object? applicationDeadline = freezed,Object? submissionUrl = freezed,Object? countryCode = freezed,Object? durationUnit = freezed,Object? buyerOrganization = freezed,Object? status = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? success = freezed,Object? message = freezed,Object? id = freezed,Object? tenderId = freezed,Object? noticePublicationId = freezed,Object? noticeTypeCode = freezed,Object? formType = freezed,Object? noticeLanguageCode = freezed,Object? issueDate = freezed,Object? issueTime = freezed,Object? publicationDate = freezed,Object? title = freezed,Object? description = freezed,Object? procurementTypeCode = freezed,Object? procedureCode = freezed,Object? estimatedValue = freezed,Object? currency = freezed,Object? duration = freezed,Object? tenderDeadline = freezed,Object? submissionDeadline = freezed,Object? applicationDeadline = freezed,Object? submissionUrl = freezed,Object? countryCode = freezed,Object? durationUnit = freezed,Object? buyerOrganization = freezed,Object? lots = freezed,Object? officialLanguages = freezed,Object? status = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as bool?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable as bool?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
as String?,id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String?,id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String?,tenderId: freezed == tenderId ? _self.tenderId : tenderId // ignore: cast_nullable_to_non_nullable as String?,tenderId: freezed == tenderId ? _self.tenderId : tenderId // ignore: cast_nullable_to_non_nullable
as String?,noticePublicationId: freezed == noticePublicationId ? _self.noticePublicationId : noticePublicationId // ignore: cast_nullable_to_non_nullable as String?,noticePublicationId: freezed == noticePublicationId ? _self.noticePublicationId : noticePublicationId // ignore: cast_nullable_to_non_nullable
as String?,publicationDate: freezed == publicationDate ? _self.publicationDate : publicationDate // ignore: cast_nullable_to_non_nullable as String?,noticeTypeCode: freezed == noticeTypeCode ? _self.noticeTypeCode : noticeTypeCode // ignore: cast_nullable_to_non_nullable
as String?,formType: freezed == formType ? _self.formType : formType // ignore: cast_nullable_to_non_nullable
as String?,noticeLanguageCode: freezed == noticeLanguageCode ? _self.noticeLanguageCode : noticeLanguageCode // ignore: cast_nullable_to_non_nullable
as String?,issueDate: freezed == issueDate ? _self.issueDate : issueDate // ignore: cast_nullable_to_non_nullable
as int?,issueTime: freezed == issueTime ? _self.issueTime : issueTime // ignore: cast_nullable_to_non_nullable
as int?,publicationDate: freezed == publicationDate ? _self.publicationDate : publicationDate // ignore: cast_nullable_to_non_nullable
as int?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable as int?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,procurementTypeCode: freezed == procurementTypeCode ? _self.procurementTypeCode : procurementTypeCode // ignore: cast_nullable_to_non_nullable as String?,procurementTypeCode: freezed == procurementTypeCode ? _self.procurementTypeCode : procurementTypeCode // ignore: cast_nullable_to_non_nullable
@@ -87,7 +92,9 @@ as int?,submissionUrl: freezed == submissionUrl ? _self.submissionUrl : submissi
as String?,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable as String?,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable
as String?,durationUnit: freezed == durationUnit ? _self.durationUnit : durationUnit // ignore: cast_nullable_to_non_nullable as String?,durationUnit: freezed == durationUnit ? _self.durationUnit : durationUnit // ignore: cast_nullable_to_non_nullable
as String?,buyerOrganization: freezed == buyerOrganization ? _self.buyerOrganization : buyerOrganization // ignore: cast_nullable_to_non_nullable as String?,buyerOrganization: freezed == buyerOrganization ? _self.buyerOrganization : buyerOrganization // ignore: cast_nullable_to_non_nullable
as Organization?,status: freezed == status ? _self.status : status // ignore: cast_nullable_to_non_nullable as Organization?,lots: freezed == lots ? _self.lots : lots // ignore: cast_nullable_to_non_nullable
as List<Lot>?,officialLanguages: freezed == officialLanguages ? _self.officialLanguages : officialLanguages // ignore: cast_nullable_to_non_nullable
as List<String>?,status: freezed == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as String?, as String?,
)); ));
} }
@@ -185,10 +192,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool? success, String? message, String? id, @JsonKey(name: 'tender_id') String? tenderId, @JsonKey(name: 'notice_publication_id') String? noticePublicationId, @JsonKey(name: 'publication_date') int? publicationDate, String? title, String? description, @JsonKey(name: 'procurement_type_code') String? procurementTypeCode, @JsonKey(name: 'procedure_code') String? procedureCode, @JsonKey(name: 'estimated_value') int? estimatedValue, String? currency, String? duration, @JsonKey(name: 'tender_deadline') int? tenderDeadline, @JsonKey(name: 'submission_deadline') int? submissionDeadline, @JsonKey(name: 'application_deadline') int? applicationDeadline, @JsonKey(name: 'submission_url') String? submissionUrl, @JsonKey(name: 'country_code') String? countryCode, @JsonKey(name: 'duration_unit') String? durationUnit, @JsonKey(name: 'buyer_organization') Organization? buyerOrganization, String? status)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool? success, String? message, String? id, @JsonKey(name: 'tender_id') String? tenderId, @JsonKey(name: 'notice_publication_id') String? noticePublicationId, @JsonKey(name: 'notice_type_code') String? noticeTypeCode, @JsonKey(name: 'form_type') String? formType, @JsonKey(name: 'notice_language_code') String? noticeLanguageCode, @JsonKey(name: 'issue_date', fromJson: unixTimestampFromJson) int? issueDate, @JsonKey(name: 'issue_time', fromJson: unixTimestampFromJson) int? issueTime, @JsonKey(name: 'publication_date', fromJson: unixTimestampFromJson) int? publicationDate, String? title, String? description, @JsonKey(name: 'procurement_type_code') String? procurementTypeCode, @JsonKey(name: 'procedure_code') String? procedureCode, @JsonKey(name: 'estimated_value') int? estimatedValue, String? currency, String? duration, @JsonKey(name: 'tender_deadline', fromJson: unixTimestampFromJson) int? tenderDeadline, @JsonKey(name: 'submission_deadline', fromJson: unixTimestampFromJson) int? submissionDeadline, @JsonKey(name: 'application_deadline', fromJson: unixTimestampFromJson) int? applicationDeadline, @JsonKey(name: 'submission_url') String? submissionUrl, @JsonKey(name: 'country_code') String? countryCode, @JsonKey(name: 'duration_unit') String? durationUnit, @JsonKey(name: 'buyer_organization') Organization? buyerOrganization, List<Lot>? lots, @JsonKey(name: 'official_languages') List<String>? officialLanguages, String? status)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _TenderData() when $default != null: case _TenderData() when $default != null:
return $default(_that.success,_that.message,_that.id,_that.tenderId,_that.noticePublicationId,_that.publicationDate,_that.title,_that.description,_that.procurementTypeCode,_that.procedureCode,_that.estimatedValue,_that.currency,_that.duration,_that.tenderDeadline,_that.submissionDeadline,_that.applicationDeadline,_that.submissionUrl,_that.countryCode,_that.durationUnit,_that.buyerOrganization,_that.status);case _: return $default(_that.success,_that.message,_that.id,_that.tenderId,_that.noticePublicationId,_that.noticeTypeCode,_that.formType,_that.noticeLanguageCode,_that.issueDate,_that.issueTime,_that.publicationDate,_that.title,_that.description,_that.procurementTypeCode,_that.procedureCode,_that.estimatedValue,_that.currency,_that.duration,_that.tenderDeadline,_that.submissionDeadline,_that.applicationDeadline,_that.submissionUrl,_that.countryCode,_that.durationUnit,_that.buyerOrganization,_that.lots,_that.officialLanguages,_that.status);case _:
return orElse(); return orElse();
} }
@@ -206,10 +213,10 @@ return $default(_that.success,_that.message,_that.id,_that.tenderId,_that.notice
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool? success, String? message, String? id, @JsonKey(name: 'tender_id') String? tenderId, @JsonKey(name: 'notice_publication_id') String? noticePublicationId, @JsonKey(name: 'publication_date') int? publicationDate, String? title, String? description, @JsonKey(name: 'procurement_type_code') String? procurementTypeCode, @JsonKey(name: 'procedure_code') String? procedureCode, @JsonKey(name: 'estimated_value') int? estimatedValue, String? currency, String? duration, @JsonKey(name: 'tender_deadline') int? tenderDeadline, @JsonKey(name: 'submission_deadline') int? submissionDeadline, @JsonKey(name: 'application_deadline') int? applicationDeadline, @JsonKey(name: 'submission_url') String? submissionUrl, @JsonKey(name: 'country_code') String? countryCode, @JsonKey(name: 'duration_unit') String? durationUnit, @JsonKey(name: 'buyer_organization') Organization? buyerOrganization, String? status) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool? success, String? message, String? id, @JsonKey(name: 'tender_id') String? tenderId, @JsonKey(name: 'notice_publication_id') String? noticePublicationId, @JsonKey(name: 'notice_type_code') String? noticeTypeCode, @JsonKey(name: 'form_type') String? formType, @JsonKey(name: 'notice_language_code') String? noticeLanguageCode, @JsonKey(name: 'issue_date', fromJson: unixTimestampFromJson) int? issueDate, @JsonKey(name: 'issue_time', fromJson: unixTimestampFromJson) int? issueTime, @JsonKey(name: 'publication_date', fromJson: unixTimestampFromJson) int? publicationDate, String? title, String? description, @JsonKey(name: 'procurement_type_code') String? procurementTypeCode, @JsonKey(name: 'procedure_code') String? procedureCode, @JsonKey(name: 'estimated_value') int? estimatedValue, String? currency, String? duration, @JsonKey(name: 'tender_deadline', fromJson: unixTimestampFromJson) int? tenderDeadline, @JsonKey(name: 'submission_deadline', fromJson: unixTimestampFromJson) int? submissionDeadline, @JsonKey(name: 'application_deadline', fromJson: unixTimestampFromJson) int? applicationDeadline, @JsonKey(name: 'submission_url') String? submissionUrl, @JsonKey(name: 'country_code') String? countryCode, @JsonKey(name: 'duration_unit') String? durationUnit, @JsonKey(name: 'buyer_organization') Organization? buyerOrganization, List<Lot>? lots, @JsonKey(name: 'official_languages') List<String>? officialLanguages, String? status) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _TenderData(): case _TenderData():
return $default(_that.success,_that.message,_that.id,_that.tenderId,_that.noticePublicationId,_that.publicationDate,_that.title,_that.description,_that.procurementTypeCode,_that.procedureCode,_that.estimatedValue,_that.currency,_that.duration,_that.tenderDeadline,_that.submissionDeadline,_that.applicationDeadline,_that.submissionUrl,_that.countryCode,_that.durationUnit,_that.buyerOrganization,_that.status);case _: return $default(_that.success,_that.message,_that.id,_that.tenderId,_that.noticePublicationId,_that.noticeTypeCode,_that.formType,_that.noticeLanguageCode,_that.issueDate,_that.issueTime,_that.publicationDate,_that.title,_that.description,_that.procurementTypeCode,_that.procedureCode,_that.estimatedValue,_that.currency,_that.duration,_that.tenderDeadline,_that.submissionDeadline,_that.applicationDeadline,_that.submissionUrl,_that.countryCode,_that.durationUnit,_that.buyerOrganization,_that.lots,_that.officialLanguages,_that.status);case _:
throw StateError('Unexpected subclass'); throw StateError('Unexpected subclass');
} }
@@ -226,10 +233,10 @@ return $default(_that.success,_that.message,_that.id,_that.tenderId,_that.notice
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool? success, String? message, String? id, @JsonKey(name: 'tender_id') String? tenderId, @JsonKey(name: 'notice_publication_id') String? noticePublicationId, @JsonKey(name: 'publication_date') int? publicationDate, String? title, String? description, @JsonKey(name: 'procurement_type_code') String? procurementTypeCode, @JsonKey(name: 'procedure_code') String? procedureCode, @JsonKey(name: 'estimated_value') int? estimatedValue, String? currency, String? duration, @JsonKey(name: 'tender_deadline') int? tenderDeadline, @JsonKey(name: 'submission_deadline') int? submissionDeadline, @JsonKey(name: 'application_deadline') int? applicationDeadline, @JsonKey(name: 'submission_url') String? submissionUrl, @JsonKey(name: 'country_code') String? countryCode, @JsonKey(name: 'duration_unit') String? durationUnit, @JsonKey(name: 'buyer_organization') Organization? buyerOrganization, String? status)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool? success, String? message, String? id, @JsonKey(name: 'tender_id') String? tenderId, @JsonKey(name: 'notice_publication_id') String? noticePublicationId, @JsonKey(name: 'notice_type_code') String? noticeTypeCode, @JsonKey(name: 'form_type') String? formType, @JsonKey(name: 'notice_language_code') String? noticeLanguageCode, @JsonKey(name: 'issue_date', fromJson: unixTimestampFromJson) int? issueDate, @JsonKey(name: 'issue_time', fromJson: unixTimestampFromJson) int? issueTime, @JsonKey(name: 'publication_date', fromJson: unixTimestampFromJson) int? publicationDate, String? title, String? description, @JsonKey(name: 'procurement_type_code') String? procurementTypeCode, @JsonKey(name: 'procedure_code') String? procedureCode, @JsonKey(name: 'estimated_value') int? estimatedValue, String? currency, String? duration, @JsonKey(name: 'tender_deadline', fromJson: unixTimestampFromJson) int? tenderDeadline, @JsonKey(name: 'submission_deadline', fromJson: unixTimestampFromJson) int? submissionDeadline, @JsonKey(name: 'application_deadline', fromJson: unixTimestampFromJson) int? applicationDeadline, @JsonKey(name: 'submission_url') String? submissionUrl, @JsonKey(name: 'country_code') String? countryCode, @JsonKey(name: 'duration_unit') String? durationUnit, @JsonKey(name: 'buyer_organization') Organization? buyerOrganization, List<Lot>? lots, @JsonKey(name: 'official_languages') List<String>? officialLanguages, String? status)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _TenderData() when $default != null: case _TenderData() when $default != null:
return $default(_that.success,_that.message,_that.id,_that.tenderId,_that.noticePublicationId,_that.publicationDate,_that.title,_that.description,_that.procurementTypeCode,_that.procedureCode,_that.estimatedValue,_that.currency,_that.duration,_that.tenderDeadline,_that.submissionDeadline,_that.applicationDeadline,_that.submissionUrl,_that.countryCode,_that.durationUnit,_that.buyerOrganization,_that.status);case _: return $default(_that.success,_that.message,_that.id,_that.tenderId,_that.noticePublicationId,_that.noticeTypeCode,_that.formType,_that.noticeLanguageCode,_that.issueDate,_that.issueTime,_that.publicationDate,_that.title,_that.description,_that.procurementTypeCode,_that.procedureCode,_that.estimatedValue,_that.currency,_that.duration,_that.tenderDeadline,_that.submissionDeadline,_that.applicationDeadline,_that.submissionUrl,_that.countryCode,_that.durationUnit,_that.buyerOrganization,_that.lots,_that.officialLanguages,_that.status);case _:
return null; return null;
} }
@@ -241,7 +248,7 @@ return $default(_that.success,_that.message,_that.id,_that.tenderId,_that.notice
@JsonSerializable() @JsonSerializable()
class _TenderData implements TenderData { class _TenderData implements TenderData {
const _TenderData({required this.success, required this.message, required this.id, @JsonKey(name: 'tender_id') required this.tenderId, @JsonKey(name: 'notice_publication_id') required this.noticePublicationId, @JsonKey(name: 'publication_date') required this.publicationDate, required this.title, required this.description, @JsonKey(name: 'procurement_type_code') required this.procurementTypeCode, @JsonKey(name: 'procedure_code') required this.procedureCode, @JsonKey(name: 'estimated_value') required this.estimatedValue, required this.currency, required this.duration, @JsonKey(name: 'tender_deadline') required this.tenderDeadline, @JsonKey(name: 'submission_deadline') required this.submissionDeadline, @JsonKey(name: 'application_deadline') required this.applicationDeadline, @JsonKey(name: 'submission_url') required this.submissionUrl, @JsonKey(name: 'country_code') required this.countryCode, @JsonKey(name: 'duration_unit') required this.durationUnit, @JsonKey(name: 'buyer_organization') required this.buyerOrganization, required this.status}); const _TenderData({required this.success, required this.message, required this.id, @JsonKey(name: 'tender_id') required this.tenderId, @JsonKey(name: 'notice_publication_id') required this.noticePublicationId, @JsonKey(name: 'notice_type_code') required this.noticeTypeCode, @JsonKey(name: 'form_type') required this.formType, @JsonKey(name: 'notice_language_code') required this.noticeLanguageCode, @JsonKey(name: 'issue_date', fromJson: unixTimestampFromJson) required this.issueDate, @JsonKey(name: 'issue_time', fromJson: unixTimestampFromJson) required this.issueTime, @JsonKey(name: 'publication_date', fromJson: unixTimestampFromJson) required this.publicationDate, required this.title, required this.description, @JsonKey(name: 'procurement_type_code') required this.procurementTypeCode, @JsonKey(name: 'procedure_code') required this.procedureCode, @JsonKey(name: 'estimated_value') required this.estimatedValue, required this.currency, required this.duration, @JsonKey(name: 'tender_deadline', fromJson: unixTimestampFromJson) required this.tenderDeadline, @JsonKey(name: 'submission_deadline', fromJson: unixTimestampFromJson) required this.submissionDeadline, @JsonKey(name: 'application_deadline', fromJson: unixTimestampFromJson) required this.applicationDeadline, @JsonKey(name: 'submission_url') required this.submissionUrl, @JsonKey(name: 'country_code') required this.countryCode, @JsonKey(name: 'duration_unit') required this.durationUnit, @JsonKey(name: 'buyer_organization') required this.buyerOrganization, required final List<Lot>? lots, @JsonKey(name: 'official_languages') required final List<String>? officialLanguages, required this.status}): _lots = lots,_officialLanguages = officialLanguages;
factory _TenderData.fromJson(Map<String, dynamic> json) => _$TenderDataFromJson(json); factory _TenderData.fromJson(Map<String, dynamic> json) => _$TenderDataFromJson(json);
@override final bool? success; @override final bool? success;
@@ -249,7 +256,12 @@ class _TenderData implements TenderData {
@override final String? id; @override final String? id;
@override@JsonKey(name: 'tender_id') final String? tenderId; @override@JsonKey(name: 'tender_id') final String? tenderId;
@override@JsonKey(name: 'notice_publication_id') final String? noticePublicationId; @override@JsonKey(name: 'notice_publication_id') final String? noticePublicationId;
@override@JsonKey(name: 'publication_date') final int? publicationDate; @override@JsonKey(name: 'notice_type_code') final String? noticeTypeCode;
@override@JsonKey(name: 'form_type') final String? formType;
@override@JsonKey(name: 'notice_language_code') final String? noticeLanguageCode;
@override@JsonKey(name: 'issue_date', fromJson: unixTimestampFromJson) final int? issueDate;
@override@JsonKey(name: 'issue_time', fromJson: unixTimestampFromJson) final int? issueTime;
@override@JsonKey(name: 'publication_date', fromJson: unixTimestampFromJson) final int? publicationDate;
@override final String? title; @override final String? title;
@override final String? description; @override final String? description;
@override@JsonKey(name: 'procurement_type_code') final String? procurementTypeCode; @override@JsonKey(name: 'procurement_type_code') final String? procurementTypeCode;
@@ -257,13 +269,31 @@ class _TenderData implements TenderData {
@override@JsonKey(name: 'estimated_value') final int? estimatedValue; @override@JsonKey(name: 'estimated_value') final int? estimatedValue;
@override final String? currency; @override final String? currency;
@override final String? duration; @override final String? duration;
@override@JsonKey(name: 'tender_deadline') final int? tenderDeadline; @override@JsonKey(name: 'tender_deadline', fromJson: unixTimestampFromJson) final int? tenderDeadline;
@override@JsonKey(name: 'submission_deadline') final int? submissionDeadline; @override@JsonKey(name: 'submission_deadline', fromJson: unixTimestampFromJson) final int? submissionDeadline;
@override@JsonKey(name: 'application_deadline') final int? applicationDeadline; @override@JsonKey(name: 'application_deadline', fromJson: unixTimestampFromJson) final int? applicationDeadline;
@override@JsonKey(name: 'submission_url') final String? submissionUrl; @override@JsonKey(name: 'submission_url') final String? submissionUrl;
@override@JsonKey(name: 'country_code') final String? countryCode; @override@JsonKey(name: 'country_code') final String? countryCode;
@override@JsonKey(name: 'duration_unit') final String? durationUnit; @override@JsonKey(name: 'duration_unit') final String? durationUnit;
@override@JsonKey(name: 'buyer_organization') final Organization? buyerOrganization; @override@JsonKey(name: 'buyer_organization') final Organization? buyerOrganization;
final List<Lot>? _lots;
@override List<Lot>? get lots {
final value = _lots;
if (value == null) return null;
if (_lots is EqualUnmodifiableListView) return _lots;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
final List<String>? _officialLanguages;
@override@JsonKey(name: 'official_languages') List<String>? get officialLanguages {
final value = _officialLanguages;
if (value == null) return null;
if (_officialLanguages is EqualUnmodifiableListView) return _officialLanguages;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override final String? status; @override final String? status;
/// Create a copy of TenderData /// Create a copy of TenderData
@@ -279,16 +309,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _TenderData&&(identical(other.success, success) || other.success == success)&&(identical(other.message, message) || other.message == message)&&(identical(other.id, id) || other.id == id)&&(identical(other.tenderId, tenderId) || other.tenderId == tenderId)&&(identical(other.noticePublicationId, noticePublicationId) || other.noticePublicationId == noticePublicationId)&&(identical(other.publicationDate, publicationDate) || other.publicationDate == publicationDate)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.procurementTypeCode, procurementTypeCode) || other.procurementTypeCode == procurementTypeCode)&&(identical(other.procedureCode, procedureCode) || other.procedureCode == procedureCode)&&(identical(other.estimatedValue, estimatedValue) || other.estimatedValue == estimatedValue)&&(identical(other.currency, currency) || other.currency == currency)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.tenderDeadline, tenderDeadline) || other.tenderDeadline == tenderDeadline)&&(identical(other.submissionDeadline, submissionDeadline) || other.submissionDeadline == submissionDeadline)&&(identical(other.applicationDeadline, applicationDeadline) || other.applicationDeadline == applicationDeadline)&&(identical(other.submissionUrl, submissionUrl) || other.submissionUrl == submissionUrl)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)&&(identical(other.durationUnit, durationUnit) || other.durationUnit == durationUnit)&&(identical(other.buyerOrganization, buyerOrganization) || other.buyerOrganization == buyerOrganization)&&(identical(other.status, status) || other.status == status)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _TenderData&&(identical(other.success, success) || other.success == success)&&(identical(other.message, message) || other.message == message)&&(identical(other.id, id) || other.id == id)&&(identical(other.tenderId, tenderId) || other.tenderId == tenderId)&&(identical(other.noticePublicationId, noticePublicationId) || other.noticePublicationId == noticePublicationId)&&(identical(other.noticeTypeCode, noticeTypeCode) || other.noticeTypeCode == noticeTypeCode)&&(identical(other.formType, formType) || other.formType == formType)&&(identical(other.noticeLanguageCode, noticeLanguageCode) || other.noticeLanguageCode == noticeLanguageCode)&&(identical(other.issueDate, issueDate) || other.issueDate == issueDate)&&(identical(other.issueTime, issueTime) || other.issueTime == issueTime)&&(identical(other.publicationDate, publicationDate) || other.publicationDate == publicationDate)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.procurementTypeCode, procurementTypeCode) || other.procurementTypeCode == procurementTypeCode)&&(identical(other.procedureCode, procedureCode) || other.procedureCode == procedureCode)&&(identical(other.estimatedValue, estimatedValue) || other.estimatedValue == estimatedValue)&&(identical(other.currency, currency) || other.currency == currency)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.tenderDeadline, tenderDeadline) || other.tenderDeadline == tenderDeadline)&&(identical(other.submissionDeadline, submissionDeadline) || other.submissionDeadline == submissionDeadline)&&(identical(other.applicationDeadline, applicationDeadline) || other.applicationDeadline == applicationDeadline)&&(identical(other.submissionUrl, submissionUrl) || other.submissionUrl == submissionUrl)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)&&(identical(other.durationUnit, durationUnit) || other.durationUnit == durationUnit)&&(identical(other.buyerOrganization, buyerOrganization) || other.buyerOrganization == buyerOrganization)&&const DeepCollectionEquality().equals(other._lots, _lots)&&const DeepCollectionEquality().equals(other._officialLanguages, _officialLanguages)&&(identical(other.status, status) || other.status == status));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hashAll([runtimeType,success,message,id,tenderId,noticePublicationId,publicationDate,title,description,procurementTypeCode,procedureCode,estimatedValue,currency,duration,tenderDeadline,submissionDeadline,applicationDeadline,submissionUrl,countryCode,durationUnit,buyerOrganization,status]); int get hashCode => Object.hashAll([runtimeType,success,message,id,tenderId,noticePublicationId,noticeTypeCode,formType,noticeLanguageCode,issueDate,issueTime,publicationDate,title,description,procurementTypeCode,procedureCode,estimatedValue,currency,duration,tenderDeadline,submissionDeadline,applicationDeadline,submissionUrl,countryCode,durationUnit,buyerOrganization,const DeepCollectionEquality().hash(_lots),const DeepCollectionEquality().hash(_officialLanguages),status]);
@override @override
String toString() { String toString() {
return 'TenderData(success: $success, message: $message, id: $id, tenderId: $tenderId, noticePublicationId: $noticePublicationId, publicationDate: $publicationDate, title: $title, description: $description, procurementTypeCode: $procurementTypeCode, procedureCode: $procedureCode, estimatedValue: $estimatedValue, currency: $currency, duration: $duration, tenderDeadline: $tenderDeadline, submissionDeadline: $submissionDeadline, applicationDeadline: $applicationDeadline, submissionUrl: $submissionUrl, countryCode: $countryCode, durationUnit: $durationUnit, buyerOrganization: $buyerOrganization, status: $status)'; return 'TenderData(success: $success, message: $message, id: $id, tenderId: $tenderId, noticePublicationId: $noticePublicationId, noticeTypeCode: $noticeTypeCode, formType: $formType, noticeLanguageCode: $noticeLanguageCode, issueDate: $issueDate, issueTime: $issueTime, publicationDate: $publicationDate, title: $title, description: $description, procurementTypeCode: $procurementTypeCode, procedureCode: $procedureCode, estimatedValue: $estimatedValue, currency: $currency, duration: $duration, tenderDeadline: $tenderDeadline, submissionDeadline: $submissionDeadline, applicationDeadline: $applicationDeadline, submissionUrl: $submissionUrl, countryCode: $countryCode, durationUnit: $durationUnit, buyerOrganization: $buyerOrganization, lots: $lots, officialLanguages: $officialLanguages, status: $status)';
} }
@@ -299,7 +329,7 @@ abstract mixin class _$TenderDataCopyWith<$Res> implements $TenderDataCopyWith<$
factory _$TenderDataCopyWith(_TenderData value, $Res Function(_TenderData) _then) = __$TenderDataCopyWithImpl; factory _$TenderDataCopyWith(_TenderData value, $Res Function(_TenderData) _then) = __$TenderDataCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
bool? success, String? message, String? id,@JsonKey(name: 'tender_id') String? tenderId,@JsonKey(name: 'notice_publication_id') String? noticePublicationId,@JsonKey(name: 'publication_date') int? publicationDate, String? title, String? description,@JsonKey(name: 'procurement_type_code') String? procurementTypeCode,@JsonKey(name: 'procedure_code') String? procedureCode,@JsonKey(name: 'estimated_value') int? estimatedValue, String? currency, String? duration,@JsonKey(name: 'tender_deadline') int? tenderDeadline,@JsonKey(name: 'submission_deadline') int? submissionDeadline,@JsonKey(name: 'application_deadline') int? applicationDeadline,@JsonKey(name: 'submission_url') String? submissionUrl,@JsonKey(name: 'country_code') String? countryCode,@JsonKey(name: 'duration_unit') String? durationUnit,@JsonKey(name: 'buyer_organization') Organization? buyerOrganization, String? status bool? success, String? message, String? id,@JsonKey(name: 'tender_id') String? tenderId,@JsonKey(name: 'notice_publication_id') String? noticePublicationId,@JsonKey(name: 'notice_type_code') String? noticeTypeCode,@JsonKey(name: 'form_type') String? formType,@JsonKey(name: 'notice_language_code') String? noticeLanguageCode,@JsonKey(name: 'issue_date', fromJson: unixTimestampFromJson) int? issueDate,@JsonKey(name: 'issue_time', fromJson: unixTimestampFromJson) int? issueTime,@JsonKey(name: 'publication_date', fromJson: unixTimestampFromJson) int? publicationDate, String? title, String? description,@JsonKey(name: 'procurement_type_code') String? procurementTypeCode,@JsonKey(name: 'procedure_code') String? procedureCode,@JsonKey(name: 'estimated_value') int? estimatedValue, String? currency, String? duration,@JsonKey(name: 'tender_deadline', fromJson: unixTimestampFromJson) int? tenderDeadline,@JsonKey(name: 'submission_deadline', fromJson: unixTimestampFromJson) int? submissionDeadline,@JsonKey(name: 'application_deadline', fromJson: unixTimestampFromJson) int? applicationDeadline,@JsonKey(name: 'submission_url') String? submissionUrl,@JsonKey(name: 'country_code') String? countryCode,@JsonKey(name: 'duration_unit') String? durationUnit,@JsonKey(name: 'buyer_organization') Organization? buyerOrganization, List<Lot>? lots,@JsonKey(name: 'official_languages') List<String>? officialLanguages, String? status
}); });
@@ -316,14 +346,19 @@ class __$TenderDataCopyWithImpl<$Res>
/// Create a copy of TenderData /// Create a copy of TenderData
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? success = freezed,Object? message = freezed,Object? id = freezed,Object? tenderId = freezed,Object? noticePublicationId = freezed,Object? publicationDate = freezed,Object? title = freezed,Object? description = freezed,Object? procurementTypeCode = freezed,Object? procedureCode = freezed,Object? estimatedValue = freezed,Object? currency = freezed,Object? duration = freezed,Object? tenderDeadline = freezed,Object? submissionDeadline = freezed,Object? applicationDeadline = freezed,Object? submissionUrl = freezed,Object? countryCode = freezed,Object? durationUnit = freezed,Object? buyerOrganization = freezed,Object? status = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? success = freezed,Object? message = freezed,Object? id = freezed,Object? tenderId = freezed,Object? noticePublicationId = freezed,Object? noticeTypeCode = freezed,Object? formType = freezed,Object? noticeLanguageCode = freezed,Object? issueDate = freezed,Object? issueTime = freezed,Object? publicationDate = freezed,Object? title = freezed,Object? description = freezed,Object? procurementTypeCode = freezed,Object? procedureCode = freezed,Object? estimatedValue = freezed,Object? currency = freezed,Object? duration = freezed,Object? tenderDeadline = freezed,Object? submissionDeadline = freezed,Object? applicationDeadline = freezed,Object? submissionUrl = freezed,Object? countryCode = freezed,Object? durationUnit = freezed,Object? buyerOrganization = freezed,Object? lots = freezed,Object? officialLanguages = freezed,Object? status = freezed,}) {
return _then(_TenderData( return _then(_TenderData(
success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as bool?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable as bool?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
as String?,id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String?,id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String?,tenderId: freezed == tenderId ? _self.tenderId : tenderId // ignore: cast_nullable_to_non_nullable as String?,tenderId: freezed == tenderId ? _self.tenderId : tenderId // ignore: cast_nullable_to_non_nullable
as String?,noticePublicationId: freezed == noticePublicationId ? _self.noticePublicationId : noticePublicationId // ignore: cast_nullable_to_non_nullable as String?,noticePublicationId: freezed == noticePublicationId ? _self.noticePublicationId : noticePublicationId // ignore: cast_nullable_to_non_nullable
as String?,publicationDate: freezed == publicationDate ? _self.publicationDate : publicationDate // ignore: cast_nullable_to_non_nullable as String?,noticeTypeCode: freezed == noticeTypeCode ? _self.noticeTypeCode : noticeTypeCode // ignore: cast_nullable_to_non_nullable
as String?,formType: freezed == formType ? _self.formType : formType // ignore: cast_nullable_to_non_nullable
as String?,noticeLanguageCode: freezed == noticeLanguageCode ? _self.noticeLanguageCode : noticeLanguageCode // ignore: cast_nullable_to_non_nullable
as String?,issueDate: freezed == issueDate ? _self.issueDate : issueDate // ignore: cast_nullable_to_non_nullable
as int?,issueTime: freezed == issueTime ? _self.issueTime : issueTime // ignore: cast_nullable_to_non_nullable
as int?,publicationDate: freezed == publicationDate ? _self.publicationDate : publicationDate // ignore: cast_nullable_to_non_nullable
as int?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable as int?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,procurementTypeCode: freezed == procurementTypeCode ? _self.procurementTypeCode : procurementTypeCode // ignore: cast_nullable_to_non_nullable as String?,procurementTypeCode: freezed == procurementTypeCode ? _self.procurementTypeCode : procurementTypeCode // ignore: cast_nullable_to_non_nullable
@@ -338,7 +373,9 @@ as int?,submissionUrl: freezed == submissionUrl ? _self.submissionUrl : submissi
as String?,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable as String?,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable
as String?,durationUnit: freezed == durationUnit ? _self.durationUnit : durationUnit // ignore: cast_nullable_to_non_nullable as String?,durationUnit: freezed == durationUnit ? _self.durationUnit : durationUnit // ignore: cast_nullable_to_non_nullable
as String?,buyerOrganization: freezed == buyerOrganization ? _self.buyerOrganization : buyerOrganization // ignore: cast_nullable_to_non_nullable as String?,buyerOrganization: freezed == buyerOrganization ? _self.buyerOrganization : buyerOrganization // ignore: cast_nullable_to_non_nullable
as Organization?,status: freezed == status ? _self.status : status // ignore: cast_nullable_to_non_nullable as Organization?,lots: freezed == lots ? _self._lots : lots // ignore: cast_nullable_to_non_nullable
as List<Lot>?,officialLanguages: freezed == officialLanguages ? _self._officialLanguages : officialLanguages // ignore: cast_nullable_to_non_nullable
as List<String>?,status: freezed == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as String?, as String?,
)); ));
} }
@@ -12,6 +12,11 @@ _TenderData _$TenderDataFromJson(Map<String, dynamic> json) => _TenderData(
id: json['id'] as String?, id: json['id'] as String?,
tenderId: json['tender_id'] as String?, tenderId: json['tender_id'] as String?,
noticePublicationId: json['notice_publication_id'] as String?, noticePublicationId: json['notice_publication_id'] as String?,
noticeTypeCode: json['notice_type_code'] as String?,
formType: json['form_type'] as String?,
noticeLanguageCode: json['notice_language_code'] as String?,
issueDate: unixTimestampFromJson(json['issue_date']),
issueTime: unixTimestampFromJson(json['issue_time']),
publicationDate: unixTimestampFromJson(json['publication_date']), publicationDate: unixTimestampFromJson(json['publication_date']),
title: json['title'] as String?, title: json['title'] as String?,
description: json['description'] as String?, description: json['description'] as String?,
@@ -32,6 +37,14 @@ _TenderData _$TenderDataFromJson(Map<String, dynamic> json) => _TenderData(
: Organization.fromJson( : Organization.fromJson(
json['buyer_organization'] as Map<String, dynamic>, json['buyer_organization'] as Map<String, dynamic>,
), ),
lots:
(json['lots'] as List<dynamic>?)
?.map((e) => Lot.fromJson(e as Map<String, dynamic>))
.toList(),
officialLanguages:
(json['official_languages'] as List<dynamic>?)
?.map((e) => e as String)
.toList(),
status: json['status'] as String?, status: json['status'] as String?,
); );
@@ -42,6 +55,11 @@ Map<String, dynamic> _$TenderDataToJson(_TenderData instance) =>
'id': instance.id, 'id': instance.id,
'tender_id': instance.tenderId, 'tender_id': instance.tenderId,
'notice_publication_id': instance.noticePublicationId, 'notice_publication_id': instance.noticePublicationId,
'notice_type_code': instance.noticeTypeCode,
'form_type': instance.formType,
'notice_language_code': instance.noticeLanguageCode,
'issue_date': instance.issueDate,
'issue_time': instance.issueTime,
'publication_date': instance.publicationDate, 'publication_date': instance.publicationDate,
'title': instance.title, 'title': instance.title,
'description': instance.description, 'description': instance.description,
@@ -57,5 +75,7 @@ Map<String, dynamic> _$TenderDataToJson(_TenderData instance) =>
'country_code': instance.countryCode, 'country_code': instance.countryCode,
'duration_unit': instance.durationUnit, 'duration_unit': instance.durationUnit,
'buyer_organization': instance.buyerOrganization, 'buyer_organization': instance.buyerOrganization,
'lots': instance.lots,
'official_languages': instance.officialLanguages,
'status': instance.status, 'status': instance.status,
}; };
+33
View File
@@ -11,6 +11,38 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
class $AssetsFontsGen {
const $AssetsFontsGen();
/// File path: assets/fonts/Roboto-Black.ttf
String get robotoBlack => 'assets/fonts/Roboto-Black.ttf';
/// File path: assets/fonts/Roboto-Bold.ttf
String get robotoBold => 'assets/fonts/Roboto-Bold.ttf';
/// File path: assets/fonts/Roboto-Light.ttf
String get robotoLight => 'assets/fonts/Roboto-Light.ttf';
/// File path: assets/fonts/Roboto-Medium.ttf
String get robotoMedium => 'assets/fonts/Roboto-Medium.ttf';
/// File path: assets/fonts/Roboto-Regular.ttf
String get robotoRegular => 'assets/fonts/Roboto-Regular.ttf';
/// File path: assets/fonts/Roboto-Thin.ttf
String get robotoThin => 'assets/fonts/Roboto-Thin.ttf';
/// List of all assets
List<String> get values => [
robotoBlack,
robotoBold,
robotoLight,
robotoMedium,
robotoRegular,
robotoThin,
];
}
class $AssetsIconsGen { class $AssetsIconsGen {
const $AssetsIconsGen(); const $AssetsIconsGen();
@@ -301,6 +333,7 @@ class $AssetsSvgsGen {
class Assets { class Assets {
const Assets._(); const Assets._();
static const $AssetsFontsGen fonts = $AssetsFontsGen();
static const $AssetsIconsGen icons = $AssetsIconsGen(); static const $AssetsIconsGen icons = $AssetsIconsGen();
static const $AssetsPngsGen pngs = $AssetsPngsGen(); static const $AssetsPngsGen pngs = $AssetsPngsGen();
static const $AssetsSvgsGen svgs = $AssetsSvgsGen(); static const $AssetsSvgsGen svgs = $AssetsSvgsGen();
+16
View File
@@ -0,0 +1,16 @@
// dart format width=80
/// GENERATED CODE - DO NOT MODIFY BY HAND
/// *****************************************************
/// FlutterGen
/// *****************************************************
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: deprecated_member_use,directives_ordering,implicit_dynamic_list_literal,unnecessary_import
class FontFamily {
FontFamily._();
/// Font family: Roboto
static const String roboto = 'Roboto';
}
+1 -93
View File
@@ -39,42 +39,12 @@ class TendersViewModel with ChangeNotifier {
static const int _pageSize = 10; static const int _pageSize = 10;
// --- Offset pagination state --- // --- Infinite-scroll offset state ---
int _currentOffset = 0; int _currentOffset = 0;
int _lastValidPages = 1;
int _lastValidLimit = _pageSize;
bool _hasMoreData = true; bool _hasMoreData = true;
bool get hasMoreData => _hasMoreData; bool get hasMoreData => _hasMoreData;
/// 0-based current page index.
int get currentPageIndex {
final meta = _tendersResponse?.meta;
final limit = meta?.limit ?? _lastValidLimit;
if (limit > 0) {
return _currentOffset ~/ limit;
}
if (meta?.offset != null && limit > 0) {
return meta!.offset! ~/ limit;
}
if (meta?.page != null) {
return meta!.page! - 1;
}
return 0;
}
/// 1-based current page (for display).
int get currentPage => currentPageIndex + 1;
int get totalPages {
final pages = _tendersResponse?.meta?.pages;
if (pages != null && pages > 0) {
return pages;
}
return _lastValidPages;
}
// Filter States // Filter States
DateTimeRange? selectedDateRange; DateTimeRange? selectedDateRange;
String selectedStatus = 'All'; String selectedStatus = 'All';
@@ -233,12 +203,6 @@ class TendersViewModel with ChangeNotifier {
case Ok<TendersResponse>(): case Ok<TendersResponse>():
_tendersResponse = result.value; _tendersResponse = result.value;
final meta = _tendersResponse?.meta; final meta = _tendersResponse?.meta;
if (meta?.pages != null && meta!.pages! > 0) {
_lastValidPages = meta.pages!;
}
if (meta?.limit != null && meta!.limit! > 0) {
_lastValidLimit = meta.limit!;
}
if (meta?.offset != null) { if (meta?.offset != null) {
_currentOffset = meta!.offset!; _currentOffset = meta!.offset!;
} }
@@ -299,12 +263,6 @@ class TendersViewModel with ChangeNotifier {
data: result.value.data?.copyWith(tenders: combined), data: result.value.data?.copyWith(tenders: combined),
); );
final newMeta = result.value.meta; final newMeta = result.value.meta;
if (newMeta?.pages != null && newMeta!.pages! > 0) {
_lastValidPages = newMeta.pages!;
}
if (newMeta?.limit != null && newMeta!.limit! > 0) {
_lastValidLimit = newMeta.limit!;
}
_hasMoreData = _hasMoreData =
newMeta?.hasMore ?? (newTenders.length >= _pageSize); newMeta?.hasMore ?? (newTenders.length >= _pageSize);
} else { } else {
@@ -492,54 +450,4 @@ class TendersViewModel with ChangeNotifier {
sortOrder = ''; sortOrder = '';
notifyListeners(); notifyListeners();
} }
/// [selected] is the absolute 0-based target page index.
Future<void> handlePaginationChange(int selected) async {
if (selected < 0) {
return;
}
final maxPage = totalPages - 1;
if (maxPage >= 0 && selected > maxPage) {
return;
}
if (selected == currentPageIndex) {
return;
}
await _goToOffsetPage(selected);
}
Future<void> _goToOffsetPage(int pageIndex) async {
final limit = _tendersResponse?.meta?.limit ?? _pageSize;
_currentOffset = pageIndex * limit;
await _fetchPage();
}
Future<void> _fetchPage() async {
_isLoading = true;
notifyListeners();
final result = await _tendersRepository.getTenders(
limit: _pageSize,
offset: _currentOffset,
query: _searchQuery,
sortBy: sortBy,
sortOrder: sortOrder,
publicationDateFrom: startDateUnix,
publicationDateTo: endDateUnix,
);
_handleListResult(result, fetchAllFeedbacks: true);
_isLoading = false;
notifyListeners();
_errorMessage = null;
notifyListeners();
}
/// Kept for backwards compatibility with existing callers.
/// Accepts a 1-based page number.
void jumpToPage(int page) {
handlePaginationChange(page - 1);
}
} }
@@ -4,7 +4,6 @@ import 'package:tm_app/core/theme/colors.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/tender_detail_view_model.dart'; import 'package:tm_app/view_models/tender_detail_view_model.dart';
import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_action.dart';
import 'package:tm_app/views/detail/widgets/tender_detail_card.dart';
import 'package:tm_app/views/detail/widgets/tender_detail_header.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_header.dart';
import '../../../core/constants/tender_approval_status.dart'; import '../../../core/constants/tender_approval_status.dart';
@@ -121,8 +120,6 @@ class _TenderDetailDesktopPageState extends State<TenderDetailDesktopPage> {
detail: detail, detail: detail,
), ),
SizedBox(height: 32.0.h()), SizedBox(height: 32.0.h()),
TenderDetailCard(detail: detail),
SizedBox(height: 24.0.h()),
TenderDetailActions( TenderDetailActions(
isScreenBig: true, isScreenBig: true,
detail: detail, detail: detail,
@@ -5,7 +5,6 @@ import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/view_models/tender_detail_view_model.dart'; import 'package:tm_app/view_models/tender_detail_view_model.dart';
import 'package:tm_app/views/detail/strings/tender_details_strings.dart'; import 'package:tm_app/views/detail/strings/tender_details_strings.dart';
import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_action.dart';
import 'package:tm_app/views/detail/widgets/tender_detail_card.dart';
import 'package:tm_app/views/detail/widgets/tender_detail_header.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_header.dart';
import 'package:tm_app/views/shared/tender_app_bar.dart'; import 'package:tm_app/views/shared/tender_app_bar.dart';
@@ -112,8 +111,6 @@ class _TenderDetailMobilePageState extends State<TenderDetailMobilePage> {
children: [ children: [
TenderDetailHeader(isScreenBig: false, detail: detail), TenderDetailHeader(isScreenBig: false, detail: detail),
SizedBox(height: 24.0.h()), SizedBox(height: 24.0.h()),
TenderDetailCard(detail: detail),
SizedBox(height: 24.0.h()),
TenderDetailActions( TenderDetailActions(
isScreenBig: false, isScreenBig: false,
detail: detail, detail: detail,
@@ -4,7 +4,6 @@ import 'package:tm_app/core/theme/colors.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/tender_detail_view_model.dart'; import 'package:tm_app/view_models/tender_detail_view_model.dart';
import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_action.dart';
import 'package:tm_app/views/detail/widgets/tender_detail_card.dart';
import 'package:tm_app/views/detail/widgets/tender_detail_header.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_header.dart';
import 'package:tm_app/views/shared/tablet_navigation_widget.dart'; import 'package:tm_app/views/shared/tablet_navigation_widget.dart';
@@ -123,8 +122,6 @@ class _TenderDetailTabletPageState extends State<TenderDetailTabletPage> {
SizedBox(height: 28.0.h()), SizedBox(height: 28.0.h()),
TenderDetailHeader(isScreenBig: true, detail: detail), TenderDetailHeader(isScreenBig: true, detail: detail),
SizedBox(height: 28.0.h()), SizedBox(height: 28.0.h()),
TenderDetailCard(detail: detail),
SizedBox(height: 24.0.h()),
TenderDetailActions( TenderDetailActions(
isScreenBig: true, isScreenBig: true,
detail: detail, detail: detail,
@@ -42,4 +42,35 @@ class TenderDetailsStrings {
static const String requestSuccessfullyRegistered = static const String requestSuccessfullyRegistered =
'Your request has been successfully registered.'; 'Your request has been successfully registered.';
static const String confirmAndClose = 'Confirm and close'; static const String confirmAndClose = 'Confirm and close';
// Toast copy shown after the user confirms the Accept/submission modal
// (see tender_detail_action.dart).
static const String submissionReceived =
'Thank you. We have received your submission and will be in touch with you shortly.';
// Field labels for the tender-details info section (one per backend field
// rendered there).
// Tender detail field labels
static const String noticeTypeCode = 'Notice Type';
static const String formType = 'Form Type';
static const String noticeLanguageCode = 'Notice Language';
static const String issueDate = 'Issue Date';
static const String issueTime = 'Issue Time';
static const String procedureCode = 'Procedure';
static const String durationLabel = 'Duration';
static const String publicationDate = 'Publication Date';
static const String tenderDeadlineDate = 'Tender Deadline';
static const String submissionDeadline = 'Submission Deadline';
static const String applicationDeadline = 'Application Deadline';
static const String countryCode = 'Country';
static const String buyerName = 'Buyer';
static const String companyId = 'Company ID';
static const String buyerCity = 'City';
static const String buyerCountry = 'Buyer Country';
static const String officialLanguages = 'Official Languages';
static const String status = 'Status';
static const String tenderId = 'Tender ID';
static const String lots = 'Lots';
static const String lotId = 'Lot ID';
static const String lotTitle = 'Title';
static const String lotDescription = 'Description';
} }
@@ -1,84 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:tm_app/core/utils/size_config.dart';
import '../../../core/theme/colors.dart';
class DetailDropDown extends StatefulWidget {
final String title;
final List<String> items;
const DetailDropDown({required this.title, required this.items, super.key});
@override
State<DetailDropDown> createState() => _DetailDropDownState();
}
class _DetailDropDownState extends State<DetailDropDown>
with SingleTickerProviderStateMixin {
bool isOpen = true;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
InkWell(
onTap: () {
setState(() {
isOpen = !isOpen;
});
},
child: Container(
width: 24.0.w(),
height: 24.0.h(),
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: AppColors.grey60),
),
alignment: Alignment.center,
child: Icon(
isOpen
? CupertinoIcons.chevron_up
: CupertinoIcons.chevron_down,
color: AppColors.grey60,
size: 12,
),
),
),
SizedBox(width: 8.0.w()),
Text(
widget.title,
style: TextStyle(
color: AppColors.grey80,
fontSize: 16.0.sp(),
fontWeight: FontWeight.w600,
),
),
],
),
SizedBox(height: 12.0.h()),
isOpen
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:
widget.items
.map(
(e) => Padding(
padding: EdgeInsets.only(left: 12.0.w()),
child: Text(e),
),
)
.toList(),
)
: const SizedBox.shrink(),
],
);
}
}
@@ -4,14 +4,11 @@ import 'package:tm_app/core/theme/colors.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/auth_view_model.dart'; import 'package:tm_app/view_models/auth_view_model.dart';
import 'package:tm_app/view_models/tender_detail_view_model.dart'; import 'package:tm_app/view_models/tender_detail_view_model.dart';
import 'package:tm_app/views/detail/widgets/meeting_time_bottom_sheet.dart';
import 'package:tm_app/views/detail/widgets/meeting_time_dialog.dart';
import 'package:tm_app/views/shared/base_button.dart'; import 'package:tm_app/views/shared/base_button.dart';
import 'package:tm_app/views/shared/select_submission_bottom_sheet.dart'; import 'package:tm_app/views/shared/select_submission_bottom_sheet.dart';
import '../../../core/constants/tender_approval_status.dart'; import '../../../core/constants/tender_approval_status.dart';
import '../../../core/constants/tender_submision_mode.dart'; import '../../../core/constants/tender_submision_mode.dart';
import '../../../core/routes/app_routes.dart';
import '../../../core/utils/app_toast.dart'; import '../../../core/utils/app_toast.dart';
import '../../../data/services/model/tender_data/tender_data.dart'; import '../../../data/services/model/tender_data/tender_data.dart';
import '../../shared/select_submission_dialog.dart'; import '../../shared/select_submission_dialog.dart';
@@ -68,38 +65,10 @@ class _TenderDetailActionsState extends State<TenderDetailActions> {
builder: (context) { builder: (context) {
return SelectSubmissionBottomSheet( return SelectSubmissionBottomSheet(
onConfirm: (String value) { onConfirm: (String value) {
if (value == // Confirming the submission modal shows a
TenderSubmissionMode.selfApply.value) { // confirmation toast (no API call, no
// viewModel.submitTenderApproval( // navigation), per product request.
// tenderId: widget.detail.id!, _showSubmissionReceivedToast();
// submissionMode: value,
// );
CompletionOfDocumentsRouteData(
tenderId: widget.detail.id!,
).push(context);
} else if (value ==
TenderSubmissionMode.partnership.value) {
Future.delayed(Duration.zero, () {
if (context.mounted) {
showModalBottomSheet(
context: context,
builder: (_) {
return MeetingTimeBottomSheet(
onConfirm: (value) {
// AppToast.success(
// context,
// TenderDetailsStrings
// .meetingTimeSuccessfullySet,
// );
},
);
},
);
}
});
}
}, },
); );
}, },
@@ -143,6 +112,16 @@ class _TenderDetailActionsState extends State<TenderDetailActions> {
); );
} }
// Shows a success toast confirming the submission was received. Guards on
// `mounted` because it runs from a modal callback after the sheet/dialog is
// popped, and uses the State's own (stable page) context, not the modal's.
void _showSubmissionReceivedToast() {
if (!mounted) {
return;
}
AppToast.success(context, TenderDetailsStrings.submissionReceived);
}
Row _webActions(Role userRole) { Row _webActions(Role userRole) {
return Row( return Row(
children: [ children: [
@@ -206,33 +185,7 @@ class _TenderDetailActionsState extends State<TenderDetailActions> {
builder: (context) { builder: (context) {
return SelectSubmissionDialog( return SelectSubmissionDialog(
onConfirm: (String value) { onConfirm: (String value) {
if (value == _showSubmissionReceivedToast();
TenderSubmissionMode.selfApply.value) {
if (context.mounted) {
if (userRole == Role.analyst) {
const FinalCompletionOfDocumentsRouteData()
.push(context);
} else {
CompletionOfDocumentsRouteData(
tenderId: widget.detail.id!,
).push(context);
}
}
} else if (value ==
TenderSubmissionMode.partnership.value) {
Future.delayed(Duration.zero, () {
if (context.mounted) {
showDialog(
context: context,
builder: (context) {
return MeetingTimeDialog(
onConfirm: (value) {},
);
},
);
}
});
}
}, },
); );
}, },
@@ -242,41 +195,7 @@ class _TenderDetailActionsState extends State<TenderDetailActions> {
builder: (context) { builder: (context) {
return SelectSubmissionBottomSheet( return SelectSubmissionBottomSheet(
onConfirm: (String value) { onConfirm: (String value) {
if (value == _showSubmissionReceivedToast();
TenderSubmissionMode.selfApply.value) {
// viewModel.submitTenderApproval(
// tenderId: widget.detail.id!,
// submissionMode: value,
// );
if (context.mounted) {
const FinalCompletionOfDocumentsRouteData()
.push(context);
}
} else if (value ==
TenderSubmissionMode.partnership.value) {
showDialog(
context: context,
builder: (context) {
return MeetingTimeBottomSheet(
onConfirm: (value) {
AppToast.success(
context,
TenderDetailsStrings
.meetingTimeSuccessfullySet,
);
},
);
// return MeetingTimeDialog(
// onConfirm: (String value) {
// if (context.mounted) {
// const CompletionOfDocumentsRouteData()
// .push(context);
// }
// },
// );
},
);
}
}, },
); );
}, },
@@ -1,95 +0,0 @@
import 'package:flutter/material.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/tender_data/tender_data.dart';
import 'package:tm_app/views/detail/strings/tender_details_strings.dart';
class TenderDetailCard extends StatelessWidget {
final TenderData detail;
const TenderDetailCard({required this.detail, super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.paleOrange,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColors.veryPaleOrange),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
TenderDetailsStrings.tenderMatchProfile,
style: TextStyle(
fontSize: 12.0.sp(),
color: AppColors.grey80,
fontWeight: FontWeight.w400,
),
),
Text(
'75%',
style: TextStyle(
fontSize: 12.0.sp(),
fontWeight: FontWeight.w600,
color: AppColors.jellyBean,
),
),
],
),
SizedBox(height: 6.0.h()),
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: 0.75,
backgroundColor: AppColors.grey20,
valueColor: const AlwaysStoppedAnimation<Color>(
AppColors.jellyBean,
),
minHeight: 6.0.h(),
),
),
SizedBox(height: 16.0.h()),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(Icons.warning_rounded, color: AppColors.red),
SizedBox(width: 8.0.w()),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
TenderDetailsStrings.tenderIncompleteResume,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0.sp(),
color: AppColors.grey80,
),
),
SizedBox(height: 4.0.h()),
Text(
TenderDetailsStrings.tenderNoExperience,
style: TextStyle(
fontSize: 14.0.sp(),
color: AppColors.grey70,
fontWeight: FontWeight.w400,
),
),
],
),
),
],
),
],
),
);
}
}
@@ -9,7 +9,6 @@ import 'package:tm_app/views/detail/widgets/tender_detail_info_section.dart';
import '../../../core/constants/assets.dart'; import '../../../core/constants/assets.dart';
import '../../shared/flag.dart'; import '../../shared/flag.dart';
import '../strings/tender_details_strings.dart'; import '../strings/tender_details_strings.dart';
import 'detail_drop_down.dart';
class TenderDetailHeader extends StatelessWidget { class TenderDetailHeader extends StatelessWidget {
final bool isScreenBig; final bool isScreenBig;
@@ -41,44 +40,17 @@ class TenderDetailHeader extends StatelessWidget {
SizedBox(height: 16.0.h()), SizedBox(height: 16.0.h()),
_locationBudgetRow(), _locationBudgetRow(),
SizedBox(height: 24.0.h()), SizedBox(height: 24.0.h()),
_projectId(),
SizedBox(height: 12.0.h()),
_winningChanceAndDificulty(),
SizedBox(height: 24.0.h()),
_keyRisksDropDown(),
SizedBox(height: 24.0.h()),
Divider(color: AppColors.grey20), Divider(color: AppColors.grey20),
TenderDetailInfoSection(isScreenBig: isScreenBig, detail: detail), TenderDetailInfoSection(isScreenBig: isScreenBig, detail: detail),
SizedBox(height: 24.0.h()), SizedBox(height: 24.0.h()),
_overViewTitle(), _overViewTitle(),
SizedBox(height: 12.0.h()), SizedBox(height: 12.0.h()),
_descriptionText(), _descriptionText(),
SizedBox(height: 24.0.h()),
_serviceDropDown(),
SizedBox(height: 24.0.h()),
_requirementsDropDown(),
SizedBox(height: 24.0.h()),
_backgroundDropDown(),
], ],
), ),
); );
} }
Widget _serviceDropDown() =>
const DetailDropDown(title: 'Servive/Product', items: []);
Widget _backgroundDropDown() =>
const DetailDropDown(title: 'Background', items: []);
Widget _requirementsDropDown() {
return const DetailDropDown(
title: 'Requirements (Summary)',
items: [
'Market consultation for selecting and procuring a Customer Data Platform (CDP) to unify student data from multiple sources and improve personalization and marketing.',
],
);
}
Widget _descriptionText() { Widget _descriptionText() {
return Text( return Text(
detail.description ?? '', detail.description ?? '',
@@ -101,82 +73,6 @@ class TenderDetailHeader extends StatelessWidget {
); );
} }
Widget _keyRisksDropDown() {
return const DetailDropDown(
title: 'Key risks & dealbreakers',
items: [
'ISO 27001:2022 likely mandatory for future tender.',
'Lack of past performance — public sector values references.',
'Implementation & ongoing support required (15 person team expectation).',
],
);
}
Widget _winningChanceAndDificulty() {
return Row(
children: [
Container(
height: 24.0.h(),
padding: EdgeInsets.symmetric(
horizontal: 16.0.w(),
vertical: 4.0.h(),
),
decoration: BoxDecoration(
color: AppColors.blue0,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Winning chance: <5%',
style: TextStyle(
color: AppColors.blue10,
fontSize: 12.0.sp(),
fontWeight: FontWeight.w500,
),
),
),
SizedBox(width: 12.0.w()),
Container(
height: 24.0.h(),
padding: EdgeInsets.symmetric(
horizontal: 16.0.w(),
vertical: 4.0.h(),
),
decoration: BoxDecoration(
color: AppColors.blue0,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Difficulty 2/5',
style: TextStyle(
color: AppColors.blue10,
fontSize: 12.0.sp(),
fontWeight: FontWeight.w500,
),
),
),
],
);
}
Widget _projectId() {
return Container(
height: 24.0.h(),
padding: EdgeInsets.symmetric(horizontal: 16.0.w(), vertical: 4.0.h()),
decoration: BoxDecoration(
color: AppColors.blue0,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Project ID 6243372025NLD',
style: TextStyle(
color: AppColors.blue10,
fontSize: 12.0.sp(),
fontWeight: FontWeight.w500,
),
),
);
}
Widget _locationBudgetRow() { Widget _locationBudgetRow() {
return Row( return Row(
children: [ children: [
@@ -1,10 +1,19 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tm_app/core/theme/colors.dart';
import 'package:tm_app/core/utils/date_utils.dart';
import 'package:tm_app/core/utils/price_extension.dart'; import 'package:tm_app/core/utils/price_extension.dart';
import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/data/services/model/tender_data/tender_data.dart'; import 'package:tm_app/data/services/model/tender_data/tender_data.dart';
import 'package:tm_app/views/detail/strings/tender_details_strings.dart'; import 'package:tm_app/views/detail/strings/tender_details_strings.dart';
import 'package:tm_app/views/detail/widgets/deadline_item.dart'; import 'package:tm_app/views/detail/widgets/deadline_item.dart';
import 'package:tm_app/views/detail/widgets/info_item.dart'; import 'package:tm_app/views/detail/widgets/info_item.dart';
// Renders only the backend-driven fields for the tender-details page (notice
// type, form type, languages, issue/publication dates, procedure, value,
// duration, deadlines, buyer org/address, lots, status, ids), using
// InfoItem/DeadlineItem. The _infoItem() helper returns an empty list when a
// value is null/blank so empty fields don't render as blank rows (this is why
// each entry is spread with `...`).
class TenderDetailInfoSection extends StatelessWidget { class TenderDetailInfoSection extends StatelessWidget {
final bool isScreenBig; final bool isScreenBig;
final TenderData detail; final TenderData detail;
@@ -17,24 +26,114 @@ class TenderDetailInfoSection extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final buyer = detail.buyerOrganization;
final address = buyer?.address;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
DeadlineItem(detail: detail, isScreenBig: isScreenBig), DeadlineItem(detail: detail, isScreenBig: isScreenBig),
InfoItem( ..._infoItem(TenderDetailsStrings.noticeTypeCode, detail.noticeTypeCode),
title: TenderDetailsStrings.tenderClientLabel, ..._infoItem(TenderDetailsStrings.formType, detail.formType),
value: detail.buyerOrganization!.name ?? '', ..._infoItem(
TenderDetailsStrings.noticeLanguageCode,
detail.noticeLanguageCode,
), ),
InfoItem( ..._infoItem(
title: TenderDetailsStrings.estimatedValue, TenderDetailsStrings.issueDate,
value: _date(detail.issueDate),
'${detail.estimatedValue?.formattedPrice} ${detail.currency ?? ''}',
), ),
InfoItem( ..._infoItem(
title: TenderDetailsStrings.tenderReferenceNumberLabel, TenderDetailsStrings.issueTime,
value: detail.noticePublicationId ?? '', _dateTime(detail.issueTime),
), ),
..._infoItem(TenderDetailsStrings.procedureCode, detail.procedureCode),
..._infoItem(
TenderDetailsStrings.estimatedValue,
detail.estimatedValue == null
? null
: '${detail.estimatedValue?.formattedPrice} ${detail.currency ?? ''}',
),
..._infoItem(TenderDetailsStrings.durationLabel, _duration()),
..._infoItem(
TenderDetailsStrings.publicationDate,
_date(detail.publicationDate),
),
..._infoItem(
TenderDetailsStrings.tenderDeadlineDate,
_date(detail.tenderDeadline),
),
..._infoItem(TenderDetailsStrings.countryCode, detail.countryCode),
..._infoItem(TenderDetailsStrings.buyerName, buyer?.name),
..._infoItem(TenderDetailsStrings.companyId, buyer?.companyId),
..._infoItem(TenderDetailsStrings.buyerCity, address?.cityName),
..._infoItem(TenderDetailsStrings.buyerCountry, address?.countryCode),
..._infoItem(
TenderDetailsStrings.officialLanguages,
(detail.officialLanguages == null ||
detail.officialLanguages!.isEmpty)
? null
: detail.officialLanguages!.join(', '),
),
..._infoItem(TenderDetailsStrings.status, detail.status),
..._infoItem(TenderDetailsStrings.tenderId, detail.tenderId),
..._lotsSection(),
], ],
); );
} }
/// Returns an [InfoItem] only when [value] has content, so empty fields are
/// not rendered as blank rows.
List<Widget> _infoItem(String title, String? value) {
if (value == null || value.trim().isEmpty) {
return const [];
}
return [InfoItem(title: title, value: value)];
}
List<Widget> _lotsSection() {
final lots = detail.lots;
if (lots == null || lots.isEmpty) {
return const [];
}
return [
SizedBox(height: 8.0.h()),
Text(
TenderDetailsStrings.lots,
style: TextStyle(
color: AppColors.grey80,
fontWeight: FontWeight.w600,
fontSize: 16.0.sp(),
),
),
SizedBox(height: 8.0.h()),
for (final lot in lots) ...[
..._infoItem(TenderDetailsStrings.lotId, lot.lotId),
..._infoItem(TenderDetailsStrings.lotTitle, lot.title),
..._infoItem(TenderDetailsStrings.lotDescription, lot.description),
],
];
}
String? _duration() {
final duration = detail.duration?.trim() ?? '';
final unit = detail.durationUnit?.trim() ?? '';
final combined = '$duration $unit'.trim();
return combined.isEmpty ? null : combined;
}
String? _date(int? unix) {
if (unix == null) {
return null;
}
return timeConvertor(unix);
}
String? _dateTime(int? unix) {
if (unix == null) {
return null;
}
return timeConvertor(unix, hasTime: true);
}
} }
@@ -87,25 +87,9 @@ class DesktopNavigationWidget extends StatelessWidget {
iconPath: AssetsManager.tenders, iconPath: AssetsManager.tenders,
activeIconPath: AssetsManager.tendersActive, activeIconPath: AssetsManager.tendersActive,
), ),
const SizedBox(width: 24), // Board tab is intentionally hidden from desktop navigation. The
_navigationItem( // Board route/page itself remains intact; the nav item (at tab index
context: context, // 2) can be restored from git history when the feature is re-enabled.
text: TendersStrings.board,
isActive: currentIndex == 2,
onTap: () {
if (currentIndex == 2) {
return;
} else {
Router.neglect(
context,
() => const BoardRouteData().go(context),
);
// context.read<TendersViewModel>().getTenders();
}
},
iconPath: AssetsManager.board,
activeIconPath: AssetsManager.boardActive,
),
const SizedBox(width: 24), const SizedBox(width: 24),
_navigationItem( _navigationItem(
context: context, context: context,
+1 -17
View File
@@ -13,7 +13,6 @@ import 'package:tm_app/views/tenders/strings/tenders_strings.dart';
import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart'; import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart';
import 'package:tm_app/views/tenders/widgets/tenders_filter_dialog.dart'; import 'package:tm_app/views/tenders/widgets/tenders_filter_dialog.dart';
import 'package:tm_app/views/tenders/widgets/tenders_sort_dialog.dart'; import 'package:tm_app/views/tenders/widgets/tenders_sort_dialog.dart';
import 'package:tm_app/views/tenders/widgets/windowed_pagination.dart';
import '../../../core/constants/common_strings.dart'; import '../../../core/constants/common_strings.dart';
import '../../../core/utils/app_toast.dart'; import '../../../core/utils/app_toast.dart';
@@ -78,13 +77,9 @@ class _DesktopTendersPageState extends State<DesktopTendersPage> {
return const Center(child: Text(CommonStrings.noData)); return const Center(child: Text(CommonStrings.noData));
} }
return Column( return Center(
children: [
Expanded(
child: SingleChildScrollView(
child: SizedBox( child: SizedBox(
width: 740, width: 740,
height: 800,
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(vertical: 4.0.h()), padding: EdgeInsets.symmetric(vertical: 4.0.h()),
child: MainTendersSlider( child: MainTendersSlider(
@@ -93,17 +88,6 @@ class _DesktopTendersPageState extends State<DesktopTendersPage> {
), ),
), ),
), ),
),
),
SizedBox(
width: 740,
child: WindowedPagination(
currentPage: vm.currentPageIndex,
totalPages: vm.totalPages,
onPageChanged: vm.handlePaginationChange,
),
),
],
); );
}, },
), ),
+2 -19
View File
@@ -9,7 +9,6 @@ import '../../../core/utils/app_toast.dart';
import '../../shared/tender_app_bar.dart'; import '../../shared/tender_app_bar.dart';
import '../strings/tenders_strings.dart'; import '../strings/tenders_strings.dart';
import '../widgets/main_tenders_slider.dart'; import '../widgets/main_tenders_slider.dart';
import '../widgets/windowed_pagination.dart';
class MobileTendersPage extends StatefulWidget { class MobileTendersPage extends StatefulWidget {
const MobileTendersPage({super.key}); const MobileTendersPage({super.key});
@@ -62,29 +61,13 @@ class _MobileTendersPageState extends State<MobileTendersPage> {
return const Center(child: Text(CommonStrings.noData)); return const Center(child: Text(CommonStrings.noData));
} }
return Column( return SingleChildScrollView(
children: [
Expanded(
child: SingleChildScrollView(
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(vertical: 18.0.h()), padding: EdgeInsets.symmetric(vertical: 18.0.h()),
child: MainTendersSlider( child: MainTendersSlider(
key: ValueKey( tenders: viewModel.tendersResponse?.data?.tenders ?? [],
'tenders-slider-${viewModel.currentPageIndex}',
),
tenders:
viewModel.tendersResponse?.data?.tenders ?? [],
), ),
), ),
),
),
WindowedPagination(
currentPage: viewModel.currentPageIndex,
totalPages: viewModel.totalPages,
onPageChanged: viewModel.handlePaginationChange,
compact: true,
),
],
); );
}, },
), ),
+1 -16
View File
@@ -5,7 +5,6 @@ import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/view_models/tenders_view_model.dart'; import 'package:tm_app/view_models/tenders_view_model.dart';
import 'package:tm_app/views/shared/tablet_navigation_widget.dart'; import 'package:tm_app/views/shared/tablet_navigation_widget.dart';
import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart'; import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart';
import 'package:tm_app/views/tenders/widgets/windowed_pagination.dart';
import '../../../core/constants/common_strings.dart'; import '../../../core/constants/common_strings.dart';
import '../../shared/tender_app_bar.dart'; import '../../shared/tender_app_bar.dart';
@@ -54,30 +53,16 @@ class _TabletTendersPageState extends State<TabletTendersPage> {
viewModel.isLoading == false) { viewModel.isLoading == false) {
return const Center(child: Text(CommonStrings.noData)); return const Center(child: Text(CommonStrings.noData));
} }
return Column( return SingleChildScrollView(
children: [
Expanded(
child: SingleChildScrollView(
child: Padding( child: Padding(
padding: EdgeInsetsDirectional.only( padding: EdgeInsetsDirectional.only(
end: 24.0.w(), end: 24.0.w(),
top: 128.0.h(), top: 128.0.h(),
), ),
child: MainTendersSlider( child: MainTendersSlider(
key: ValueKey(
'tenders-slider-${viewModel.currentPageIndex}',
),
tenders: viewModel.tendersResponse?.data?.tenders ?? [], tenders: viewModel.tendersResponse?.data?.tenders ?? [],
), ),
), ),
),
),
WindowedPagination(
currentPage: viewModel.currentPageIndex,
totalPages: viewModel.totalPages,
onPageChanged: viewModel.handlePaginationChange,
),
],
); );
}, },
), ),
@@ -25,8 +25,46 @@ class MainTendersSlider extends StatefulWidget {
class _MainTendersSliderState extends State<MainTendersSlider> { class _MainTendersSliderState extends State<MainTendersSlider> {
final PageController pageController = PageController(initialPage: 0); final PageController pageController = PageController(initialPage: 0);
final ScrollController _scrollController = ScrollController();
int currentPage = 1; int currentPage = 1;
/// Start prefetching the next page once this fraction of the currently
/// loaded items has been scrolled/swiped past.
static const double _prefetchFraction = 0.5;
@override
void initState() {
super.initState();
// The scroll controller is only attached to the desktop ListView, so only
// register the prefetch listener there. Mobile prefetching is driven by
// PageView's onPageChanged instead.
if (widget.isDesktop) {
_scrollController.addListener(_onScroll);
}
}
void _onScroll() {
if (!_scrollController.hasClients) {
return;
}
final position = _scrollController.position;
if (position.maxScrollExtent <= 0) {
return;
}
if (position.pixels >= position.maxScrollExtent * _prefetchFraction) {
_maybeLoadMore();
}
}
void _maybeLoadMore() {
final viewModel = context.read<TendersViewModel>();
if (viewModel.hasMoreData &&
!viewModel.isLoadingMore &&
!viewModel.isLoading) {
viewModel.loadMoreTenders();
}
}
void _goToPreviousPage() { void _goToPreviousPage() {
if (currentPage == 1) { if (currentPage == 1) {
return; return;
@@ -82,6 +120,7 @@ class _MainTendersSliderState extends State<MainTendersSlider> {
@override @override
void dispose() { void dispose() {
pageController.dispose(); pageController.dispose();
_scrollController.dispose();
super.dispose(); super.dispose();
} }
@@ -106,6 +145,10 @@ class _MainTendersSliderState extends State<MainTendersSlider> {
controller: pageController, controller: pageController,
itemCount: widget.tenders.length, itemCount: widget.tenders.length,
onPageChanged: (index) { onPageChanged: (index) {
// currentPage is still the previous page here; a forward swipe means
// the new (1-indexed) page is greater than the old one.
final bool isForwardSwipe = index + 1 > currentPage;
setState(() { setState(() {
currentPage = index + 1; currentPage = index + 1;
}); });
@@ -115,6 +158,15 @@ class _MainTendersSliderState extends State<MainTendersSlider> {
if (!viewModel.hasFeedbackForTender(id)) { if (!viewModel.hasFeedbackForTender(id)) {
viewModel.getTenderFeedback(id); viewModel.getTenderFeedback(id);
} }
// Infinite scroll: once we swipe forward past the halfway point of the
// loaded tenders, prefetch the next page in the background. Skip
// back-swipes so we don't refetch while the user reviews earlier
// tenders.
if (isForwardSwipe &&
index >= (widget.tenders.length * _prefetchFraction).floor()) {
_maybeLoadMore();
}
}, },
itemBuilder: (context, index) { itemBuilder: (context, index) {
final tender = widget.tenders[index]; final tender = widget.tenders[index];
@@ -230,6 +282,7 @@ class _MainTendersSliderState extends State<MainTendersSlider> {
children: [ children: [
Expanded( Expanded(
child: ListView.separated( child: ListView.separated(
controller: _scrollController,
padding: EdgeInsets.all(16.0.h()), padding: EdgeInsets.all(16.0.h()),
itemCount: itemCount:
widget.tenders.length + widget.tenders.length +
@@ -8,7 +8,6 @@ import '../../../core/constants/assets.dart';
import '../../../core/theme/colors.dart'; import '../../../core/theme/colors.dart';
import '../../../core/utils/date_utils.dart'; import '../../../core/utils/date_utils.dart';
import '../../../core/utils/size_config.dart'; import '../../../core/utils/size_config.dart';
import '../../detail/strings/tender_details_strings.dart';
import '../../shared/flag.dart'; import '../../shared/flag.dart';
import '../strings/tenders_strings.dart'; import '../strings/tenders_strings.dart';
import 'tender_action_buttons_row.dart'; import 'tender_action_buttons_row.dart';
@@ -83,14 +82,6 @@ class TenderCard extends StatelessWidget {
), ),
child: Column( child: Column(
children: [ children: [
// Match percentage
_matchPercentage(),
SizedBox(height: isDesktop ? 4.0.h() : 8.0.h()),
// Progress bar
_progressBar(),
SizedBox(height: isDesktop ? 8.0.h() : 16.0.h()),
// Location and apply button // Location and apply button
Row( Row(
children: [ children: [
@@ -243,51 +234,6 @@ class TenderCard extends StatelessWidget {
); );
} }
Widget _matchPercentage() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
TenderDetailsStrings.tenderMatchProfile,
style: TextStyle(
color: AppColors.grey80,
fontSize: isDesktop ? 12.0.sp() : 14.0.sp(),
fontWeight: isDesktop ? FontWeight.w400 : FontWeight.w500,
),
),
Text(
'${45}%',
style: TextStyle(
color: AppColors.secondaryTextColor,
fontSize: isDesktop ? 12.0.sp() : 16.0.sp(),
fontWeight: FontWeight.w600,
),
),
],
);
}
Widget _progressBar() {
return Container(
width: double.infinity,
height: 4.0.h(),
decoration: BoxDecoration(
color: AppColors.grey20,
borderRadius: BorderRadius.circular(2),
),
child: FractionallySizedBox(
alignment: Alignment.centerLeft,
widthFactor: (45) / 100,
child: Container(
decoration: BoxDecoration(
color: AppColors.secondary50,
borderRadius: BorderRadius.circular(2),
),
),
),
);
}
Widget _locationInfo() { Widget _locationInfo() {
return Expanded( return Expanded(
child: Row( child: Row(
@@ -1,250 +0,0 @@
import 'package:flutter/material.dart';
import 'package:tm_app/core/theme/colors.dart';
/// A windowed numbered pagination widget that mirrors the Next.js
/// implementation: ±10 page-button window around the current page,
/// 1-based labels, prev/next chevrons.
class WindowedPagination extends StatelessWidget {
const WindowedPagination({
super.key,
required this.currentPage,
required this.totalPages,
required this.onPageChanged,
this.maxPageJump = 10,
this.compact = false,
});
/// 0-based absolute current page index.
final int currentPage;
/// Total number of pages (1-based count).
final int totalPages;
/// Called with the 0-based target page index.
final ValueChanged<int> onPageChanged;
/// Half-width of the page-button window (web uses 10).
final int maxPageJump;
/// If true, renders smaller buttons (for mobile).
final bool compact;
@override
Widget build(BuildContext context) {
if (totalPages <= 1) {
return const SizedBox.shrink();
}
final clampedCurrent = currentPage.clamp(0, totalPages - 1);
final windowStart = (clampedCurrent - maxPageJump).clamp(0, totalPages - 1);
final windowEnd = (clampedCurrent + maxPageJump).clamp(0, totalPages - 1);
final buttons = <Widget>[];
buttons.add(
_NavButton(
icon: Icons.chevron_left,
enabled: clampedCurrent > 0,
compact: compact,
onTap: () => onPageChanged(clampedCurrent - 1),
),
);
if (windowStart > 0) {
buttons.add(_buildPageButton(0, clampedCurrent));
if (windowStart > 1) {
buttons.add(const _Ellipsis());
}
}
for (var i = windowStart; i <= windowEnd; i++) {
buttons.add(_buildPageButton(i, clampedCurrent));
}
if (windowEnd < totalPages - 1) {
if (windowEnd < totalPages - 2) {
buttons.add(const _Ellipsis());
}
buttons.add(_buildPageButton(totalPages - 1, clampedCurrent));
}
buttons.add(
_NavButton(
icon: Icons.chevron_right,
enabled: clampedCurrent < totalPages - 1,
compact: compact,
onTap: () => onPageChanged(clampedCurrent + 1),
),
);
final gap = compact ? 4.0 : 6.0;
return Padding(
padding: EdgeInsets.symmetric(vertical: compact ? 8 : 12),
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: constraints.maxWidth),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: _withSpacing(buttons, gap),
),
),
);
},
),
);
}
Widget _buildPageButton(int pageIndex, int currentPageIndex) {
final isActive = pageIndex == currentPageIndex;
return _PageButton(
label: '${pageIndex + 1}',
isActive: isActive,
compact: compact,
onTap: isActive ? null : () => onPageChanged(pageIndex),
);
}
static List<Widget> _withSpacing(List<Widget> items, double gap) {
final result = <Widget>[];
for (var i = 0; i < items.length; i++) {
result.add(items[i]);
if (i != items.length - 1) {
result.add(SizedBox(width: gap));
}
}
return result;
}
}
class _PageButton extends StatelessWidget {
const _PageButton({
required this.label,
required this.isActive,
required this.compact,
this.onTap,
});
final String label;
final bool isActive;
final bool compact;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
final size = compact ? 32.0 : 38.0;
return SizedBox(
width: size,
height: size,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: onTap,
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
gradient: isActive
? const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.mainBlue, AppColors.primaryColor],
)
: null,
color: isActive ? null : AppColors.grey0,
border: Border.all(
color: isActive ? Colors.transparent : AppColors.grey30,
width: 1,
),
boxShadow: isActive
? [
BoxShadow(
color: AppColors.mainBlue.withValues(alpha: 0.25),
blurRadius: 8,
offset: const Offset(0, 2),
),
]
: null,
),
child: Text(
label,
style: TextStyle(
fontSize: compact ? 12 : 13,
fontWeight: isActive ? FontWeight.w600 : FontWeight.w500,
color: isActive ? Colors.white : AppColors.grey70,
),
),
),
),
),
);
}
}
class _NavButton extends StatelessWidget {
const _NavButton({
required this.icon,
required this.enabled,
required this.compact,
required this.onTap,
});
final IconData icon;
final bool enabled;
final bool compact;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final size = compact ? 32.0 : 38.0;
return SizedBox(
width: size,
height: size,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: enabled ? onTap : null,
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: AppColors.grey0,
border: Border.all(
color: enabled ? AppColors.grey30 : AppColors.grey20,
width: 1,
),
),
child: Icon(
icon,
size: compact ? 18 : 20,
color: enabled ? AppColors.grey70 : AppColors.grey40,
),
),
),
),
);
}
}
class _Ellipsis extends StatelessWidget {
const _Ellipsis();
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 2),
child: Text(
'',
style: TextStyle(
fontSize: 14,
color: AppColors.grey60,
fontWeight: FontWeight.w500,
),
),
);
}
}
+18
View File
@@ -58,6 +58,11 @@ void main() {
id: 'TND123', id: 'TND123',
tenderId: 'TND123', tenderId: 'TND123',
noticePublicationId: 'NP123', noticePublicationId: 'NP123',
noticeTypeCode: 'cn-standard',
formType: 'competition',
noticeLanguageCode: 'ENG',
issueDate: 1700000000,
issueTime: 1700000000,
publicationDate: 1700000000, publicationDate: 1700000000,
title: 'Tender Title', title: 'Tender Title',
description: 'Some description', description: 'Some description',
@@ -74,7 +79,11 @@ void main() {
durationUnit: 'month', durationUnit: 'month',
buyerOrganization: Organization( buyerOrganization: Organization(
name: 'Test Organization', name: 'Test Organization',
companyId: 'ORG001',
address: null,
), ),
lots: null,
officialLanguages: null,
status: 'active', status: 'active',
); );
@@ -114,6 +123,11 @@ void main() {
id: 'TND001', id: 'TND001',
tenderId: 'TND001', tenderId: 'TND001',
noticePublicationId: 'NP1', noticePublicationId: 'NP1',
noticeTypeCode: 'cn-standard',
formType: 'competition',
noticeLanguageCode: 'ENG',
issueDate: 1700000000,
issueTime: 1700000000,
publicationDate: 1700000000, publicationDate: 1700000000,
title: 'Old Tender', title: 'Old Tender',
description: 'Old desc', description: 'Old desc',
@@ -130,7 +144,11 @@ void main() {
durationUnit: 'month', durationUnit: 'month',
buyerOrganization: Organization( buyerOrganization: Organization(
name: 'Old Org', name: 'Old Org',
companyId: 'ORG001',
address: null,
), ),
lots: null,
officialLanguages: null,
status: 'inactive', status: 'inactive',
), ),
updatedAt: 1750000000, updatedAt: 1750000000,
+158
View File
@@ -0,0 +1,158 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/data/services/model/lot/lot.dart';
import 'package:tm_app/data/services/model/organization/organization.dart';
import 'package:tm_app/data/services/model/organization/organization_address.dart';
import 'package:tm_app/data/services/model/tender_data/tender_data.dart';
void main() {
group('Lot', () {
test('fromJson maps lot_id/title/description', () {
final lot = Lot.fromJson(const {
'lot_id': 'LOT-1',
'title': 'Lot title',
'description': 'Lot description',
});
expect(lot.lotId, 'LOT-1');
expect(lot.title, 'Lot title');
expect(lot.description, 'Lot description');
});
test('toJson round-trips back to the same values', () {
const lot = Lot(lotId: 'LOT-2', title: 'T', description: 'D');
expect(Lot.fromJson(lot.toJson()), lot);
});
test('missing fields parse as null (resilient to partial payloads)', () {
final lot = Lot.fromJson(const {});
expect(lot.lotId, isNull);
expect(lot.title, isNull);
expect(lot.description, isNull);
});
});
group('OrganizationAddress', () {
test('fromJson maps city_name/country_code', () {
final address = OrganizationAddress.fromJson(const {
'city_name': 'Amsterdam',
'country_code': 'NL',
});
expect(address.cityName, 'Amsterdam');
expect(address.countryCode, 'NL');
});
test('toJson round-trips back to the same values', () {
const address = OrganizationAddress(
cityName: 'Berlin',
countryCode: 'DE',
);
expect(OrganizationAddress.fromJson(address.toJson()), address);
});
test('nested under Organization.fromJson', () {
final org = Organization.fromJson(const {
'name': 'Buyer Org',
'company_id': 'COMP-1',
'address': {'city_name': 'Paris', 'country_code': 'FR'},
});
expect(org.name, 'Buyer Org');
expect(org.companyId, 'COMP-1');
expect(org.address?.cityName, 'Paris');
expect(org.address?.countryCode, 'FR');
});
});
group('TenderData new fields', () {
Map<String, Object?> baseJson({
Object? issueDate,
Object? issueTime,
}) => {
'success': true,
'message': 'ok',
'id': 'TND-1',
'tender_id': 'TND-1',
'notice_publication_id': 'NP-1',
'notice_type_code': 'NT-1',
'form_type': 'open',
'notice_language_code': 'en',
'issue_date': issueDate,
'issue_time': issueTime,
'publication_date': 1700000000,
'title': 'Title',
'description': 'Desc',
'procurement_type_code': 'PT-1',
'procedure_code': 'PRC-1',
'estimated_value': 1000,
'currency': 'EUR',
'duration': '12',
'tender_deadline': 1710000000,
'submission_deadline': 1710000000,
'application_deadline': 1710000000,
'submission_url': 'https://example.com',
'country_code': 'NL',
'duration_unit': 'month',
'buyer_organization': {
'name': 'Buyer Org',
'company_id': 'COMP-1',
'address': {'city_name': 'Amsterdam', 'country_code': 'NL'},
},
'lots': [
{'lot_id': 'LOT-1', 'title': 'Lot title', 'description': 'Lot desc'},
],
'official_languages': ['en', 'nl'],
'status': 'active',
};
test('parses notice/form/language, lots and official_languages', () {
final tender = TenderData.fromJson(baseJson(
issueDate: 1700000000,
issueTime: 1700003600,
));
expect(tender.noticeTypeCode, 'NT-1');
expect(tender.formType, 'open');
expect(tender.noticeLanguageCode, 'en');
expect(tender.officialLanguages, ['en', 'nl']);
expect(tender.lots, hasLength(1));
expect(tender.lots?.first.lotId, 'LOT-1');
expect(tender.buyerOrganization?.address?.cityName, 'Amsterdam');
});
test('issue_date/issue_time accept epoch seconds', () {
final tender = TenderData.fromJson(baseJson(
issueDate: 1700000000,
issueTime: 1700003600,
));
expect(tender.issueDate, 1700000000);
expect(tender.issueTime, 1700003600);
});
test('issue_date/issue_time accept millisecond and string inputs', () {
final tender = TenderData.fromJson(baseJson(
issueDate: '1700000000',
issueTime: 1700000000000,
));
// unixTimestampFromJson normalizes the raw value; the >1e10 ms value is
// kept as-is here (timeConvertor downscales it at display time).
expect(tender.issueDate, 1700000000);
expect(tender.issueTime, 1700000000000);
});
test('non-positive or missing issue_date/issue_time become null', () {
final tender = TenderData.fromJson(baseJson(
issueDate: 0,
issueTime: null,
));
expect(tender.issueDate, isNull);
expect(tender.issueTime, isNull);
});
});
}