Initial commit
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/notification_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/order_single_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/orders_list_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_response.dart';
|
||||
|
||||
import '../../models/banner_response.dart';
|
||||
|
||||
abstract class HomeRemoteDataSource {
|
||||
Future<OrdersListResponse> ordersList(Map<String, dynamic> request);
|
||||
|
||||
Future<OrderSingleResponse> orderSingle(String orderId);
|
||||
|
||||
Future<CommentResponse> comment(CommentRequest request);
|
||||
|
||||
Future<NotificationResponse> notification(Map<String, dynamic> request);
|
||||
|
||||
Future<ReadNotificationResponse> notificationRead(
|
||||
ReadNotificationRequest request,
|
||||
);
|
||||
|
||||
Future<List<BannerResponse>> bannersResponse();
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
import 'package:cargocalculaterapp/features/home/data/models/banner_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/notification_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/order_single_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/orders_list_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_response.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import '../../../../../constants/constants.dart';
|
||||
import '../../../../../core/error/exceptions.dart';
|
||||
import '../../../../../core/local_source/local_source.dart';
|
||||
import '../../../../../injector_container.dart';
|
||||
import 'home_remote_data_source.dart';
|
||||
|
||||
class HomeRemoteDataSourceImpl extends HomeRemoteDataSource {
|
||||
final Dio dio;
|
||||
|
||||
HomeRemoteDataSourceImpl(this.dio);
|
||||
|
||||
@override
|
||||
Future<OrdersListResponse> ordersList(Map<String, dynamic> request) async {
|
||||
try {
|
||||
final Response response = await dio.get(
|
||||
Constants.baseUrl + Urls.orderList,
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
queryParameters: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return OrdersListResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<OrderSingleResponse> orderSingle(String orderId) async {
|
||||
try {
|
||||
final Response response = await dio.get(
|
||||
"${Constants.baseUrl}${Urls.orderList}/$orderId",
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return OrderSingleResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CommentResponse> comment(CommentRequest request) async {
|
||||
try {
|
||||
final Response response = await dio.post(
|
||||
"${Constants.baseUrl}${Urls.comment}",
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
data: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return CommentResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<NotificationResponse> notification(
|
||||
Map<String, dynamic> request,
|
||||
) async {
|
||||
try {
|
||||
final Response response = await dio.get(
|
||||
"${Constants.baseUrl}${Urls.notification}",
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
queryParameters: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return NotificationResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ReadNotificationResponse> notificationRead(
|
||||
ReadNotificationRequest request,
|
||||
) async {
|
||||
try {
|
||||
final Response response = await dio.put(
|
||||
"${Constants.baseUrl}${Urls.notificationRead}",
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
data: request,
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
return ReadNotificationResponse.fromJson(response.data);
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<BannerResponse>> bannersResponse() async {
|
||||
try {
|
||||
final Response response = await dio.get(
|
||||
"${Constants.baseUrl}${Urls.banners}",
|
||||
options: DioConstants.options
|
||||
..headers = {
|
||||
"Authorization": "Bearer ${sl<LocalSource>().getAccessToken()}",
|
||||
},
|
||||
);
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
final List<BannerResponse> images = (response.data as List)
|
||||
.map((json) => BannerResponse.fromJson(json))
|
||||
.toList();
|
||||
return images;
|
||||
}
|
||||
throw ServerException.fromJson(response.data);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException.fromJson(e.response?.data);
|
||||
} on FormatException {
|
||||
throw ServerException(message: Validations.someThingWentWrong);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
lib/features/home/data/models/banner_response.dart
Normal file
27
lib/features/home/data/models/banner_response.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
class BannerResponse {
|
||||
BannerResponse({this.id, this.image, this.createdAt, this.updatedAt, this.v});
|
||||
|
||||
BannerResponse.fromJson(dynamic json) {
|
||||
id = json['_id'];
|
||||
image = json['image'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
v = json['__v'];
|
||||
}
|
||||
|
||||
String? id;
|
||||
String? image;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? v;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['_id'] = id;
|
||||
map['image'] = image;
|
||||
map['createdAt'] = createdAt;
|
||||
map['updatedAt'] = updatedAt;
|
||||
map['__v'] = v;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
24
lib/features/home/data/models/comment_request.dart
Normal file
24
lib/features/home/data/models/comment_request.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
class CommentRequest {
|
||||
CommentRequest({
|
||||
this.message,
|
||||
this.rate,
|
||||
this.orderNumber,});
|
||||
|
||||
CommentRequest.fromJson(dynamic json) {
|
||||
message = json['message'];
|
||||
rate = json['rate'];
|
||||
orderNumber = json['order_number'];
|
||||
}
|
||||
String? message;
|
||||
int? rate;
|
||||
String? orderNumber;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['message'] = message;
|
||||
map['rate'] = rate;
|
||||
map['order_number'] = orderNumber;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
44
lib/features/home/data/models/comment_response.dart
Normal file
44
lib/features/home/data/models/comment_response.dart
Normal file
@@ -0,0 +1,44 @@
|
||||
class CommentResponse {
|
||||
CommentResponse({
|
||||
this.message,
|
||||
this.rate,
|
||||
this.userUcode,
|
||||
this.orderNumber,
|
||||
this.id,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.v,});
|
||||
|
||||
CommentResponse.fromJson(dynamic json) {
|
||||
message = json['message'];
|
||||
rate = json['rate'];
|
||||
userUcode = json['user_ucode'];
|
||||
orderNumber = json['order_number'];
|
||||
id = json['_id'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
v = json['__v'];
|
||||
}
|
||||
String? message;
|
||||
int? rate;
|
||||
String? userUcode;
|
||||
String? orderNumber;
|
||||
String? id;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? v;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['message'] = message;
|
||||
map['rate'] = rate;
|
||||
map['user_ucode'] = userUcode;
|
||||
map['order_number'] = orderNumber;
|
||||
map['_id'] = id;
|
||||
map['createdAt'] = createdAt;
|
||||
map['updatedAt'] = updatedAt;
|
||||
map['__v'] = v;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
79
lib/features/home/data/models/notification_response.dart
Normal file
79
lib/features/home/data/models/notification_response.dart
Normal file
@@ -0,0 +1,79 @@
|
||||
import 'package:cargocalculaterapp/core/models/name.dart';
|
||||
|
||||
class NotificationResponse {
|
||||
NotificationResponse({this.notifications, this.totalCount});
|
||||
|
||||
NotificationResponse.fromJson(dynamic json) {
|
||||
if (json['notifications'] != null) {
|
||||
notifications = [];
|
||||
json['notifications'].forEach((v) {
|
||||
notifications?.add(Notifications.fromJson(v));
|
||||
});
|
||||
}
|
||||
totalCount = json['totalCount'];
|
||||
}
|
||||
|
||||
List<Notifications>? notifications;
|
||||
int? totalCount;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
if (notifications != null) {
|
||||
map['notifications'] = notifications?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
map['totalCount'] = totalCount;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class Notifications {
|
||||
Notifications({
|
||||
this.id,
|
||||
this.userUcode,
|
||||
this.orderNumber,
|
||||
this.text,
|
||||
this.status,
|
||||
this.isRead,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.v,
|
||||
});
|
||||
|
||||
Notifications.fromJson(dynamic json) {
|
||||
id = json['_id'];
|
||||
userUcode = json['user_ucode'];
|
||||
orderNumber = json['order_number'];
|
||||
text = json['text'] != null ? Name.fromJson(json['text']) : null;
|
||||
status = json['status'];
|
||||
isRead = json['is_read'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
v = json['__v'];
|
||||
}
|
||||
|
||||
String? id;
|
||||
String? userUcode;
|
||||
String? orderNumber;
|
||||
Name? text;
|
||||
String? status;
|
||||
bool? isRead;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? v;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['_id'] = id;
|
||||
map['user_ucode'] = userUcode;
|
||||
map['order_number'] = orderNumber;
|
||||
if (text != null) {
|
||||
map['text'] = text?.toJson();
|
||||
}
|
||||
map['status'] = status;
|
||||
map['is_read'] = isRead;
|
||||
map['createdAt'] = createdAt;
|
||||
map['updatedAt'] = updatedAt;
|
||||
map['__v'] = v;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
145
lib/features/home/data/models/order_single_response.dart
Normal file
145
lib/features/home/data/models/order_single_response.dart
Normal file
@@ -0,0 +1,145 @@
|
||||
import 'package:cargocalculaterapp/core/models/name.dart';
|
||||
|
||||
class OrderSingleResponse {
|
||||
OrderSingleResponse({
|
||||
this.id,
|
||||
this.userUcode,
|
||||
this.phoneNumber,
|
||||
this.gmail,
|
||||
this.quantity,
|
||||
this.nameUz,
|
||||
this.nameRu,
|
||||
this.nameZh,
|
||||
this.weight,
|
||||
this.m3,
|
||||
this.averageWeightKg,
|
||||
this.wharehouseUz,
|
||||
this.wharehouseRu,
|
||||
this.wharehouseZh,
|
||||
this.deliveryPrice,
|
||||
this.shipmentId,
|
||||
this.leadId,
|
||||
this.status,
|
||||
this.partiallyArrived,
|
||||
this.fullArrived,
|
||||
this.orderNumber,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.v,
|
||||
this.arrivalDate,
|
||||
this.images,
|
||||
});
|
||||
|
||||
OrderSingleResponse.fromJson(dynamic json) {
|
||||
userUcode = json['user_ucode'];
|
||||
phoneNumber = json['phone_number'];
|
||||
gmail = json['gmail'];
|
||||
quantity = json['quantity'];
|
||||
nameUz = json['nameUz'];
|
||||
nameRu = json['nameRu'];
|
||||
nameZh = json['nameZh'];
|
||||
weight = json['weight'];
|
||||
m3 = json['m3'];
|
||||
averageWeightKg = json['average_weight_kg'];
|
||||
wharehouseUz = json['wharehouseUz'];
|
||||
wharehouseRu = json['wharehouseRu'];
|
||||
wharehouseZh = json['wharehouseZh'];
|
||||
deliveryPrice = json['delivery_price'];
|
||||
shipmentId = json['shipment_id'];
|
||||
leadId = json['lead_id'];
|
||||
status = json['status'] != null ? Status.fromJson(json['status']) : null;
|
||||
partiallyArrived = json['partially_arrived'];
|
||||
fullArrived = json['full_arrived'];
|
||||
orderNumber = json['order_number'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
v = json['__v'];
|
||||
id = json['id'];
|
||||
arrivalDate = json['arrival_date'];
|
||||
images = json['images'] != null ? json['images'].cast<String>() : [];
|
||||
}
|
||||
|
||||
String? userUcode;
|
||||
String? phoneNumber;
|
||||
String? gmail;
|
||||
int? quantity;
|
||||
String? nameUz;
|
||||
String? nameRu;
|
||||
String? nameZh;
|
||||
String? weight;
|
||||
String? m3;
|
||||
String? averageWeightKg;
|
||||
String? wharehouseUz;
|
||||
String? wharehouseRu;
|
||||
String? wharehouseZh;
|
||||
String? deliveryPrice;
|
||||
String? shipmentId;
|
||||
String? leadId;
|
||||
Status? status;
|
||||
bool? partiallyArrived;
|
||||
bool? fullArrived;
|
||||
String? orderNumber;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? v;
|
||||
String? id;
|
||||
String? arrivalDate;
|
||||
List<String>? images;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['user_ucode'] = userUcode;
|
||||
map['phone_number'] = phoneNumber;
|
||||
map['gmail'] = gmail;
|
||||
map['quantity'] = quantity;
|
||||
map['nameUz'] = nameUz;
|
||||
map['nameRu'] = nameRu;
|
||||
map['nameZh'] = nameZh;
|
||||
map['weight'] = weight;
|
||||
map['m3'] = m3;
|
||||
map['average_weight_kg'] = averageWeightKg;
|
||||
map['wharehouseUz'] = wharehouseUz;
|
||||
map['wharehouseRu'] = wharehouseRu;
|
||||
map['wharehouseZh'] = wharehouseZh;
|
||||
map['delivery_price'] = deliveryPrice;
|
||||
map['shipment_id'] = shipmentId;
|
||||
map['lead_id'] = leadId;
|
||||
if (status != null) {
|
||||
map['status'] = status?.toJson();
|
||||
}
|
||||
map['partially_arrived'] = partiallyArrived;
|
||||
map['full_arrived'] = fullArrived;
|
||||
map['order_number'] = orderNumber;
|
||||
map['createdAt'] = createdAt;
|
||||
map['updatedAt'] = updatedAt;
|
||||
map['__v'] = v;
|
||||
map['id'] = id;
|
||||
map['arrival_date'] = arrivalDate;
|
||||
map['images'] = images;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class Status {
|
||||
Status({this.code, this.translations});
|
||||
|
||||
Status.fromJson(dynamic json) {
|
||||
code = json['code'];
|
||||
translations =
|
||||
json['translations'] != null
|
||||
? Name.fromJson(json['translations'])
|
||||
: null;
|
||||
}
|
||||
|
||||
String? code;
|
||||
Name? translations;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['code'] = code;
|
||||
if (translations != null) {
|
||||
map['translations'] = translations?.toJson();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
160
lib/features/home/data/models/orders_list_response.dart
Normal file
160
lib/features/home/data/models/orders_list_response.dart
Normal file
@@ -0,0 +1,160 @@
|
||||
import '../../../../core/models/name.dart';
|
||||
|
||||
class OrdersListResponse {
|
||||
OrdersListResponse({this.totalCount, this.data});
|
||||
|
||||
OrdersListResponse.fromJson(dynamic json) {
|
||||
totalCount = json['totalCount'];
|
||||
if (json['data'] != null) {
|
||||
data = [];
|
||||
json['data'].forEach((v) {
|
||||
data?.add(Data.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int? totalCount;
|
||||
List<Data>? data;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['totalCount'] = totalCount;
|
||||
if (data != null) {
|
||||
map['data'] = data?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class Data {
|
||||
Data({
|
||||
this.id,
|
||||
this.userUcode,
|
||||
this.phoneNumber,
|
||||
this.gmail,
|
||||
this.quantity,
|
||||
this.nameUz,
|
||||
this.nameRu,
|
||||
this.nameZh,
|
||||
this.size,
|
||||
this.wharehouseUz,
|
||||
this.wharehouseRu,
|
||||
this.wharehouseZh,
|
||||
this.deliveryPrice,
|
||||
this.shipmentId,
|
||||
this.leadId,
|
||||
this.status,
|
||||
this.partiallyArrived,
|
||||
this.fullArrived,
|
||||
this.orderNumber,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.v,
|
||||
this.arrivalDate,
|
||||
});
|
||||
|
||||
Data.fromJson(dynamic json) {
|
||||
userUcode = json['user_ucode'];
|
||||
phoneNumber = json['phone_number'];
|
||||
gmail = json['gmail'];
|
||||
quantity = json['quantity'];
|
||||
nameUz = json['nameUz'];
|
||||
nameRu = json['nameRu'];
|
||||
nameZh = json['nameZh'];
|
||||
size = json['size'];
|
||||
wharehouseUz = json['wharehouseUz'];
|
||||
wharehouseRu = json['wharehouseRu'];
|
||||
wharehouseZh = json['wharehouseZh'];
|
||||
deliveryPrice = json['delivery_price'];
|
||||
shipmentId = json['shipment_id'];
|
||||
leadId = json['lead_id'];
|
||||
status = json['status'] != null ? Status.fromJson(json['status']) : null;
|
||||
partiallyArrived = json['partially_arrived'];
|
||||
fullArrived = json['full_arrived'];
|
||||
orderNumber = json['order_number'];
|
||||
createdAt = json['createdAt'];
|
||||
updatedAt = json['updatedAt'];
|
||||
v = json['__v'];
|
||||
id = json['id'];
|
||||
arrivalDate = json['arrival_date'];
|
||||
}
|
||||
|
||||
String? userUcode;
|
||||
String? phoneNumber;
|
||||
String? gmail;
|
||||
int? quantity;
|
||||
String? nameUz;
|
||||
String? nameRu;
|
||||
String? nameZh;
|
||||
String? size;
|
||||
String? wharehouseUz;
|
||||
String? wharehouseRu;
|
||||
String? wharehouseZh;
|
||||
String? deliveryPrice;
|
||||
String? shipmentId;
|
||||
String? leadId;
|
||||
Status? status;
|
||||
bool? partiallyArrived;
|
||||
bool? fullArrived;
|
||||
String? orderNumber;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
int? v;
|
||||
String? id;
|
||||
String? arrivalDate;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['_id'] = id;
|
||||
map['user_ucode'] = userUcode;
|
||||
map['phone_number'] = phoneNumber;
|
||||
map['gmail'] = gmail;
|
||||
map['quantity'] = quantity;
|
||||
map['nameUz'] = nameUz;
|
||||
map['nameRu'] = nameRu;
|
||||
map['nameZh'] = nameZh;
|
||||
map['size'] = size;
|
||||
map['wharehouseUz'] = wharehouseUz;
|
||||
map['wharehouseRu'] = wharehouseRu;
|
||||
map['wharehouseZh'] = wharehouseZh;
|
||||
map['delivery_price'] = deliveryPrice;
|
||||
map['shipment_id'] = shipmentId;
|
||||
map['lead_id'] = leadId;
|
||||
if (status != null) {
|
||||
map['status'] = status?.toJson();
|
||||
}
|
||||
map['partially_arrived'] = partiallyArrived;
|
||||
map['full_arrived'] = fullArrived;
|
||||
map['order_number'] = orderNumber;
|
||||
map['createdAt'] = createdAt;
|
||||
map['updatedAt'] = updatedAt;
|
||||
map['__v'] = v;
|
||||
map['id'] = id;
|
||||
map['arrival_date'] = arrivalDate;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class Status {
|
||||
Status({this.code, this.translations});
|
||||
|
||||
Status.fromJson(dynamic json) {
|
||||
code = json['code'];
|
||||
translations =
|
||||
json['translations'] != null
|
||||
? Name.fromJson(json['translations'])
|
||||
: null;
|
||||
}
|
||||
|
||||
String? code;
|
||||
Name? translations;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['code'] = code;
|
||||
if (translations != null) {
|
||||
map['translations'] = translations?.toJson();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
16
lib/features/home/data/models/read_notification_request.dart
Normal file
16
lib/features/home/data/models/read_notification_request.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
class ReadNotificationRequest {
|
||||
ReadNotificationRequest({
|
||||
this.notificationIds,});
|
||||
|
||||
ReadNotificationRequest.fromJson(dynamic json) {
|
||||
notificationIds = json['notification_ids'] != null ? json['notification_ids'].cast<String>() : [];
|
||||
}
|
||||
List<String>? notificationIds;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['notification_ids'] = notificationIds;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
class ReadNotificationResponse {
|
||||
ReadNotificationResponse({
|
||||
this.message,});
|
||||
|
||||
ReadNotificationResponse.fromJson(dynamic json) {
|
||||
message = json['message'];
|
||||
}
|
||||
String? message;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['message'] = message;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
88
lib/features/home/data/repository/home_repository_impl.dart
Normal file
88
lib/features/home/data/repository/home_repository_impl.dart
Normal file
@@ -0,0 +1,88 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/banner_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/notification_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/order_single_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/orders_list_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../domain/repository/home_repository.dart';
|
||||
import '../data_source/remote/home_remote_data_source.dart';
|
||||
|
||||
class HomeRepositoryImpl extends HomeRepository {
|
||||
final HomeRemoteDataSource remoteDataSource;
|
||||
|
||||
HomeRepositoryImpl(this.remoteDataSource);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, OrdersListResponse>> ordersList(
|
||||
Map<String, dynamic> request,
|
||||
) async {
|
||||
try {
|
||||
final response = await remoteDataSource.ordersList(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, OrderSingleResponse>> orderSingle(
|
||||
String orderId,
|
||||
) async {
|
||||
try {
|
||||
final response = await remoteDataSource.orderSingle(orderId);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, CommentResponse>> comment(
|
||||
CommentRequest request,
|
||||
) async {
|
||||
try {
|
||||
final response = await remoteDataSource.comment(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, NotificationResponse>> notification(
|
||||
Map<String, dynamic> request,
|
||||
) async {
|
||||
try {
|
||||
final response = await remoteDataSource.notification(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, ReadNotificationResponse>> notificationRead(
|
||||
ReadNotificationRequest request,
|
||||
) async {
|
||||
try {
|
||||
final response = await remoteDataSource.notificationRead(request);
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<BannerResponse>>> bannersGet() async {
|
||||
try {
|
||||
final response = await remoteDataSource.bannersResponse();
|
||||
return Right(response);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
30
lib/features/home/domain/repository/home_repository.dart
Normal file
30
lib/features/home/domain/repository/home_repository.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/comment_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/notification_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/order_single_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/orders_list_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../../../core/error/failure.dart';
|
||||
import '../../data/models/banner_response.dart';
|
||||
|
||||
abstract class HomeRepository {
|
||||
Future<Either<Failure, OrdersListResponse>> ordersList(
|
||||
Map<String, dynamic> request,
|
||||
);
|
||||
|
||||
Future<Either<Failure, OrderSingleResponse>> orderSingle(String orderId);
|
||||
|
||||
Future<Either<Failure, CommentResponse>> comment(CommentRequest request);
|
||||
|
||||
Future<Either<Failure, NotificationResponse>> notification(
|
||||
Map<String, dynamic> request,
|
||||
);
|
||||
|
||||
Future<Either<Failure, ReadNotificationResponse>> notificationRead(
|
||||
ReadNotificationRequest request,
|
||||
);
|
||||
|
||||
Future<Either<Failure, List<BannerResponse>>> bannersGet();
|
||||
}
|
||||
16
lib/features/home/domain/usecase/banners_usecase.dart
Normal file
16
lib/features/home/domain/usecase/banners_usecase.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../data/models/banner_response.dart';
|
||||
import '../repository/home_repository.dart';
|
||||
|
||||
class BannersUseCase extends UseCase<List<BannerResponse>, NoParams> {
|
||||
final HomeRepository repository;
|
||||
|
||||
BannersUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<BannerResponse>>> call(NoParams params) async {
|
||||
return await repository.bannersGet();
|
||||
}
|
||||
}
|
||||
16
lib/features/home/domain/usecase/comment_usecase.dart
Normal file
16
lib/features/home/domain/usecase/comment_usecase.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../data/models/comment_request.dart';
|
||||
import '../../data/models/comment_response.dart';
|
||||
import '../repository/home_repository.dart';
|
||||
|
||||
class CommentUseCase extends UseCase<CommentResponse, CommentRequest>{
|
||||
final HomeRepository repository;
|
||||
|
||||
CommentUseCase(this.repository);
|
||||
@override
|
||||
Future<Either<Failure, CommentResponse>> call(CommentRequest params) async {
|
||||
return await repository.comment(params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_request.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../repository/home_repository.dart';
|
||||
|
||||
class NotificationReadUseCase extends UseCase<ReadNotificationResponse, ReadNotificationRequest>{
|
||||
final HomeRepository repository;
|
||||
|
||||
NotificationReadUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, ReadNotificationResponse>> call(ReadNotificationRequest params) async {
|
||||
return await repository.notificationRead(params);
|
||||
}
|
||||
|
||||
}
|
||||
20
lib/features/home/domain/usecase/notification_usecase.dart
Normal file
20
lib/features/home/domain/usecase/notification_usecase.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/notification_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../../../../core/usecase/usecase.dart';
|
||||
import '../repository/home_repository.dart';
|
||||
|
||||
class NotificationUseCase
|
||||
extends UseCase<NotificationResponse, Map<String, dynamic>> {
|
||||
final HomeRepository repository;
|
||||
|
||||
NotificationUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, NotificationResponse>> call(
|
||||
Map<String, dynamic> params,
|
||||
) async {
|
||||
return await repository.notification(params);
|
||||
}
|
||||
}
|
||||
17
lib/features/home/domain/usecase/order_single_usecase.dart
Normal file
17
lib/features/home/domain/usecase/order_single_usecase.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/order_single_response.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../repository/home_repository.dart';
|
||||
|
||||
class OrderSingleUseCase extends UseCase<OrderSingleResponse, String> {
|
||||
final HomeRepository repository;
|
||||
|
||||
OrderSingleUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, OrderSingleResponse>> call(String params) async {
|
||||
return await repository.orderSingle(params);
|
||||
}
|
||||
}
|
||||
19
lib/features/home/domain/usecase/orders_list_usecase.dart
Normal file
19
lib/features/home/domain/usecase/orders_list_usecase.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:cargocalculaterapp/core/error/failure.dart';
|
||||
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/orders_list_response.dart';
|
||||
import 'package:cargocalculaterapp/features/home/domain/repository/home_repository.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
class OrdersListUseCase
|
||||
extends UseCase<OrdersListResponse, Map<String, dynamic>> {
|
||||
final HomeRepository repository;
|
||||
|
||||
OrdersListUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, OrdersListResponse>> call(
|
||||
Map<String, dynamic> params,
|
||||
) async {
|
||||
return await repository.ordersList(params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../../router/app_routes.dart';
|
||||
import '../../../data/models/comment_request.dart';
|
||||
import '../../../domain/usecase/comment_usecase.dart';
|
||||
|
||||
part 'comment_event.dart';
|
||||
|
||||
part 'comment_state.dart';
|
||||
|
||||
class CommentBloc extends Bloc<CommentEvent, CommentState> {
|
||||
CommentBloc(this.commentUseCase)
|
||||
: super(const CommentState(rating: 0, isLoading: false)) {
|
||||
on<RatingChangedEvent>(_ratingChange);
|
||||
on<CommentEnterEvent>(_commentEnter);
|
||||
on<SubmitEvent>(_commentSubmit);
|
||||
}
|
||||
|
||||
final CommentUseCase commentUseCase;
|
||||
|
||||
void _ratingChange(RatingChangedEvent event, Emitter<CommentState> emit) {
|
||||
emit(state.copyWith(rating: event.rating));
|
||||
}
|
||||
|
||||
void _commentEnter(CommentEnterEvent event, Emitter<CommentState> emit) {
|
||||
emit(state.copyWith(comment: event.comment));
|
||||
}
|
||||
|
||||
Future<void> _commentSubmit(
|
||||
SubmitEvent event,
|
||||
Emitter<CommentState> emit,
|
||||
) async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final response = await commentUseCase(
|
||||
CommentRequest(
|
||||
orderNumber: event.orderId,
|
||||
rate: state.rating.toInt(),
|
||||
message: state.comment,
|
||||
),
|
||||
);
|
||||
response.fold(
|
||||
(l) {
|
||||
emit(state.copyWith(isLoading: false));
|
||||
},
|
||||
(r) {
|
||||
emit(state.copyWith(isLoading: false));
|
||||
Navigator.pop(rootNavigatorKey.currentContext!);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
part of 'comment_bloc.dart';
|
||||
|
||||
sealed class CommentEvent extends Equatable {
|
||||
const CommentEvent();
|
||||
}
|
||||
|
||||
final class RatingChangedEvent extends CommentEvent {
|
||||
final double rating;
|
||||
|
||||
const RatingChangedEvent({required this.rating});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [rating];
|
||||
}
|
||||
|
||||
final class CommentEnterEvent extends CommentEvent {
|
||||
final String comment;
|
||||
|
||||
const CommentEnterEvent({required this.comment});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [comment];
|
||||
}
|
||||
|
||||
final class SubmitEvent extends CommentEvent {
|
||||
const SubmitEvent({required this.orderId});
|
||||
|
||||
final String orderId;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [orderId];
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
part of 'comment_bloc.dart';
|
||||
|
||||
class CommentState extends Equatable {
|
||||
const CommentState({
|
||||
required this.rating,
|
||||
required this.isLoading,
|
||||
this.comment,
|
||||
});
|
||||
|
||||
final double rating;
|
||||
final bool isLoading;
|
||||
final String? comment;
|
||||
|
||||
CommentState copyWith({double? rating, bool? isLoading, String? comment}) {
|
||||
return CommentState(
|
||||
rating: rating ?? this.rating,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
comment: comment ?? this.comment,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [rating, isLoading, comment];
|
||||
}
|
||||
127
lib/features/home/presentation/bloc/home_bloc.dart
Normal file
127
lib/features/home/presentation/bloc/home_bloc.dart
Normal file
@@ -0,0 +1,127 @@
|
||||
import 'package:cargocalculaterapp/constants/constants.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/orders_list_response.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../../core/usecase/usecase.dart';
|
||||
import '../../data/models/banner_response.dart';
|
||||
import '../../domain/usecase/banners_usecase.dart';
|
||||
import '../../domain/usecase/orders_list_usecase.dart';
|
||||
|
||||
part 'home_event.dart';
|
||||
|
||||
part 'home_state.dart';
|
||||
|
||||
class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
HomeBloc(this.ordersListUseCase, this.bannersUseCase)
|
||||
: super(
|
||||
const HomeState(
|
||||
paginationLoading: false,
|
||||
isLoading: false,
|
||||
page: 1,
|
||||
selectedStatus: AppConst.all,
|
||||
),
|
||||
) {
|
||||
on<GetAllOrdersListEvent>(_getAllOrders);
|
||||
on<GetOrdersEvent>(_getOrders);
|
||||
on<OrderStatusEvent>(_changeOrderStatus);
|
||||
on<PaginationEvent>(_paginationLoading);
|
||||
on<GetBannersEvent>(_getBanner);
|
||||
on<RefreshEvent>(_refresh);
|
||||
}
|
||||
|
||||
final OrdersListUseCase ordersListUseCase;
|
||||
final BannersUseCase bannersUseCase;
|
||||
|
||||
final Map<String, dynamic> productsRequest = {};
|
||||
|
||||
void _getAllOrders(GetAllOrdersListEvent event, Emitter<HomeState> emit) {
|
||||
productsRequest.remove(AppKeys.status);
|
||||
productsRequest[AppKeys.page] = 1;
|
||||
productsRequest[AppKeys.limit] = AppConst.limit;
|
||||
add(const GetOrdersEvent());
|
||||
}
|
||||
|
||||
Future<void> _getOrders(GetOrdersEvent event, Emitter<HomeState> emit) async {
|
||||
emit(state.copyWith(isLoading: event.isLoading));
|
||||
final response = await ordersListUseCase(productsRequest);
|
||||
response.fold(
|
||||
(l) {
|
||||
emit(state.copyWith(isLoading: false, paginationLoading: false));
|
||||
},
|
||||
(r) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
isLoading: false,
|
||||
paginationLoading: false,
|
||||
ordersList: r,
|
||||
page: productsRequest[AppKeys.page],
|
||||
pageCount: ((r.totalCount ?? 1) / 20).ceil(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _changeOrderStatus(OrderStatusEvent event, Emitter<HomeState> emit) {
|
||||
if (event.status == state.selectedStatus) {
|
||||
return;
|
||||
}
|
||||
emit(state.copyWith(selectedStatus: event.status));
|
||||
productsRequest[AppKeys.page] = 1;
|
||||
if (event.status == AppConst.all) {
|
||||
productsRequest.remove(AppConst.status);
|
||||
} else {
|
||||
productsRequest[AppConst.status] = event.status;
|
||||
}
|
||||
add(const GetOrdersEvent());
|
||||
}
|
||||
|
||||
Future<void> _paginationLoading(
|
||||
PaginationEvent event,
|
||||
Emitter<HomeState> emit,
|
||||
) async {
|
||||
productsRequest[AppKeys.page] = productsRequest[AppKeys.page] + 1;
|
||||
emit(
|
||||
state.copyWith(
|
||||
paginationLoading: true,
|
||||
page: productsRequest[AppKeys.page],
|
||||
),
|
||||
);
|
||||
final response = await ordersListUseCase(productsRequest);
|
||||
response.fold(
|
||||
(l) {
|
||||
emit(state.copyWith(isLoading: false, paginationLoading: false));
|
||||
},
|
||||
(r) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
isLoading: false,
|
||||
paginationLoading: false,
|
||||
ordersList: OrdersListResponse(
|
||||
totalCount: r.totalCount,
|
||||
data: state.ordersList?.data?..addAll(r.data ?? []),
|
||||
),
|
||||
page: productsRequest[AppKeys.page],
|
||||
pageCount: ((r.totalCount ?? 1) / 20).ceil(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _getBanner(
|
||||
GetBannersEvent event,
|
||||
Emitter<HomeState> emit,
|
||||
) async {
|
||||
final response = await bannersUseCase(const NoParams());
|
||||
response.fold((l) {}, (r) {
|
||||
emit(state.copyWith(banners: r));
|
||||
});
|
||||
}
|
||||
|
||||
void _refresh(RefreshEvent event, Emitter<HomeState> emit) {
|
||||
productsRequest[AppKeys.page] = 1;
|
||||
add(const GetOrdersEvent(isLoading: false));
|
||||
add(const GetBannersEvent());
|
||||
}
|
||||
}
|
||||
51
lib/features/home/presentation/bloc/home_event.dart
Normal file
51
lib/features/home/presentation/bloc/home_event.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
part of 'home_bloc.dart';
|
||||
|
||||
sealed class HomeEvent extends Equatable {
|
||||
const HomeEvent();
|
||||
}
|
||||
|
||||
final class GetAllOrdersListEvent extends HomeEvent {
|
||||
const GetAllOrdersListEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
final class GetOrdersEvent extends HomeEvent {
|
||||
const GetOrdersEvent({this.isLoading = true});
|
||||
|
||||
final bool isLoading;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [isLoading];
|
||||
}
|
||||
|
||||
final class OrderStatusEvent extends HomeEvent {
|
||||
final String status;
|
||||
|
||||
const OrderStatusEvent({required this.status});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [status];
|
||||
}
|
||||
|
||||
final class PaginationEvent extends HomeEvent {
|
||||
const PaginationEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
final class GetBannersEvent extends HomeEvent {
|
||||
const GetBannersEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
final class RefreshEvent extends HomeEvent {
|
||||
const RefreshEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
52
lib/features/home/presentation/bloc/home_state.dart
Normal file
52
lib/features/home/presentation/bloc/home_state.dart
Normal file
@@ -0,0 +1,52 @@
|
||||
part of 'home_bloc.dart';
|
||||
|
||||
class HomeState extends Equatable {
|
||||
const HomeState({
|
||||
required this.isLoading,
|
||||
required this.paginationLoading,
|
||||
required this.page,
|
||||
this.ordersList,
|
||||
this.pageCount,
|
||||
this.banners,
|
||||
required this.selectedStatus,
|
||||
});
|
||||
|
||||
final bool isLoading;
|
||||
final bool paginationLoading;
|
||||
final int page;
|
||||
final int? pageCount;
|
||||
final OrdersListResponse? ordersList;
|
||||
final String selectedStatus;
|
||||
final List<BannerResponse>? banners;
|
||||
|
||||
HomeState copyWith({
|
||||
bool? isLoading,
|
||||
bool? paginationLoading,
|
||||
int? page,
|
||||
int? pageCount,
|
||||
OrdersListResponse? ordersList,
|
||||
String? selectedStatus,
|
||||
List<BannerResponse>? banners,
|
||||
}) {
|
||||
return HomeState(
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
page: page ?? this.page,
|
||||
pageCount: pageCount ?? this.pageCount,
|
||||
paginationLoading: paginationLoading ?? this.paginationLoading,
|
||||
ordersList: ordersList ?? this.ordersList,
|
||||
selectedStatus: selectedStatus ?? this.selectedStatus,
|
||||
banners: banners ?? this.banners,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
isLoading,
|
||||
paginationLoading,
|
||||
page,
|
||||
ordersList,
|
||||
pageCount,
|
||||
selectedStatus,
|
||||
banners,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import 'package:cargocalculaterapp/constants/constants.dart';
|
||||
import 'package:cargocalculaterapp/features/home/data/models/read_notification_request.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../data/models/notification_response.dart';
|
||||
import '../../../domain/usecase/notification_read_usecase.dart';
|
||||
import '../../../domain/usecase/notification_usecase.dart';
|
||||
|
||||
part 'notification_event.dart';
|
||||
|
||||
part 'notification_state.dart';
|
||||
|
||||
class NotificationBloc extends Bloc<NotificationEvent, NotificationState> {
|
||||
NotificationBloc(this.notificationUseCase, this.notificationReadUseCase)
|
||||
: super(
|
||||
const NotificationState(
|
||||
isLoading: false,
|
||||
currentPage: 1,
|
||||
paginationLoading: false,
|
||||
),
|
||||
) {
|
||||
on<GetNotificationsEvent>(_onGetNotifications);
|
||||
on<NotificationPaginationEvent>(_notificationPagination);
|
||||
on<ReadNotificationsEvent>(_notificationRead);
|
||||
}
|
||||
|
||||
final NotificationUseCase notificationUseCase;
|
||||
final NotificationReadUseCase notificationReadUseCase;
|
||||
final Map<String, dynamic> notificationRequest = {};
|
||||
|
||||
Future<void> _onGetNotifications(
|
||||
GetNotificationsEvent event,
|
||||
Emitter<NotificationState> emit,
|
||||
) async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
notificationRequest[AppKeys.page] = 1;
|
||||
notificationRequest[AppKeys.limit] = AppConst.limit;
|
||||
|
||||
final response = await notificationUseCase(notificationRequest);
|
||||
response.fold(
|
||||
(l) {
|
||||
emit(state.copyWith(isLoading: false));
|
||||
},
|
||||
(r) {
|
||||
add(ReadNotificationsEvent(notifications: r.notifications ?? []));
|
||||
emit(
|
||||
state.copyWith(
|
||||
isLoading: false,
|
||||
currentPage: 1,
|
||||
notifications: r.notifications,
|
||||
pageCount: ((r.totalCount ?? 1) / 20).ceil(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _notificationPagination(
|
||||
NotificationPaginationEvent event,
|
||||
Emitter<NotificationState> emit,
|
||||
) async {
|
||||
notificationRequest[AppKeys.page] = event.page;
|
||||
emit(state.copyWith(paginationLoading: true, currentPage: event.page));
|
||||
final response = await notificationUseCase(notificationRequest);
|
||||
response.fold(
|
||||
(l) {
|
||||
emit(state.copyWith(paginationLoading: false));
|
||||
},
|
||||
(r) {
|
||||
add(ReadNotificationsEvent(notifications: r.notifications ?? []));
|
||||
final List<Notifications> notifications = [];
|
||||
notifications.addAll(List.of(state.notifications ?? []));
|
||||
notifications.addAll(List.of(r.notifications ?? []));
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
paginationLoading: false,
|
||||
pageCount: ((r.totalCount ?? 1) / 20).ceil(),
|
||||
notifications: notifications,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _notificationRead(
|
||||
ReadNotificationsEvent event,
|
||||
Emitter<NotificationState> emit,
|
||||
) async {
|
||||
final List<String> ids = [];
|
||||
for (var element in event.notifications) {
|
||||
if (!(element.isRead ?? false)) {
|
||||
ids.add(element.id ?? "");
|
||||
}
|
||||
}
|
||||
if (ids.isNotEmpty) {
|
||||
await notificationReadUseCase(
|
||||
ReadNotificationRequest(notificationIds: ids),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
part of 'notification_bloc.dart';
|
||||
|
||||
sealed class NotificationEvent extends Equatable {
|
||||
const NotificationEvent();
|
||||
}
|
||||
|
||||
final class GetNotificationsEvent extends NotificationEvent {
|
||||
const GetNotificationsEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
final class NotificationPaginationEvent extends NotificationEvent {
|
||||
const NotificationPaginationEvent({required this.page});
|
||||
|
||||
final int page;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [page];
|
||||
}
|
||||
|
||||
final class ReadNotificationsEvent extends NotificationEvent {
|
||||
final List<Notifications> notifications;
|
||||
|
||||
const ReadNotificationsEvent({required this.notifications});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [notifications];
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
part of 'notification_bloc.dart';
|
||||
|
||||
class NotificationState extends Equatable {
|
||||
const NotificationState({
|
||||
required this.isLoading,
|
||||
required this.paginationLoading,
|
||||
required this.currentPage,
|
||||
this.pageCount,
|
||||
this.notifications,
|
||||
});
|
||||
|
||||
final bool isLoading;
|
||||
final bool paginationLoading;
|
||||
final int currentPage;
|
||||
final int? pageCount;
|
||||
final List<Notifications>? notifications;
|
||||
|
||||
NotificationState copyWith({
|
||||
bool? isLoading,
|
||||
bool? paginationLoading,
|
||||
int? currentPage,
|
||||
int? pageCount,
|
||||
List<Notifications>? notifications,
|
||||
}) {
|
||||
return NotificationState(
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
paginationLoading: paginationLoading ?? this.paginationLoading,
|
||||
currentPage: currentPage ?? this.currentPage,
|
||||
pageCount: pageCount ?? this.pageCount,
|
||||
notifications: notifications ?? this.notifications,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
isLoading,
|
||||
paginationLoading,
|
||||
currentPage,
|
||||
pageCount,
|
||||
notifications,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import 'package:cargocalculaterapp/features/home/data/models/order_single_response.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../domain/usecase/order_single_usecase.dart';
|
||||
|
||||
part 'order_single_event.dart';
|
||||
|
||||
part 'order_single_state.dart';
|
||||
|
||||
class OrderSingleBloc extends Bloc<OrderSingleEvent, OrderSingleState> {
|
||||
OrderSingleBloc(this.orderSingleUseCase)
|
||||
: super(const OrderSingleState(isLoading: false)) {
|
||||
on<GetOrderSingleEvent>(_getOrderInfo);
|
||||
}
|
||||
|
||||
final OrderSingleUseCase orderSingleUseCase;
|
||||
|
||||
Future<void> _getOrderInfo(
|
||||
GetOrderSingleEvent event,
|
||||
Emitter<OrderSingleState> emit,
|
||||
) async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final response = await orderSingleUseCase(event.orderId);
|
||||
response.fold(
|
||||
(l) {
|
||||
emit(state.copyWith(isLoading: false));
|
||||
},
|
||||
(r) {
|
||||
emit(state.copyWith(isLoading: false, orderSingleResponse: r));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
part of 'order_single_bloc.dart';
|
||||
|
||||
sealed class OrderSingleEvent extends Equatable {
|
||||
const OrderSingleEvent();
|
||||
}
|
||||
|
||||
class GetOrderSingleEvent extends OrderSingleEvent {
|
||||
const GetOrderSingleEvent({required this.orderId});
|
||||
|
||||
final String orderId;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [orderId];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
part of 'order_single_bloc.dart';
|
||||
|
||||
class OrderSingleState extends Equatable {
|
||||
const OrderSingleState({required this.isLoading, this.orderSingleResponse});
|
||||
|
||||
final bool isLoading;
|
||||
final OrderSingleResponse? orderSingleResponse;
|
||||
|
||||
OrderSingleState copyWith({
|
||||
bool? isLoading,
|
||||
OrderSingleResponse? orderSingleResponse,
|
||||
}) {
|
||||
return OrderSingleState(
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
orderSingleResponse: orderSingleResponse ?? this.orderSingleResponse,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [isLoading, orderSingleResponse];
|
||||
}
|
||||
13
lib/features/home/presentation/mixin/banner_mixin.dart
Normal file
13
lib/features/home/presentation/mixin/banner_mixin.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
mixin BannerMixin {
|
||||
late PageController pageController;
|
||||
|
||||
void initController() {
|
||||
pageController = PageController(viewportFraction: 0.94);
|
||||
}
|
||||
|
||||
void disposeController() {
|
||||
pageController.dispose();
|
||||
}
|
||||
}
|
||||
13
lib/features/home/presentation/mixin/comment_mixin.dart
Normal file
13
lib/features/home/presentation/mixin/comment_mixin.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
mixin CommentMixin {
|
||||
late TextEditingController commentController;
|
||||
|
||||
void initControllers() {
|
||||
commentController = TextEditingController();
|
||||
}
|
||||
|
||||
void disposeControllers() {
|
||||
commentController.dispose();
|
||||
}
|
||||
}
|
||||
13
lib/features/home/presentation/mixin/home_mixin.dart
Normal file
13
lib/features/home/presentation/mixin/home_mixin.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
mixin HomeMixin {
|
||||
late ScrollController scrollController;
|
||||
|
||||
void initControllers() {
|
||||
scrollController = ScrollController();
|
||||
}
|
||||
|
||||
void disposeControllers() {
|
||||
scrollController.dispose();
|
||||
}
|
||||
}
|
||||
13
lib/features/home/presentation/mixin/image_page_mixin.dart
Normal file
13
lib/features/home/presentation/mixin/image_page_mixin.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
mixin ImagePageMixin {
|
||||
late PageController pageController;
|
||||
|
||||
void initController(int initialPage) {
|
||||
pageController = PageController(initialPage: initialPage + 1);
|
||||
}
|
||||
|
||||
void disposeController() {
|
||||
pageController.dispose();
|
||||
}
|
||||
}
|
||||
13
lib/features/home/presentation/mixin/notification_mixin.dart
Normal file
13
lib/features/home/presentation/mixin/notification_mixin.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
mixin NotificationMixin {
|
||||
late ScrollController scrollController;
|
||||
|
||||
void initControllers() {
|
||||
scrollController = ScrollController();
|
||||
}
|
||||
|
||||
void disposeControllers() {
|
||||
scrollController.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class CommentArgument {
|
||||
final String orderId;
|
||||
|
||||
CommentArgument({required this.orderId});
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
class ImagePageArgument {
|
||||
final List<String>? images;
|
||||
final int? currentPage;
|
||||
|
||||
ImagePageArgument({this.images, this.currentPage});
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
class OrderInfoArgument{
|
||||
final String orderId;
|
||||
final String orderNumber;
|
||||
|
||||
OrderInfoArgument({required this.orderId, required this.orderNumber});
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:cargocalculaterapp/core/widgets/loading/progress_hud.dart';
|
||||
import 'package:cargocalculaterapp/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../../../core/widgets/rating/rating_bar_widget.dart';
|
||||
import '../../../../../core/widgets/text_filds/custom_text_field_name.dart';
|
||||
import '../../bloc/comment/comment_bloc.dart';
|
||||
import '../../mixin/comment_mixin.dart';
|
||||
import '../arguments/comment_argument.dart';
|
||||
|
||||
class CommentPage extends StatefulWidget {
|
||||
const CommentPage({super.key, required this.argument});
|
||||
|
||||
final CommentArgument? argument;
|
||||
|
||||
@override
|
||||
State<CommentPage> createState() => _CommentPageState();
|
||||
}
|
||||
|
||||
class _CommentPageState extends State<CommentPage> with CommentMixin {
|
||||
@override
|
||||
void initState() {
|
||||
initControllers();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<CommentBloc, CommentState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(AppLocalization.current.left_comment)),
|
||||
body: ModalProgressHUD(
|
||||
inAsyncCall: state.isLoading,
|
||||
child: ListView(
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
children: [
|
||||
Text(
|
||||
AppLocalization.current.comment_desc,
|
||||
style: context.text.authDesc,
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
RatingBarWidget(
|
||||
rating: state.rating,
|
||||
separator: AppUtils.kBoxWith12,
|
||||
iconsSize: 24,
|
||||
onRatingChanged: (rating) {
|
||||
context.read<CommentBloc>().add(
|
||||
RatingChangedEvent(rating: rating),
|
||||
);
|
||||
},
|
||||
),
|
||||
AppUtils.kBoxHeight24,
|
||||
CustomTextFieldName(
|
||||
name: AppLocalization.current.comment,
|
||||
hint: AppLocalization.current.comment_text,
|
||||
minLines: 4,
|
||||
controller: commentController,
|
||||
onchange: (comment) {
|
||||
context.read<CommentBloc>().add(
|
||||
CommentEnterEvent(comment: comment),
|
||||
);
|
||||
},
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
Padding(
|
||||
padding: AppUtils.kPaddingHor34,
|
||||
child: ElevatedButton(
|
||||
onPressed:
|
||||
state.rating > 0 && (state.comment?.isNotEmpty ?? false)
|
||||
? () {
|
||||
context.read<CommentBloc>().add(
|
||||
SubmitEvent(
|
||||
orderId: widget.argument?.orderId ?? "",
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
child: Text(AppLocalization.current.send),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disposeControllers();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
139
lib/features/home/presentation/pages/home_page.dart
Normal file
139
lib/features/home/presentation/pages/home_page.dart
Normal file
@@ -0,0 +1,139 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/local_source/local_source.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:cargocalculaterapp/core/widgets/loading/custom_loading.dart';
|
||||
import 'package:cargocalculaterapp/features/home/presentation/pages/widgets/banners_widget.dart';
|
||||
import 'package:cargocalculaterapp/features/home/presentation/pages/widgets/empty_order_widget.dart';
|
||||
import 'package:cargocalculaterapp/features/home/presentation/pages/widgets/order_list_shimmer.dart';
|
||||
import 'package:cargocalculaterapp/features/home/presentation/pages/widgets/orders_list_widget.dart';
|
||||
import 'package:cargocalculaterapp/features/home/presentation/pages/widgets/status_list_widget.dart';
|
||||
import 'package:cargocalculaterapp/generated/l10n.dart';
|
||||
import 'package:cargocalculaterapp/router/name_routes.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import '../../../../injector_container.dart';
|
||||
import '../bloc/home_bloc.dart';
|
||||
import '../mixin/home_mixin.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
State<HomePage> createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> with HomeMixin {
|
||||
@override
|
||||
void initState() {
|
||||
context.read<HomeBloc>().add(const GetAllOrdersListEvent());
|
||||
context.read<HomeBloc>().add(const GetBannersEvent());
|
||||
initControllers();
|
||||
_scrollListener();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _scrollListener() {
|
||||
scrollController.addListener(() {
|
||||
final state = context.read<HomeBloc>().state;
|
||||
if (scrollController.position.pixels >=
|
||||
scrollController.position.maxScrollExtent - 200 &&
|
||||
!state.paginationLoading &&
|
||||
state.page <= (state.pageCount ?? 1)) {
|
||||
context.read<HomeBloc>().add(const PaginationEvent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<HomeBloc, HomeState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
backgroundColor: context.color.grayBackground,
|
||||
appBar: AppBar(
|
||||
title: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: sl<LocalSource>().getUCode()),
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalization.current.text_copied),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
sl<LocalSource>().getUCode(),
|
||||
style: context.text.secondaryText14,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(AppLocalization.current.orders),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
automaticallyImplyLeading: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, Routes.notification);
|
||||
},
|
||||
icon: SvgPicture.asset(
|
||||
"assets/svg/ic_notification.svg",
|
||||
colorFilter: ColorFilter.mode(
|
||||
context.color.textColor,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxWith12,
|
||||
],
|
||||
),
|
||||
body: CustomScrollView(
|
||||
controller: scrollController,
|
||||
physics: const BouncingScrollPhysics(
|
||||
parent: AlwaysScrollableScrollPhysics(),
|
||||
),
|
||||
slivers: [
|
||||
StatusListWidget(selectedStatus: state.selectedStatus),
|
||||
CupertinoSliverRefreshControl(
|
||||
onRefresh: () async {
|
||||
context.read<HomeBloc>().add(const RefreshEvent());
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 1500),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (state.banners?.isNotEmpty ?? false)
|
||||
BannersWidget(banners: state.banners),
|
||||
state.isLoading
|
||||
? const OrderListShimmer()
|
||||
: (state.ordersList?.data?.length ?? 0) > 0
|
||||
? OrdersListWidget(data: state.ordersList?.data)
|
||||
: const EmptyOrderWidget(),
|
||||
if (state.paginationLoading)
|
||||
const SliverPadding(
|
||||
padding: AppUtils.kPaddingAll24,
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: Center(child: CustomLoadingWidget()),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disposeControllers();
|
||||
scrollController.removeListener(_scrollListener);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
46
lib/features/home/presentation/pages/image/image_page.dart
Normal file
46
lib/features/home/presentation/pages/image/image_page.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../../constants/constants.dart';
|
||||
import '../../mixin/image_page_mixin.dart';
|
||||
import '../arguments/image_page_argument.dart';
|
||||
|
||||
class ImagePage extends StatefulWidget {
|
||||
const ImagePage({super.key, required this.argument});
|
||||
|
||||
final ImagePageArgument? argument;
|
||||
|
||||
@override
|
||||
State<ImagePage> createState() => _ImagePageState();
|
||||
}
|
||||
|
||||
class _ImagePageState extends State<ImagePage> with ImagePageMixin {
|
||||
@override
|
||||
void initState() {
|
||||
initController(widget.argument?.currentPage ?? 0);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: PageView.builder(
|
||||
controller: pageController,
|
||||
|
||||
itemCount: widget.argument?.images?.length ?? 0,
|
||||
itemBuilder: (_, index) => InteractiveViewer(
|
||||
scaleEnabled: true,
|
||||
panEnabled: true,
|
||||
constrained: true,
|
||||
maxScale: 14,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: "${Constants.baseUrl}${widget.argument?.images?[index]}",
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/functions/base_finctions.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:cargocalculaterapp/core/widgets/loading/progress_hud.dart';
|
||||
import 'package:cargocalculaterapp/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../../../../../core/widgets/loading/custom_loading.dart';
|
||||
import '../../bloc/notification/notification_bloc.dart';
|
||||
import '../../mixin/notification_mixin.dart';
|
||||
|
||||
class NotificationPage extends StatefulWidget {
|
||||
const NotificationPage({super.key});
|
||||
|
||||
@override
|
||||
State<NotificationPage> createState() => _NotificationPageState();
|
||||
}
|
||||
|
||||
class _NotificationPageState extends State<NotificationPage>
|
||||
with NotificationMixin {
|
||||
@override
|
||||
void initState() {
|
||||
context.read<NotificationBloc>().add(const GetNotificationsEvent());
|
||||
initControllers();
|
||||
super.initState();
|
||||
_scrollListener();
|
||||
}
|
||||
|
||||
void _scrollListener() {
|
||||
scrollController.addListener(() {
|
||||
final state = context.read<NotificationBloc>().state;
|
||||
if (scrollController.position.pixels >=
|
||||
scrollController.position.maxScrollExtent - 200 &&
|
||||
!state.paginationLoading &&
|
||||
state.currentPage <= (state.pageCount ?? 1)) {
|
||||
context.read<NotificationBloc>().add(
|
||||
NotificationPaginationEvent(page: state.currentPage + 1),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<NotificationBloc, NotificationState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalization.current.notification),
|
||||
elevation: 0.2,
|
||||
),
|
||||
body: ModalProgressHUD(
|
||||
inAsyncCall: state.paginationLoading,
|
||||
child: CustomScrollView(
|
||||
controller: scrollController,
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
sliver: SliverList.separated(
|
||||
itemBuilder: (context, index) => Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Ink(
|
||||
width: 36,
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: context.color.iconBackground,
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
),
|
||||
padding: AppUtils.kPaddingAll8,
|
||||
child: SvgPicture.asset(
|
||||
"assets/svg/ic_check_box.svg",
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxWith8,
|
||||
Expanded(
|
||||
child: Text(
|
||||
Functions.getTranslatedNameModel(
|
||||
state.notifications?[index].text,
|
||||
context,
|
||||
),
|
||||
style: context.text.authDesc,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
separatorBuilder: (_, _) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Divider(
|
||||
thickness: 1,
|
||||
height: 1,
|
||||
color: context.color.lightBorder,
|
||||
),
|
||||
),
|
||||
itemCount: state.notifications?.length ?? 0,
|
||||
),
|
||||
),
|
||||
if (state.paginationLoading)
|
||||
const SliverPadding(
|
||||
padding: AppUtils.kPaddingAll24,
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: Center(child: CustomLoadingWidget()),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disposeControllers();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:cargocalculaterapp/core/widgets/loading/custom_loading.dart';
|
||||
import 'package:cargocalculaterapp/features/home/presentation/pages/arguments/image_page_argument.dart';
|
||||
import 'package:cargocalculaterapp/features/home/presentation/pages/order_info/widgets/delivery_info_widget.dart';
|
||||
import 'package:cargocalculaterapp/features/home/presentation/pages/order_info/widgets/order_info_widget.dart';
|
||||
import 'package:cargocalculaterapp/features/home/presentation/pages/order_info/widgets/status_item_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../../../../constants/constants.dart';
|
||||
import '../../../../../core/functions/base_finctions.dart';
|
||||
import '../../../../../generated/l10n.dart';
|
||||
import '../../../../../router/name_routes.dart';
|
||||
import '../../bloc/order_single/order_single_bloc.dart';
|
||||
import '../arguments/comment_argument.dart';
|
||||
import '../arguments/order_info_argument.dart';
|
||||
|
||||
class OrderInfoPage extends StatefulWidget {
|
||||
const OrderInfoPage({super.key, required this.argument});
|
||||
|
||||
final OrderInfoArgument? argument;
|
||||
|
||||
@override
|
||||
State<OrderInfoPage> createState() => _OrderInfoPageState();
|
||||
}
|
||||
|
||||
class _OrderInfoPageState extends State<OrderInfoPage> {
|
||||
@override
|
||||
void initState() {
|
||||
context.read<OrderSingleBloc>().add(
|
||||
GetOrderSingleEvent(orderId: widget.argument?.orderId ?? ""),
|
||||
);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<OrderSingleBloc, OrderSingleState>(
|
||||
builder: (context, state) {
|
||||
final status = _checkStatus(
|
||||
state.orderSingleResponse?.status?.code ?? "",
|
||||
);
|
||||
return Scaffold(
|
||||
backgroundColor: context.color.grayBackground,
|
||||
appBar: AppBar(title: Text("#${widget.argument?.orderNumber ?? ""}")),
|
||||
body: state.isLoading
|
||||
? const Center(child: CustomLoadingWidget())
|
||||
: ListView(
|
||||
padding: AppUtils.kPaddingVer24Hor16,
|
||||
children: [
|
||||
Container(
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
color: context.color.statusBackground,
|
||||
border: Border.all(color: context.color.lightBorder),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
AppLocalization.current.order_delivery_info,
|
||||
style: context.text.orderTitle,
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
DeliveryInfoWidget(
|
||||
title: AppLocalization.current.order_number,
|
||||
name: state.orderSingleResponse?.orderNumber ?? "",
|
||||
icon: "assets/svg/ic_box_gray.svg",
|
||||
),
|
||||
DeliveryInfoWidget(
|
||||
title: AppLocalization.current.warehouse,
|
||||
name:
|
||||
Localizations.localeOf(context).languageCode ==
|
||||
"uz"
|
||||
? (state.orderSingleResponse?.wharehouseUz ??
|
||||
"")
|
||||
: Localizations.localeOf(
|
||||
context,
|
||||
).languageCode ==
|
||||
"ru"
|
||||
? (state.orderSingleResponse?.wharehouseRu ??
|
||||
"")
|
||||
: (state.orderSingleResponse?.wharehouseZh ??
|
||||
""),
|
||||
icon: "assets/svg/ic_building.svg",
|
||||
),
|
||||
DeliveryInfoWidget(
|
||||
title: AppLocalization.current.delivery_price,
|
||||
name:
|
||||
state.orderSingleResponse?.deliveryPrice ?? "",
|
||||
icon: "assets/svg/ic_chek.svg",
|
||||
),
|
||||
DeliveryInfoWidget(
|
||||
title:
|
||||
AppLocalization.current.delivery_time_estimated,
|
||||
name: DateFormat('dd-MM-yyyy').format(
|
||||
DateTime.tryParse(
|
||||
state.orderSingleResponse?.arrivalDate ??
|
||||
"",
|
||||
) ??
|
||||
DateTime.now(),
|
||||
),
|
||||
icon: "assets/svg/ic_calendar.svg",
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
color: Colors.black.withAlpha(20),
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
StatusItemWidget(
|
||||
isCompleted: status >= 1,
|
||||
dividerCompleted: status > 1,
|
||||
title: Functions.getTranslatedItem(
|
||||
AppConst.statusList[AppConst.waiting],
|
||||
context,
|
||||
),
|
||||
number: "1",
|
||||
),
|
||||
StatusItemWidget(
|
||||
isCompleted: status >= 2,
|
||||
dividerCompleted: status > 2,
|
||||
title: Functions.getTranslatedItem(
|
||||
AppConst.statusList[AppConst.partReceived],
|
||||
context,
|
||||
),
|
||||
number: "2",
|
||||
),
|
||||
StatusItemWidget(
|
||||
isCompleted: status >= 3,
|
||||
dividerCompleted: status > 3,
|
||||
title: Functions.getTranslatedItem(
|
||||
AppConst.statusList[AppConst.fullReceived],
|
||||
context,
|
||||
),
|
||||
number: "3",
|
||||
),
|
||||
StatusItemWidget(
|
||||
isCompleted: status >= 4,
|
||||
dividerCompleted: status > 4,
|
||||
title: Functions.getTranslatedItem(
|
||||
AppConst.statusList[AppConst.shipped],
|
||||
context,
|
||||
),
|
||||
number: "4",
|
||||
),
|
||||
StatusItemWidget(
|
||||
dividerCompleted: status > 5,
|
||||
isCompleted: status >= 5,
|
||||
title: Functions.getTranslatedItem(
|
||||
AppConst.statusList[AppConst.leftChine],
|
||||
context,
|
||||
),
|
||||
number: "5",
|
||||
),
|
||||
StatusItemWidget(
|
||||
isCompleted: status >= 6,
|
||||
dividerCompleted: status > 6,
|
||||
title: Functions.getTranslatedItem(
|
||||
AppConst.statusList[AppConst.enteredUzbekistan],
|
||||
context,
|
||||
),
|
||||
number: "6",
|
||||
),
|
||||
StatusItemWidget(
|
||||
isCompleted: status >= 7,
|
||||
dividerCompleted: status > 7,
|
||||
title: Functions.getTranslatedItem(
|
||||
AppConst.statusList[AppConst.tashkentWarehouse],
|
||||
context,
|
||||
),
|
||||
number: "7",
|
||||
),
|
||||
StatusItemWidget(
|
||||
isCompleted: status >= 8,
|
||||
dividerCompleted: status > 8,
|
||||
title: Functions.getTranslatedItem(
|
||||
AppConst.statusList[AppConst.customsClearance],
|
||||
context,
|
||||
),
|
||||
number: "8",
|
||||
),
|
||||
StatusItemWidget(
|
||||
isCompleted: status >= 9,
|
||||
dividerCompleted: status > 9,
|
||||
title: Functions.getTranslatedItem(
|
||||
AppConst.statusList[AppConst.delivered],
|
||||
context,
|
||||
),
|
||||
showConnector: false,
|
||||
number: "9",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 16),
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
color: context.color.statusBackground,
|
||||
border: Border.all(color: context.color.lightBorder),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
AppLocalization.current.order_details,
|
||||
style: context.text.orderTitle,
|
||||
),
|
||||
AppUtils.kBoxHeight12,
|
||||
OrderInfoWidget(
|
||||
title: AppLocalization.current.order_name,
|
||||
name:
|
||||
Localizations.localeOf(context).languageCode ==
|
||||
"uz"
|
||||
? (state.orderSingleResponse?.nameUz ?? "")
|
||||
: Localizations.localeOf(
|
||||
context,
|
||||
).languageCode ==
|
||||
"ru"
|
||||
? (state.orderSingleResponse?.nameRu ?? "")
|
||||
: (state.orderSingleResponse?.nameZh ?? ""),
|
||||
),
|
||||
OrderInfoWidget(
|
||||
title: AppLocalization.current.weight,
|
||||
name: state.orderSingleResponse?.weight ?? "",
|
||||
),
|
||||
OrderInfoWidget(
|
||||
title: AppLocalization.current.size,
|
||||
name: state.orderSingleResponse?.m3 ?? "",
|
||||
),
|
||||
OrderInfoWidget(
|
||||
title: AppLocalization.current.average_weight,
|
||||
name:
|
||||
state.orderSingleResponse?.averageWeightKg ??
|
||||
"",
|
||||
),
|
||||
AppUtils.kBoxHeight8,
|
||||
if ((state.orderSingleResponse?.images?.length ?? 0) >
|
||||
0)
|
||||
Text(
|
||||
AppLocalization.current.order_photo,
|
||||
style: context.text.orderTitle,
|
||||
),
|
||||
if ((state.orderSingleResponse?.images?.length ?? 0) >
|
||||
0)
|
||||
AppUtils.kBoxHeight8,
|
||||
if ((state.orderSingleResponse?.images?.length ?? 0) >
|
||||
0)
|
||||
SizedBox(
|
||||
height: 72,
|
||||
child: ListView.separated(
|
||||
padding: EdgeInsets.zero,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) => GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.image,
|
||||
arguments: ImagePageArgument(
|
||||
currentPage: index,
|
||||
images:
|
||||
state.orderSingleResponse?.images,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: ClipRRect(
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl:
|
||||
"${Constants.baseUrl}${state.orderSingleResponse?.images?[index]}",
|
||||
width: 72,
|
||||
height: 72,
|
||||
fit: BoxFit.cover,
|
||||
errorWidget: (context, _, _) {
|
||||
return const SizedBox(
|
||||
width: 72,
|
||||
height: 72,
|
||||
child: Center(
|
||||
child: Icon(Icons.warning),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
separatorBuilder: (_, _) => AppUtils.kBoxWidth8,
|
||||
itemCount:
|
||||
state.orderSingleResponse?.images?.length ??
|
||||
0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (status == 9)
|
||||
Padding(
|
||||
padding: AppUtils.kPaddingHor34,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.comment,
|
||||
arguments: CommentArgument(
|
||||
orderId: widget.argument?.orderNumber ?? "",
|
||||
),
|
||||
);
|
||||
},
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
color: context.color.scaffoldBackgroundColor,
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
border: Border.all(
|
||||
color: context.color.primaryColor,
|
||||
),
|
||||
),
|
||||
height: 56,
|
||||
padding: AppUtils.kPaddingHor16,
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppLocalization.current.left_comment,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: context.color.textColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
int _checkStatus(String status) {
|
||||
switch (status) {
|
||||
case AppConst.waiting:
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
case AppConst.partReceived:
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
case AppConst.fullReceived:
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
case AppConst.shipped:
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
case AppConst.leftChine:
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
case AppConst.enteredUzbekistan:
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
case AppConst.tashkentWarehouse:
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
case AppConst.customsClearance:
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
case AppConst.delivered:
|
||||
{
|
||||
return 9;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
class DeliveryInfoWidget extends StatelessWidget {
|
||||
const DeliveryInfoWidget({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.title,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
final String icon;
|
||||
final String title;
|
||||
final String name;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(icon, width: 12, height: 12),
|
||||
AppUtils.kBoxWidth4,
|
||||
Expanded(child: Text(title, style: context.text.orderListTitle)),
|
||||
Text(name, style: context.text.profileCategory),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class OrderInfoWidget extends StatelessWidget {
|
||||
const OrderInfoWidget({super.key, required this.title, required this.name});
|
||||
|
||||
final String title;
|
||||
final String name;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(child: Text(title, style: context.text.orderListTitle)),
|
||||
AppUtils.kBoxWidth8,
|
||||
Expanded(
|
||||
child: Text(
|
||||
name,
|
||||
style: context.text.profileCategory,
|
||||
textAlign: TextAlign.end,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class StatusItemWidget extends StatelessWidget {
|
||||
final bool showConnector;
|
||||
final bool isCompleted;
|
||||
final bool dividerCompleted;
|
||||
final String title;
|
||||
final String number;
|
||||
|
||||
const StatusItemWidget({
|
||||
super.key,
|
||||
|
||||
this.showConnector = true,
|
||||
this.dividerCompleted = false,
|
||||
required this.isCompleted,
|
||||
required this.title,
|
||||
required this.number,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 4, bottom: 4, right: 12),
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
isCompleted
|
||||
? context.color.primaryColor
|
||||
: context.color.statusIconBackground,
|
||||
border: Border.all(color: context.color.primaryColor),
|
||||
borderRadius: AppUtils.kBorderRadius48,
|
||||
),
|
||||
padding: AppUtils.kPaddingAll6,
|
||||
child:
|
||||
isCompleted
|
||||
? const Center(
|
||||
child: Icon(
|
||||
Icons.check_rounded,
|
||||
color: Colors.white,
|
||||
size: 16,
|
||||
),
|
||||
)
|
||||
: Center(
|
||||
child: Text(number, style: context.text.statusNumber),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.start,
|
||||
style: context.text.statusDesc,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (showConnector)
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 15),
|
||||
height: 16,
|
||||
width: 2,
|
||||
color: context.color.primaryColor,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
101
lib/features/home/presentation/pages/widgets/banners_widget.dart
Normal file
101
lib/features/home/presentation/pages/widgets/banners_widget.dart
Normal file
@@ -0,0 +1,101 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../../constants/constants.dart';
|
||||
import '../../../../../core/utils/app_utils.dart';
|
||||
import '../../../data/models/banner_response.dart';
|
||||
import '../../mixin/banner_mixin.dart';
|
||||
|
||||
class BannersWidget extends StatefulWidget {
|
||||
const BannersWidget({super.key, required this.banners});
|
||||
|
||||
final List<BannerResponse>? banners;
|
||||
|
||||
@override
|
||||
State<BannersWidget> createState() => _BannersWidgetState();
|
||||
}
|
||||
|
||||
class _BannersWidgetState extends State<BannersWidget> with BannerMixin {
|
||||
Timer? timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
initController();
|
||||
if ((widget.banners?.length ?? 0) > 1) {
|
||||
animationTimer();
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant BannersWidget oldWidget) {
|
||||
if ((widget.banners?.length ?? 0) > 1) {
|
||||
animationTimer();
|
||||
} else if ((widget.banners?.length ?? 0) == 1) {
|
||||
if (timer?.isActive ?? false) {
|
||||
timer?.cancel();
|
||||
}
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
void animationTimer() {
|
||||
if (timer?.isActive ?? false) {
|
||||
timer?.cancel();
|
||||
}
|
||||
timer = Timer.periodic(const Duration(seconds: 4), (Timer timer) async {
|
||||
pageController.nextPage(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
timer?.cancel();
|
||||
pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: SizedBox(
|
||||
height: 180,
|
||||
width: double.infinity,
|
||||
child: PageView.builder(
|
||||
controller: pageController,
|
||||
itemCount: (widget.banners?.length ?? 0) > 1
|
||||
? null
|
||||
: widget.banners?.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
physics: widget.banners?.length != 1
|
||||
? null
|
||||
: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (_, index) => GestureDetector(
|
||||
onTap: () {},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: ClipRRect(
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
|
||||
child: CachedNetworkImage(
|
||||
imageUrl:
|
||||
"${Constants.baseUrl}${widget.banners?[index % (widget.banners?.length ?? 1)].image}",
|
||||
height: 180,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import '../../../../../core/utils/app_utils.dart';
|
||||
|
||||
class EmptyOrderWidget extends StatelessWidget {
|
||||
const EmptyOrderWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||
width: double.infinity,
|
||||
// padding: const EdgeInsets.symmetric(vertical: 42, horizontal: 32),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
color: context.color.statusBackground,
|
||||
border: Border.all(color: context.color.lightBorder),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: AppUtils.kBorderRadiusTop16,
|
||||
child: SvgPicture.asset(
|
||||
"assets/svg/ic_empty_box.svg",
|
||||
width: MediaQuery.sizeOf(context).width - 34,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
AppLocalization.current.no_order,
|
||||
style: context.text.titleBig,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
AppUtils.kBoxHeight12,
|
||||
Padding(
|
||||
padding: AppUtils.kPaddingHor24,
|
||||
child: Text(
|
||||
AppLocalization.current.no_order_desc,
|
||||
style: context.text.profileCategory.copyWith(
|
||||
color: context.color.lightSecondary,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
AppUtils.kBoxHeight48,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/widgets/shimmer/shimmer_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../../core/utils/app_utils.dart';
|
||||
|
||||
class OrderListShimmer extends StatelessWidget {
|
||||
const OrderListShimmer({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverPadding(
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
sliver: SliverList.separated(
|
||||
itemBuilder: (context, index) {
|
||||
return Ink(
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius12,
|
||||
color: context.color.scaffoldBackgroundColor,
|
||||
),
|
||||
child: const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ShimmerWidget(width: 60, height: 20),
|
||||
ShimmerWidget(width: 100, height: 20),
|
||||
],
|
||||
),
|
||||
AppUtils.kBoxHeight8,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ShimmerWidget(width: 40, height: 20),
|
||||
ShimmerWidget(width: 70, height: 20),
|
||||
],
|
||||
),
|
||||
AppUtils.kBoxHeight4,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ShimmerWidget(width: 100, height: 20),
|
||||
ShimmerWidget(width: 60, height: 20),
|
||||
],
|
||||
),
|
||||
AppUtils.kBoxHeight4,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ShimmerWidget(width: 80, height: 20),
|
||||
ShimmerWidget(width: 140, height: 20),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, __) => AppUtils.kBoxHeight12,
|
||||
itemCount: 6,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class OrderTextWidget extends StatelessWidget {
|
||||
const OrderTextWidget({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.name,
|
||||
this.crossAxisAlignment = CrossAxisAlignment.start,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String name;
|
||||
final CrossAxisAlignment crossAxisAlignment;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: crossAxisAlignment,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(title, style: context.text.orderListTitle),
|
||||
AppUtils.kBoxHeight6,
|
||||
Text(name, style: context.text.categoryName),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:cargocalculaterapp/features/home/presentation/pages/widgets/status_widget.dart';
|
||||
import 'package:cargocalculaterapp/generated/l10n.dart';
|
||||
import 'package:cargocalculaterapp/router/name_routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../../data/models/orders_list_response.dart';
|
||||
import '../arguments/order_info_argument.dart';
|
||||
import 'order_text_widget.dart';
|
||||
|
||||
class OrdersListWidget extends StatelessWidget {
|
||||
const OrdersListWidget({super.key, required this.data});
|
||||
|
||||
final List<Data>? data;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
|
||||
sliver: SliverList.separated(
|
||||
itemBuilder: (context, index) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.orderInfo,
|
||||
arguments: OrderInfoArgument(
|
||||
orderId: data?[index].id ?? "",
|
||||
orderNumber: data?[index].orderNumber ?? "",
|
||||
),
|
||||
);
|
||||
},
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
child: Ink(
|
||||
padding: AppUtils.kPaddingAll16,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
color: context.color.statusBackground,
|
||||
border: Border.all(color: context.color.lightBorder),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
OrderTextWidget(
|
||||
title: AppLocalization.current.order_number,
|
||||
name: data?[index].orderNumber ?? "",
|
||||
),
|
||||
StatusWidget(status: data?[index].status?.code ?? ""),
|
||||
],
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
Row(
|
||||
children: [
|
||||
OrderTextWidget(
|
||||
title: AppLocalization.current.warehouse,
|
||||
name:
|
||||
Localizations.localeOf(context).languageCode == "uz"
|
||||
? (data?[index].wharehouseUz ?? "")
|
||||
: Localizations.localeOf(context).languageCode ==
|
||||
"ru"
|
||||
? (data?[index].wharehouseRu ?? "")
|
||||
: (data?[index].wharehouseZh ?? ""),
|
||||
),
|
||||
OrderTextWidget(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
title: AppLocalization.current.order_name,
|
||||
name:
|
||||
Localizations.localeOf(context).languageCode == "uz"
|
||||
? (data?[index].nameUz ?? "")
|
||||
: Localizations.localeOf(context).languageCode ==
|
||||
"ru"
|
||||
? (data?[index].nameRu ?? "")
|
||||
: (data?[index].nameZh ?? ""),
|
||||
),
|
||||
],
|
||||
),
|
||||
AppUtils.kBoxHeight16,
|
||||
Row(
|
||||
children: [
|
||||
OrderTextWidget(
|
||||
title: AppLocalization.current.delivery_price,
|
||||
name: (data?[index].deliveryPrice ?? ""),
|
||||
),
|
||||
OrderTextWidget(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
title: AppLocalization.current.delivery_time,
|
||||
name: DateFormat('dd-MM-yyyy').format(
|
||||
DateTime.tryParse(data?[index].arrivalDate ?? "") ??
|
||||
DateTime.now(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, __) => AppUtils.kBoxHeight12,
|
||||
itemCount: data?.length ?? 0,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import 'package:cargocalculaterapp/constants/constants.dart';
|
||||
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
|
||||
import 'package:cargocalculaterapp/core/functions/base_finctions.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:cargocalculaterapp/features/home/presentation/bloc/home_bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class StatusListWidget extends StatelessWidget {
|
||||
const StatusListWidget({super.key, required this.selectedStatus});
|
||||
|
||||
final String selectedStatus;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverPersistentHeader(
|
||||
pinned: true,
|
||||
delegate: _StatusHeaderDelegate(selectedStatus: selectedStatus),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _StatusHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
final String selectedStatus;
|
||||
|
||||
_StatusHeaderDelegate({required this.selectedStatus});
|
||||
|
||||
@override
|
||||
double get minExtent => 72;
|
||||
|
||||
@override
|
||||
double get maxExtent => 72;
|
||||
|
||||
@override
|
||||
Widget build(
|
||||
BuildContext context,
|
||||
double shrinkOffset,
|
||||
bool overlapsContent,
|
||||
) {
|
||||
return Material(
|
||||
color: context.color.grayBackground,
|
||||
child: SizedBox(
|
||||
height: 72,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemBuilder: (context, index) {
|
||||
final key = AppConst.statusList.keys.toList()[index];
|
||||
return InkWell(
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
onTap: () {
|
||||
context.read<HomeBloc>().add(OrderStatusEvent(status: key));
|
||||
},
|
||||
child: Ink(
|
||||
padding: AppUtils.kPaddingHor16,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius16,
|
||||
color:
|
||||
key == selectedStatus
|
||||
? context.color.primaryColor
|
||||
: context.color.statusBackground,
|
||||
border: Border.all(
|
||||
color:
|
||||
key == selectedStatus
|
||||
? context.color.primaryColor
|
||||
: context.color.lightSecondary,
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
Functions.getTranslatedItem(
|
||||
AppConst.statusList.values.toList()[index],
|
||||
context,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
style:
|
||||
key == selectedStatus
|
||||
? context.text.statusText
|
||||
: context.text.statusText.copyWith(
|
||||
color: context.color.textColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, __) => AppUtils.kBoxWidth16,
|
||||
itemCount: AppConst.statusList.length,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRebuild(covariant _StatusHeaderDelegate oldDelegate) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import 'package:cargocalculaterapp/core/functions/base_finctions.dart';
|
||||
import 'package:cargocalculaterapp/core/theme/app_text_styles.dart';
|
||||
import 'package:cargocalculaterapp/core/theme/colors/app_colors.dart';
|
||||
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../../constants/constants.dart';
|
||||
|
||||
class StatusWidget extends StatelessWidget {
|
||||
const StatusWidget({super.key, required this.status});
|
||||
|
||||
final String status;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
switch (status) {
|
||||
case AppConst.waiting:
|
||||
{
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
color: ThemeColors.warningSurface,
|
||||
),
|
||||
child: Text(
|
||||
Functions.getTranslatedItem(
|
||||
AppConst.statusList[AppConst.waiting],
|
||||
context,
|
||||
),
|
||||
style: AppTextStyles.status.copyWith(color: ThemeColors.warning),
|
||||
),
|
||||
);
|
||||
}
|
||||
case AppConst.shipped:
|
||||
{
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
color: ThemeColors.green173Light,
|
||||
),
|
||||
child: Text(
|
||||
Functions.getTranslatedItem(
|
||||
AppConst.statusList[AppConst.shipped],
|
||||
context,
|
||||
),
|
||||
style: AppTextStyles.status,
|
||||
),
|
||||
);
|
||||
}
|
||||
case AppConst.leftChine:
|
||||
{
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
color: ThemeColors.green191Light,
|
||||
),
|
||||
child: Text(
|
||||
Functions.getTranslatedItem(
|
||||
AppConst.statusList[AppConst.leftChine],
|
||||
context,
|
||||
),
|
||||
style: AppTextStyles.status.copyWith(
|
||||
color: ThemeColors.green191text,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
default:
|
||||
{
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: AppUtils.kBorderRadius24,
|
||||
color: ThemeColors.green173Light,
|
||||
),
|
||||
child: Text(
|
||||
Functions.getTranslatedItem(
|
||||
AppConst.statusList[status],
|
||||
context,
|
||||
),
|
||||
style: AppTextStyles.status,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user