Merge branch 'refs/heads/master' into fatima
# Conflicts: # lib/core/dependencies.dart # lib/main.dart # lib/theme/colors.dartpull/152/head
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 543 KiB |
@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 3.75C9 2.09315 10.3431 0.75 12 0.75C13.6569 0.75 15 2.09315 15 3.75C15 4.82282 14.4369 5.76411 13.5901 6.29442C16.5251 6.71056 18.309 9.97478 17.1349 12.8582C16.8794 13.4857 16.2803 13.9167 15.5919 13.9167H15.0998L14.272 17.4022C14.019 18.4673 13.0967 19.25 12 19.25C10.9033 19.25 9.98103 18.4673 9.72805 17.4022L8.9002 13.9167L8.40809 13.9167C7.71969 13.9167 7.12056 13.4857 6.86508 12.8582C5.69103 9.97478 7.47495 6.71056 10.4099 6.29442C9.56313 5.76411 9 4.82282 9 3.75Z" fill="white"/>
|
||||
<path d="M7 15.25H6C5.44772 15.25 5 15.6977 5 16.25C5 16.8023 5.44772 17.25 6 17.25H7C7.55228 17.25 8 16.8023 8 16.25C8 15.6977 7.55228 15.25 7 15.25Z" fill="white"/>
|
||||
<path d="M17 15.25C16.4477 15.25 16 15.6977 16 16.25C16 16.8023 16.4477 17.25 17 17.25H18C18.5523 17.25 19 16.8023 19 16.25C19 15.6977 18.5523 15.25 18 15.25H17Z" fill="white"/>
|
||||
<path d="M13 21.25C13 20.6977 12.5523 20.25 12 20.25C11.4477 20.25 11 20.6977 11 21.25V22.25C11 22.8023 11.4477 23.25 12 23.25C12.5523 23.25 13 22.8023 13 22.25V21.25Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,7 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.0493 1.25C14.7141 1.24997 15.287 1.24994 15.7457 1.31161C16.2373 1.37771 16.7086 1.52677 17.0908 1.90902C17.4731 2.29126 17.6222 2.76253 17.6883 3.25416C17.7499 3.7129 17.7499 4.28574 17.7499 4.95059V6.04937C17.7499 6.71423 17.7499 7.28712 17.6883 7.74585C17.6222 8.23749 17.4731 8.70875 17.0908 9.091C16.7086 9.47324 16.2373 9.6223 15.7457 9.6884C15.287 9.75008 14.7141 9.75004 14.0492 9.75001L13.2269 9.75001L10.4159 11.624C10.1857 11.7775 9.88983 11.7918 9.64597 11.6613C9.4021 11.5307 9.24986 11.2766 9.24986 11V9.7469C8.87641 9.74158 8.54263 9.7272 8.25402 9.6884C7.76238 9.6223 7.29112 9.47324 6.90887 9.091C6.52662 8.70875 6.37756 8.23749 6.31147 7.74585C6.24979 7.28712 6.24982 6.71427 6.24986 6.04943V4.95063C6.24982 4.28579 6.24979 3.7129 6.31147 3.25416C6.37756 2.76253 6.52662 2.29126 6.90887 1.90902C7.29112 1.52677 7.76238 1.37771 8.25402 1.31161C8.71274 1.24994 9.28556 1.24997 9.95039 1.25L14.0493 1.25Z" fill="white"/>
|
||||
<path d="M3.17258 18.1177C4.89963 16.9608 7.10028 16.9608 8.82733 18.1177C9.13739 18.3137 9.84338 18.7614 10.2141 19.1697C10.4495 19.4288 10.6975 19.7967 10.7428 20.2626C10.7914 20.7627 10.5934 21.2221 10.2316 21.6099C9.68447 22.1963 8.95287 22.75 7.95957 22.75H4.04034C3.04704 22.75 2.31543 22.1963 1.76831 21.6099C1.40651 21.2221 1.20853 20.7627 1.25712 20.2626C1.30239 19.7967 1.55044 19.4288 1.78577 19.1697C2.15652 18.7614 2.86252 18.3137 3.17258 18.1177Z" fill="white"/>
|
||||
<path d="M20.8273 18.1177C19.1003 16.9608 16.8996 16.9608 15.1726 18.1177C14.8625 18.3137 14.1565 18.7614 13.7858 19.1697C13.5504 19.4288 13.3024 19.7967 13.2571 20.2626C13.2085 20.7627 13.4065 21.2221 13.7683 21.6099C14.3154 22.1963 15.047 22.75 16.0403 22.75H19.9596C20.9529 22.75 21.6845 22.1963 22.2316 21.6099C22.5934 21.2221 22.7914 20.7627 22.7428 20.2626C22.6975 19.7967 22.4495 19.4288 22.2141 19.1697C21.8434 18.7614 21.1374 18.3137 20.8273 18.1177Z" fill="white"/>
|
||||
<path d="M3.24986 13.5C3.24986 11.9812 4.48107 10.75 5.99986 10.75C7.51864 10.75 8.74986 11.9812 8.74986 13.5C8.74986 15.0188 7.51864 16.25 5.99986 16.25C4.48107 16.25 3.24986 15.0188 3.24986 13.5Z" fill="white"/>
|
||||
<path d="M17.9999 10.75C16.4811 10.75 15.2499 11.9812 15.2499 13.5C15.2499 15.0188 16.4811 16.25 17.9999 16.25C19.5186 16.25 20.7499 15.0188 20.7499 13.5C20.7499 11.9812 19.5186 10.75 17.9999 10.75Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@ -0,0 +1,5 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.49411 2.85661C9.06887 2.71829 8.56575 2.71849 7.82926 2.71878L5.20973 2.7188C4.36011 2.71878 3.66581 2.71877 3.11767 2.79255C2.5447 2.86967 2.04795 3.03656 1.65143 3.43352C1.25491 3.83049 1.0882 4.32779 1.01117 4.90139C0.93747 5.45014 0.937484 6.14521 0.9375 6.99576V10.0798C0.937484 10.9303 0.93747 11.6254 1.01117 12.1741C1.0882 12.7477 1.25491 13.245 1.65143 13.642C1.76583 13.7565 1.89283 13.8531 2.02893 13.9345C2.22063 14.0492 2.31647 14.1065 2.39389 14.059C2.47131 14.0115 2.46224 13.8786 2.44411 13.6127C2.43973 13.5484 2.4375 13.4836 2.4375 13.4181C2.4375 11.8631 3.6967 10.6025 5.25 10.6025C6.8033 10.6025 8.0625 11.8631 8.0625 13.4181C8.0625 13.5947 8.04626 13.7675 8.01519 13.9351C7.97875 14.1317 7.96053 14.23 8.00499 14.2841C8.04945 14.3381 8.14092 14.3389 8.32387 14.3406C8.77449 14.3448 9.22551 14.3448 9.67614 14.3406C9.85908 14.3389 9.95055 14.3381 9.99501 14.2841C10.0395 14.23 10.0213 14.1317 9.98481 13.9351C9.95374 13.7675 9.9375 13.5947 9.9375 13.4181C9.9375 11.8631 11.1967 10.6025 12.75 10.6025C14.3033 10.6025 15.5625 11.8631 15.5625 13.4181C15.5625 13.4835 15.5603 13.5484 15.5559 13.6127C15.5378 13.8786 15.5287 14.0115 15.6061 14.059C15.6835 14.1065 15.7794 14.0492 15.9711 13.9345C16.1072 13.8531 16.2342 13.7565 16.3486 13.642C16.7451 13.245 16.9118 12.7477 16.9888 12.1741C17.0625 11.6254 17.0625 10.9303 17.0625 10.0798V8.55433C17.0654 8.4286 17.0367 8.30042 16.9729 8.18177L15.9988 6.37069C15.7544 5.91626 15.5451 5.52708 15.3388 5.22173C15.1184 4.89536 14.8689 4.61351 14.5195 4.40455C14.17 4.19558 13.8038 4.10932 13.4123 4.06973C13.046 4.0327 12.6044 4.03272 12.0889 4.03274L11.2524 4.03274C11.1353 4.03274 11.0767 4.03274 11.0295 4.00933C10.9823 3.98592 10.9424 3.93349 10.8626 3.82864C10.5202 3.37853 10.0455 3.03597 9.49411 2.85661ZM11.7406 5.53441C11.5945 5.53441 11.5214 5.53441 11.4773 5.57901C11.4333 5.62362 11.4342 5.69802 11.4359 5.84684C11.4377 5.99664 11.4376 6.15736 11.4375 6.33106C11.4375 6.93389 11.4444 7.20613 11.4834 7.32612C11.5761 7.61185 11.7999 7.83587 12.0853 7.92871C12.2052 7.96769 12.3728 7.97464 12.975 7.97464H14.6556C14.8798 7.97464 14.9919 7.97464 15.0355 7.90148C15.0792 7.82832 15.026 7.72952 14.9197 7.53192L14.6949 7.11386C14.429 6.61959 14.2551 6.29803 14.0963 6.06291C13.9469 5.84183 13.845 5.75037 13.7503 5.69375C13.6556 5.63714 13.5268 5.59062 13.2616 5.5638C12.9795 5.53529 12.6142 5.53441 12.0535 5.53441H11.7406ZM4.5001 5.53125C4.91431 5.53125 5.2501 5.86704 5.2501 6.28125V7.78125C5.2501 8.19546 4.91431 8.53125 4.5001 8.53125C4.08589 8.53125 3.7501 8.19546 3.7501 7.78125V6.28125C3.7501 5.86704 4.08589 5.53125 4.5001 5.53125ZM7.5001 5.53125C7.91431 5.53125 8.2501 5.86704 8.2501 6.28125V7.78125C8.2501 8.19546 7.91431 8.53125 7.5001 8.53125C7.08589 8.53125 6.7501 8.19546 6.7501 7.78125V6.28125C6.7501 5.86704 7.08589 5.53125 7.5001 5.53125Z" fill="#2E3039"/>
|
||||
<path d="M5.25 11.5312C4.21447 11.5312 3.375 12.3707 3.375 13.4062C3.375 14.4418 4.21447 15.2812 5.25 15.2812C6.28553 15.2812 7.125 14.4418 7.125 13.4062C7.125 12.3707 6.28553 11.5312 5.25 11.5312Z" fill="#2E3039"/>
|
||||
<path d="M12.75 11.5312C11.7145 11.5312 10.875 12.3707 10.875 13.4062C10.875 14.4418 11.7145 15.2812 12.75 15.2812C13.7855 15.2812 14.625 14.4418 14.625 13.4062C14.625 12.3707 13.7855 11.5312 12.75 11.5312Z" fill="#2E3039"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,3 @@
|
||||
enum OrderDislpay{
|
||||
ALL,RRT,AMBULANCE
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
class RRTRequestModel {
|
||||
num? patientId;
|
||||
int? patientOutSa;
|
||||
bool? isOutPatient;
|
||||
int? nearestProjectId;
|
||||
num? longitude;
|
||||
num? latitude;
|
||||
String? additionalDetails;
|
||||
String? nationality;
|
||||
num? paymentAmount;
|
||||
List<Procedures>? procedures;
|
||||
|
||||
RRTRequestModel(
|
||||
{this.patientId,
|
||||
this.patientOutSa,
|
||||
this.isOutPatient,
|
||||
this.nearestProjectId,
|
||||
this.longitude,
|
||||
this.latitude,
|
||||
this.additionalDetails,
|
||||
this.nationality,
|
||||
this.paymentAmount,
|
||||
this.procedures});
|
||||
|
||||
RRTRequestModel.fromJson(Map<String, dynamic> json) {
|
||||
patientId = json['patientId'];
|
||||
patientOutSa = json['patientOutSa'];
|
||||
isOutPatient = json['isOutPatient'];
|
||||
nearestProjectId = json['nearestProjectId'];
|
||||
longitude = json['longitude'];
|
||||
latitude = json['latitude'];
|
||||
additionalDetails = json['additionalDetails'];
|
||||
nationality = json['nationality'];
|
||||
paymentAmount = json['paymentAmount'];
|
||||
if (json['procedures'] != null) {
|
||||
procedures = <Procedures>[];
|
||||
json['procedures'].forEach((v) {
|
||||
procedures!.add(new Procedures.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['patientId'] = this.patientId;
|
||||
data['patientOutSa'] = this.patientOutSa;
|
||||
data['isOutPatient'] = this.isOutPatient;
|
||||
data['nearestProjectId'] = this.nearestProjectId;
|
||||
data['longitude'] = this.longitude;
|
||||
data['latitude'] = this.latitude;
|
||||
data['additionalDetails'] = this.additionalDetails;
|
||||
data['nationality'] = this.nationality;
|
||||
data['paymentAmount'] = this.paymentAmount;
|
||||
if (this.procedures != null) {
|
||||
data['procedures'] = this.procedures!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Procedures {
|
||||
String? serviceID;
|
||||
|
||||
Procedures({this.serviceID});
|
||||
|
||||
Procedures.fromJson(Map<String, dynamic> json) {
|
||||
serviceID = json['ServiceID'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['ServiceID'] = this.serviceID;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
class ServicePrice {
|
||||
String? currency;
|
||||
dynamic maxPrice;
|
||||
dynamic maxTotalPrice;
|
||||
dynamic maxVAT;
|
||||
dynamic minPrice;
|
||||
dynamic minTotalPrice;
|
||||
dynamic minVAT;
|
||||
dynamic price;
|
||||
dynamic totalPrice;
|
||||
dynamic vat;
|
||||
|
||||
ServicePrice({
|
||||
this.currency,
|
||||
this.maxPrice,
|
||||
this.maxTotalPrice,
|
||||
this.maxVAT,
|
||||
this.minPrice,
|
||||
this.minTotalPrice,
|
||||
this.minVAT,
|
||||
this.price,
|
||||
this.totalPrice,
|
||||
this.vat});
|
||||
|
||||
ServicePrice.fromJson(dynamic json) {
|
||||
currency = json["Currency"];
|
||||
maxPrice = json["MaxPrice"];
|
||||
maxTotalPrice = json["MaxTotalPrice"];
|
||||
maxVAT = json["MaxVAT"];
|
||||
minPrice = json["MinPrice"];
|
||||
minTotalPrice = json["MinTotalPrice"];
|
||||
minVAT = json["MinVAT"];
|
||||
price = json["Price"];
|
||||
totalPrice = json["TotalPrice"];
|
||||
vat = json["VAT"];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
var map = <String, dynamic>{};
|
||||
map["Currency"] = currency;
|
||||
map["MaxPrice"] = maxPrice;
|
||||
map["MaxTotalPrice"] = maxTotalPrice;
|
||||
map["MaxVAT"] = maxVAT;
|
||||
map["MinPrice"] = minPrice;
|
||||
map["MinTotalPrice"] = minTotalPrice;
|
||||
map["MinVAT"] = minVAT;
|
||||
map["Price"] = price;
|
||||
map["TotalPrice"] = totalPrice;
|
||||
map["VAT"] = vat;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,406 @@
|
||||
class RRTServiceData {
|
||||
List<GetCMCAllOrdersResponseModel> pendingOrders = [];
|
||||
List<GetCMCAllOrdersResponseModel> completedOrders = [];
|
||||
ServicePrice servicePrice = ServicePrice();
|
||||
}
|
||||
|
||||
class GetCMCAllOrdersResponseModel {
|
||||
int? iD;
|
||||
int? patientId;
|
||||
int? patientOutSa;
|
||||
bool? isOutPatient;
|
||||
int? projectId;
|
||||
int? nearestProjectId;
|
||||
dynamic longitude;
|
||||
dynamic latitude;
|
||||
dynamic appointmentNo;
|
||||
dynamic dischargeId;
|
||||
int? statusId;
|
||||
int? serviceId;
|
||||
int? channel;
|
||||
Orderpayment? orderpayment;
|
||||
dynamic orderselectedservice;
|
||||
dynamic wforder;
|
||||
dynamic orderapprovalobj;
|
||||
String? created;
|
||||
dynamic createdBy;
|
||||
dynamic modified;
|
||||
dynamic modifiedBy;
|
||||
bool? isDeleted;
|
||||
String? statusText;
|
||||
int? paymentStatus;
|
||||
dynamic clientRequestid;
|
||||
dynamic paymentStatusText;
|
||||
String? projectName;
|
||||
String? nearestProjectName;
|
||||
dynamic paymentAmount;
|
||||
WFOrder? wFOrder;
|
||||
String? serviceText;
|
||||
bool? isSentForApproval;
|
||||
int? exaCartOrderId;
|
||||
bool? isTimer;
|
||||
int? timeSeconds;
|
||||
int? totalPendingSeconds;
|
||||
int? timeMinute;
|
||||
int? timeHour;
|
||||
int? timeTotalSeconds;
|
||||
int? timeTotalMinute;
|
||||
int? timeTotalHour;
|
||||
dynamic approvalStatus;
|
||||
bool? isActive;
|
||||
int? clickButton;
|
||||
List<dynamic>? procedures;
|
||||
dynamic pickupLocation;
|
||||
dynamic dropOffLocation;
|
||||
dynamic clinicName;
|
||||
dynamic doctorName;
|
||||
dynamic branch;
|
||||
dynamic time;
|
||||
dynamic notes;
|
||||
|
||||
GetCMCAllOrdersResponseModel(
|
||||
{this.iD,
|
||||
this.patientId,
|
||||
this.patientOutSa,
|
||||
this.isOutPatient,
|
||||
this.projectId,
|
||||
this.nearestProjectId,
|
||||
this.longitude,
|
||||
this.latitude,
|
||||
this.appointmentNo,
|
||||
this.dischargeId,
|
||||
this.statusId,
|
||||
this.serviceId,
|
||||
this.channel,
|
||||
this.orderpayment,
|
||||
this.orderselectedservice,
|
||||
this.wforder,
|
||||
this.orderapprovalobj,
|
||||
this.created,
|
||||
this.createdBy,
|
||||
this.modified,
|
||||
this.modifiedBy,
|
||||
this.isDeleted,
|
||||
this.statusText,
|
||||
this.paymentStatus,
|
||||
this.clientRequestid,
|
||||
this.paymentStatusText,
|
||||
this.projectName,
|
||||
this.nearestProjectName,
|
||||
this.paymentAmount,
|
||||
this.wFOrder,
|
||||
this.serviceText,
|
||||
this.isSentForApproval,
|
||||
this.exaCartOrderId,
|
||||
this.isTimer,
|
||||
this.timeSeconds,
|
||||
this.totalPendingSeconds,
|
||||
this.timeMinute,
|
||||
this.timeHour,
|
||||
this.timeTotalSeconds,
|
||||
this.timeTotalMinute,
|
||||
this.timeTotalHour,
|
||||
this.approvalStatus,
|
||||
this.isActive,
|
||||
this.clickButton,
|
||||
this.procedures,
|
||||
this.pickupLocation,
|
||||
this.dropOffLocation,
|
||||
this.clinicName,
|
||||
this.doctorName,
|
||||
this.branch,
|
||||
this.time,
|
||||
this.notes});
|
||||
|
||||
GetCMCAllOrdersResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
iD = json['ID'];
|
||||
patientId = json['PatientId'];
|
||||
patientOutSa = json['PatientOutSa'];
|
||||
isOutPatient = json['IsOutPatient'];
|
||||
projectId = json['ProjectId'];
|
||||
nearestProjectId = json['NearestProjectId'];
|
||||
longitude = json['Longitude'];
|
||||
latitude = json['Latitude'];
|
||||
appointmentNo = json['AppointmentNo'];
|
||||
dischargeId = json['DischargeId'];
|
||||
statusId = json['StatusId'];
|
||||
serviceId = json['ServiceId'];
|
||||
channel = json['Channel'];
|
||||
orderpayment = json['orderpayment'] != null
|
||||
? new Orderpayment.fromJson(json['orderpayment'])
|
||||
: null;
|
||||
orderselectedservice = json['orderselectedservice'];
|
||||
wforder = json['wforder'];
|
||||
orderapprovalobj = json['orderapprovalobj'];
|
||||
created = json['Created'];
|
||||
createdBy = json['CreatedBy'];
|
||||
modified = json['Modified'];
|
||||
modifiedBy = json['ModifiedBy'];
|
||||
isDeleted = json['IsDeleted'];
|
||||
statusText = json['StatusText'];
|
||||
paymentStatus = json['PaymentStatus'];
|
||||
clientRequestid = json['ClientRequestid'];
|
||||
paymentStatusText = json['PaymentStatusText'];
|
||||
projectName = json['ProjectName'];
|
||||
nearestProjectName = json['NearestProjectName'];
|
||||
paymentAmount = json['PaymentAmount'];
|
||||
wFOrder = json['WF_order'] != null
|
||||
? new WFOrder.fromJson(json['WF_order'])
|
||||
: null;
|
||||
serviceText = json['ServiceText'];
|
||||
isSentForApproval = json['isSentForApproval'];
|
||||
exaCartOrderId = json['ExaCart_OrderId'];
|
||||
isTimer = json['isTimer'];
|
||||
timeSeconds = json['TimeSeconds'];
|
||||
totalPendingSeconds = json['TotalPendingSeconds'];
|
||||
timeMinute = json['TimeMinute'];
|
||||
timeHour = json['TimeHour'];
|
||||
timeTotalSeconds = json['TimeTotalSeconds'];
|
||||
timeTotalMinute = json['TimeTotalMinute'];
|
||||
timeTotalHour = json['TimeTotalHour'];
|
||||
approvalStatus = json['ApprovalStatus'];
|
||||
isActive = json['isActive'];
|
||||
clickButton = json['ClickButton'];
|
||||
pickupLocation = json['PickupLocation'];
|
||||
dropOffLocation = json['DropOffLocation'];
|
||||
clinicName = json['clinicName'];
|
||||
doctorName = json['DoctorName'];
|
||||
branch = json['Branch'];
|
||||
time = json['Time'];
|
||||
notes = json['Notes'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['ID'] = this.iD;
|
||||
data['PatientId'] = this.patientId;
|
||||
data['PatientOutSa'] = this.patientOutSa;
|
||||
data['IsOutPatient'] = this.isOutPatient;
|
||||
data['ProjectId'] = this.projectId;
|
||||
data['NearestProjectId'] = this.nearestProjectId;
|
||||
data['Longitude'] = this.longitude;
|
||||
data['Latitude'] = this.latitude;
|
||||
data['AppointmentNo'] = this.appointmentNo;
|
||||
data['DischargeId'] = this.dischargeId;
|
||||
data['StatusId'] = this.statusId;
|
||||
data['ServiceId'] = this.serviceId;
|
||||
data['Channel'] = this.channel;
|
||||
if (this.orderpayment != null) {
|
||||
data['orderpayment'] = this.orderpayment!.toJson();
|
||||
}
|
||||
data['orderselectedservice'] = this.orderselectedservice;
|
||||
|
||||
data['wforder'] = this.wforder;
|
||||
data['orderapprovalobj'] = this.orderapprovalobj;
|
||||
data['Created'] = this.created;
|
||||
data['CreatedBy'] = this.createdBy;
|
||||
data['Modified'] = this.modified;
|
||||
data['ModifiedBy'] = this.modifiedBy;
|
||||
data['IsDeleted'] = this.isDeleted;
|
||||
data['StatusText'] = this.statusText;
|
||||
data['PaymentStatus'] = this.paymentStatus;
|
||||
data['ClientRequestid'] = this.clientRequestid;
|
||||
data['PaymentStatusText'] = this.paymentStatusText;
|
||||
data['ProjectName'] = this.projectName;
|
||||
data['NearestProjectName'] = this.nearestProjectName;
|
||||
data['PaymentAmount'] = this.paymentAmount;
|
||||
if (this.wFOrder != null) {
|
||||
data['WF_order'] = this.wFOrder!.toJson();
|
||||
}
|
||||
data['ServiceText'] = this.serviceText;
|
||||
data['isSentForApproval'] = this.isSentForApproval;
|
||||
data['ExaCart_OrderId'] = this.exaCartOrderId;
|
||||
data['isTimer'] = this.isTimer;
|
||||
data['TimeSeconds'] = this.timeSeconds;
|
||||
data['TotalPendingSeconds'] = this.totalPendingSeconds;
|
||||
data['TimeMinute'] = this.timeMinute;
|
||||
data['TimeHour'] = this.timeHour;
|
||||
data['TimeTotalSeconds'] = this.timeTotalSeconds;
|
||||
data['TimeTotalMinute'] = this.timeTotalMinute;
|
||||
data['TimeTotalHour'] = this.timeTotalHour;
|
||||
data['ApprovalStatus'] = this.approvalStatus;
|
||||
data['isActive'] = this.isActive;
|
||||
data['ClickButton'] = this.clickButton;
|
||||
data['PickupLocation'] = this.pickupLocation;
|
||||
data['DropOffLocation'] = this.dropOffLocation;
|
||||
data['clinicName'] = this.clinicName;
|
||||
data['DoctorName'] = this.doctorName;
|
||||
data['Branch'] = this.branch;
|
||||
data['Time'] = this.time;
|
||||
data['Notes'] = this.notes;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Orderpayment {
|
||||
int? iD;
|
||||
int? orderId;
|
||||
dynamic clientRequestId;
|
||||
dynamic totalAmount;
|
||||
int? paymentStatus;
|
||||
dynamic order;
|
||||
String? created;
|
||||
dynamic createdBy;
|
||||
dynamic modified;
|
||||
dynamic modifiedBy;
|
||||
bool? isDeleted;
|
||||
|
||||
Orderpayment(
|
||||
{this.iD,
|
||||
this.orderId,
|
||||
this.clientRequestId,
|
||||
this.totalAmount,
|
||||
this.paymentStatus,
|
||||
this.order,
|
||||
this.created,
|
||||
this.createdBy,
|
||||
this.modified,
|
||||
this.modifiedBy,
|
||||
this.isDeleted});
|
||||
|
||||
Orderpayment.fromJson(Map<String, dynamic> json) {
|
||||
iD = json['ID'];
|
||||
orderId = json['OrderId'];
|
||||
clientRequestId = json['ClientRequestId'];
|
||||
totalAmount = json['TotalAmount'];
|
||||
paymentStatus = json['PaymentStatus'];
|
||||
order = json['Order'];
|
||||
created = json['Created'];
|
||||
createdBy = json['CreatedBy'];
|
||||
modified = json['Modified'];
|
||||
modifiedBy = json['ModifiedBy'];
|
||||
isDeleted = json['IsDeleted'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['ID'] = this.iD;
|
||||
data['OrderId'] = this.orderId;
|
||||
data['ClientRequestId'] = this.clientRequestId;
|
||||
data['TotalAmount'] = this.totalAmount;
|
||||
data['PaymentStatus'] = this.paymentStatus;
|
||||
data['Order'] = this.order;
|
||||
data['Created'] = this.created;
|
||||
data['CreatedBy'] = this.createdBy;
|
||||
data['Modified'] = this.modified;
|
||||
data['ModifiedBy'] = this.modifiedBy;
|
||||
data['IsDeleted'] = this.isDeleted;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class WFOrder {
|
||||
dynamic wfButtonsDTO;
|
||||
int? iD;
|
||||
int? orderId;
|
||||
int? previousStep;
|
||||
int? nextStep;
|
||||
int? serviceId;
|
||||
dynamic order;
|
||||
String? created;
|
||||
dynamic createdBy;
|
||||
dynamic modified;
|
||||
dynamic modifiedBy;
|
||||
bool? isDeleted;
|
||||
|
||||
WFOrder(
|
||||
{this.wfButtonsDTO,
|
||||
this.iD,
|
||||
this.orderId,
|
||||
this.previousStep,
|
||||
this.nextStep,
|
||||
this.serviceId,
|
||||
this.order,
|
||||
this.created,
|
||||
this.createdBy,
|
||||
this.modified,
|
||||
this.modifiedBy,
|
||||
this.isDeleted});
|
||||
|
||||
WFOrder.fromJson(Map<String, dynamic> json) {
|
||||
wfButtonsDTO = json['wf_ButtonsDTO'];
|
||||
iD = json['ID'];
|
||||
orderId = json['OrderId'];
|
||||
previousStep = json['PreviousStep'];
|
||||
nextStep = json['NextStep'];
|
||||
serviceId = json['ServiceId'];
|
||||
order = json['Order'];
|
||||
created = json['Created'];
|
||||
createdBy = json['CreatedBy'];
|
||||
modified = json['Modified'];
|
||||
modifiedBy = json['ModifiedBy'];
|
||||
isDeleted = json['IsDeleted'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['wf_ButtonsDTO'] = this.wfButtonsDTO;
|
||||
data['ID'] = this.iD;
|
||||
data['OrderId'] = this.orderId;
|
||||
data['PreviousStep'] = this.previousStep;
|
||||
data['NextStep'] = this.nextStep;
|
||||
data['ServiceId'] = this.serviceId;
|
||||
data['Order'] = this.order;
|
||||
data['Created'] = this.created;
|
||||
data['CreatedBy'] = this.createdBy;
|
||||
data['Modified'] = this.modified;
|
||||
data['ModifiedBy'] = this.modifiedBy;
|
||||
data['IsDeleted'] = this.isDeleted;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ServicePrice {
|
||||
String? currency;
|
||||
dynamic maxPrice;
|
||||
dynamic maxTotalPrice;
|
||||
dynamic maxVAT;
|
||||
dynamic minPrice;
|
||||
dynamic minTotalPrice;
|
||||
dynamic minVAT;
|
||||
dynamic price;
|
||||
dynamic totalPrice;
|
||||
dynamic vat;
|
||||
|
||||
ServicePrice({
|
||||
this.currency,
|
||||
this.maxPrice,
|
||||
this.maxTotalPrice,
|
||||
this.maxVAT,
|
||||
this.minPrice,
|
||||
this.minTotalPrice,
|
||||
this.minVAT,
|
||||
this.price,
|
||||
this.totalPrice,
|
||||
this.vat});
|
||||
|
||||
ServicePrice.fromJson(dynamic json) {
|
||||
currency = json["Currency"];
|
||||
maxPrice = json["MaxPrice"];
|
||||
maxTotalPrice = json["MaxTotalPrice"];
|
||||
maxVAT = json["MaxVAT"];
|
||||
minPrice = json["MinPrice"];
|
||||
minTotalPrice = json["MinTotalPrice"];
|
||||
minVAT = json["MinVAT"];
|
||||
price = json["Price"];
|
||||
totalPrice = json["TotalPrice"];
|
||||
vat = json["VAT"];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
var map = <String, dynamic>{};
|
||||
map["Currency"] = currency;
|
||||
map["MaxPrice"] = maxPrice;
|
||||
map["MaxTotalPrice"] = maxTotalPrice;
|
||||
map["MaxVAT"] = maxVAT;
|
||||
map["MinPrice"] = minPrice;
|
||||
map["MinTotalPrice"] = minTotalPrice;
|
||||
map["MinVAT"] = minVAT;
|
||||
map["Price"] = price;
|
||||
map["TotalPrice"] = totalPrice;
|
||||
map["VAT"] = vat;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,522 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:hmg_patient_app_new/core/api/api_client.dart';
|
||||
import 'package:hmg_patient_app_new/core/api_consts.dart';
|
||||
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
|
||||
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
|
||||
import 'package:hmg_patient_app_new/services/logger_service.dart';
|
||||
|
||||
abstract class HmgServicesRepo {
|
||||
Future<Either<Failure, GenericApiModel<List<GetCMCAllOrdersResponseModel>>>> getAllComprehensiveCheckupOrders();
|
||||
|
||||
Future<Either<Failure, GenericApiModel<List<GetCMCAllOrdersResponseModel>>>> getAllHomeHealthCareCheckupOrders();
|
||||
|
||||
Future<Either<Failure, GenericApiModel<bool>>> updateCmcPresOrder(OrderUpdateRequestModel requestModel);
|
||||
|
||||
Future<Either<Failure, GenericApiModel<bool>>> updateHhcPresOrder(OrderUpdateRequestModel requestModel);
|
||||
|
||||
Future<Either<Failure, GenericApiModel<List<GetCMCServicesResponseModel>>>> getAllCmcServices({required int patientID});
|
||||
|
||||
Future<Either<Failure, GenericApiModel<List<GetCMCServicesResponseModel>>>> getAllHhcServices({required int patientID});
|
||||
|
||||
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getHospitalsList();
|
||||
|
||||
Future<Either<Failure, GenericApiModel<int>>> addCmcOrder({
|
||||
required int projectID,
|
||||
required int orderServiceID,
|
||||
required List<PatientERCMCInsertServicesList> services,
|
||||
});
|
||||
|
||||
Future<Either<Failure, GenericApiModel<int>>> addHhcOrder({
|
||||
required int projectID,
|
||||
required int orderServiceID,
|
||||
required List<PatientERCMCInsertServicesList> services,
|
||||
});
|
||||
}
|
||||
|
||||
class HmgServicesRepoImp implements HmgServicesRepo {
|
||||
final ApiClient apiClient;
|
||||
final LoggerService loggerService;
|
||||
|
||||
HmgServicesRepoImp({required this.apiClient, required this.loggerService});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<List<GetCMCAllOrdersResponseModel>>>> getAllComprehensiveCheckupOrders() async {
|
||||
Map<String, dynamic> requestBody = {};
|
||||
|
||||
try {
|
||||
GenericApiModel<List<GetCMCAllOrdersResponseModel>>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.allCMCOrdersRc,
|
||||
isRCService: true,
|
||||
body: requestBody,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
loggerService.logError("CMC Orders API Failed: $error, Status: $statusCode");
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
List<GetCMCAllOrdersResponseModel> cmcOrdersList = [];
|
||||
// Log the full response for debugging
|
||||
// Extract MessageStatus and ErrorEndUserMessage from root level
|
||||
final apiErrorMessage = response['ErrorEndUserMessage'] as String?;
|
||||
// Parse the response array
|
||||
if (response['response'] != null && response['response'] is List) {
|
||||
final ordersList = response['response'] as List;
|
||||
|
||||
for (var orderJson in ordersList) {
|
||||
if (orderJson is Map<String, dynamic>) {
|
||||
try {
|
||||
cmcOrdersList.add(GetCMCAllOrdersResponseModel.fromJson(orderJson));
|
||||
} catch (e) {
|
||||
loggerService.logError("Error parsing individual order: ${e.toString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apiResponse = GenericApiModel<List<GetCMCAllOrdersResponseModel>>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: apiErrorMessage ?? errorMessage,
|
||||
data: cmcOrdersList,
|
||||
);
|
||||
} catch (e) {
|
||||
loggerService.logError("Error parsing CMC orders: ${e.toString()}");
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
loggerService.logError("Unknown error in getAllCmcOrders: ${e.toString()}");
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<List<GetCMCAllOrdersResponseModel>>>> getAllHomeHealthCareCheckupOrders() async {
|
||||
Map<String, dynamic> requestBody = {};
|
||||
|
||||
try {
|
||||
GenericApiModel<List<GetCMCAllOrdersResponseModel>>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.allHHCOrdersRc,
|
||||
isRCService: true,
|
||||
body: requestBody,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
loggerService.logError("HHC Orders API Failed: $error, Status: $statusCode");
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
List<GetCMCAllOrdersResponseModel> cmcOrdersList = [];
|
||||
// Log the full response for debugging
|
||||
// Extract MessageStatus and ErrorEndUserMessage from root level
|
||||
final apiErrorMessage = response['ErrorEndUserMessage'] as String?;
|
||||
// Parse the response array
|
||||
if (response['response'] != null && response['response'] is List) {
|
||||
final ordersList = response['response'] as List;
|
||||
|
||||
for (var orderJson in ordersList) {
|
||||
if (orderJson is Map<String, dynamic>) {
|
||||
try {
|
||||
cmcOrdersList.add(GetCMCAllOrdersResponseModel.fromJson(orderJson));
|
||||
} catch (e) {
|
||||
loggerService.logError("Error parsing individual order: ${e.toString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apiResponse = GenericApiModel<List<GetCMCAllOrdersResponseModel>>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: apiErrorMessage ?? errorMessage,
|
||||
data: cmcOrdersList,
|
||||
);
|
||||
} catch (e) {
|
||||
loggerService.logError("Error parsing HHC orders: ${e.toString()}");
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
loggerService.logError("Unknown error in getAllHHCOrders: ${e.toString()}");
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<List<GetCMCServicesResponseModel>>>> getAllCmcServices({required int patientID}) async {
|
||||
Map<String, dynamic> requestBody = {};
|
||||
|
||||
try {
|
||||
GenericApiModel<List<GetCMCServicesResponseModel>>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
'${ApiConsts.allCMCServicesRc}?patientID=$patientID',
|
||||
isRCService: true,
|
||||
isAllowAny: true,
|
||||
body: requestBody,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
loggerService.logError("CMC Services API Failed: $error, Status: $statusCode");
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
List<GetCMCServicesResponseModel> cmcServicesList = [];
|
||||
|
||||
if (response['response'] != null && response['response'] is List) {
|
||||
final servicesList = response['response'] as List;
|
||||
|
||||
for (var serviceJson in servicesList) {
|
||||
if (serviceJson is Map<String, dynamic>) {
|
||||
cmcServicesList.add(GetCMCServicesResponseModel.fromJson(serviceJson));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apiResponse = GenericApiModel<List<GetCMCServicesResponseModel>>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: cmcServicesList,
|
||||
);
|
||||
} catch (e) {
|
||||
loggerService.logError("Error parsing CMC services: ${e.toString()}");
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
log("Unknown error in getAllCmcServices: ${e.toString()}");
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<List<GetCMCServicesResponseModel>>>> getAllHhcServices({required int patientID}) async {
|
||||
Map<String, dynamic> requestBody = {};
|
||||
|
||||
try {
|
||||
GenericApiModel<List<GetCMCServicesResponseModel>>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
'${ApiConsts.allHHCServicesRc}?patientID=$patientID',
|
||||
isRCService: true,
|
||||
isAllowAny: true,
|
||||
body: requestBody,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
loggerService.logError("HHC Services API Failed: $error, Status: $statusCode");
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
List<GetCMCServicesResponseModel> hhcServicesList = [];
|
||||
|
||||
if (response['response'] != null && response['response'] is List) {
|
||||
final servicesList = response['response'] as List;
|
||||
|
||||
for (var serviceJson in servicesList) {
|
||||
if (serviceJson is Map<String, dynamic>) {
|
||||
hhcServicesList.add(GetCMCServicesResponseModel.fromJson(serviceJson));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apiResponse = GenericApiModel<List<GetCMCServicesResponseModel>>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: hhcServicesList,
|
||||
);
|
||||
} catch (e) {
|
||||
loggerService.logError("Error parsing HHC services: ${e.toString()}");
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
log("Unknown error in getAllHhcServices: ${e.toString()}");
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<bool>>> updateCmcPresOrder(OrderUpdateRequestModel requestModel) async {
|
||||
try {
|
||||
GenericApiModel<bool>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.updateCMCOrder,
|
||||
isRCService: true,
|
||||
body: requestModel.toJson(),
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
loggerService.logError("Update CMC Order API Failed: $error, Status: $statusCode");
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
apiResponse = GenericApiModel<bool>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: true,
|
||||
);
|
||||
|
||||
loggerService.logInfo("CMC Order updated successfully: PresOrderID=${requestModel.presOrderID}");
|
||||
} catch (e) {
|
||||
loggerService.logError("Error processing update CMC order response: ${e.toString()}");
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
loggerService.logError("Unknown error in updateCmcPresOrder: ${e.toString()}");
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<bool>>> updateHhcPresOrder(OrderUpdateRequestModel requestModel) async {
|
||||
try {
|
||||
GenericApiModel<bool>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.updateHHCOrder,
|
||||
isRCService: true,
|
||||
body: requestModel.toJson(),
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
loggerService.logError("Update HHC Order API Failed: $error, Status: $statusCode");
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
apiResponse = GenericApiModel<bool>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: true,
|
||||
);
|
||||
|
||||
loggerService.logInfo("HHC Order updated successfully: PresOrderID=${requestModel.presOrderID}");
|
||||
} catch (e) {
|
||||
loggerService.logError("Error processing update HHC order response: ${e.toString()}");
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
loggerService.logError("Unknown error in updateHhcPresOrder: ${e.toString()}");
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>> getHospitalsList() async {
|
||||
Map<String, dynamic> requestBody = {};
|
||||
|
||||
try {
|
||||
GenericApiModel<List<HospitalsModel>>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.getHospitalsList,
|
||||
isRCService: false, // This uses the base HIS API URL, not RC
|
||||
body: requestBody,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
loggerService.logError("Get Hospitals List API Failed: $error, Status: $statusCode");
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
try {
|
||||
List<HospitalsModel> hospitalsList = [];
|
||||
|
||||
loggerService.logInfo("Hospitals List Raw Response: $response");
|
||||
|
||||
if (response['ListProject'] != null && response['ListProject'] is List) {
|
||||
final projectsList = response['ListProject'] as List;
|
||||
|
||||
for (var projectJson in projectsList) {
|
||||
try {
|
||||
if (projectJson is Map<String, dynamic>) {
|
||||
hospitalsList.add(HospitalsModel.fromJson(projectJson));
|
||||
}
|
||||
} catch (e) {
|
||||
loggerService.logError('Error parsing hospital item: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loggerService.logInfo('Hospitals list response array is empty or missing');
|
||||
}
|
||||
|
||||
apiResponse = GenericApiModel<List<HospitalsModel>>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: hospitalsList,
|
||||
);
|
||||
|
||||
loggerService.logInfo("Hospitals fetched successfully: ${hospitalsList.length} hospitals");
|
||||
} catch (e) {
|
||||
loggerService.logError("Error parsing hospitals list: ${e.toString()}");
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
loggerService.logError("Unknown error in getHospitalsList: ${e.toString()}");
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<int>>> addCmcOrder({
|
||||
required int projectID,
|
||||
required int orderServiceID,
|
||||
required List<PatientERCMCInsertServicesList> services,
|
||||
}) async {
|
||||
try {
|
||||
GenericApiModel<int>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
final requestBody = {
|
||||
'ProjectID': projectID,
|
||||
'OrderServiceID': orderServiceID,
|
||||
'procedures': services.map((service) => service.toJson()).toList(),
|
||||
};
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.addCMCOrder,
|
||||
isRCService: true,
|
||||
body: requestBody,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
loggerService.logError("Add CMC Order API Failed: $error, Status: $statusCode");
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
int requestId = 0;
|
||||
if (response is Map) {
|
||||
requestId = response['response'];
|
||||
}
|
||||
try {
|
||||
apiResponse = GenericApiModel<int>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: requestId,
|
||||
);
|
||||
|
||||
loggerService.logInfo("CMC Order added successfully: ProjectID=$projectID, OrderServiceID=$orderServiceID");
|
||||
} catch (e) {
|
||||
loggerService.logError("Error processing add CMC order response: ${e.toString()}");
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
loggerService.logError("Unknown error in addCmcOrder: ${e.toString()}");
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, GenericApiModel<int>>> addHhcOrder({
|
||||
required int projectID,
|
||||
required int orderServiceID,
|
||||
required List<PatientERCMCInsertServicesList> services,
|
||||
}) async {
|
||||
try {
|
||||
GenericApiModel<int>? apiResponse;
|
||||
Failure? failure;
|
||||
|
||||
final requestBody = {
|
||||
'ProjectID': projectID,
|
||||
'OrderServiceID': orderServiceID,
|
||||
'procedures': services.map((service) => service.toJson()).toList(),
|
||||
};
|
||||
|
||||
await apiClient.post(
|
||||
ApiConsts.addHHCOrder,
|
||||
isRCService: true,
|
||||
body: requestBody,
|
||||
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||
failure = failureType;
|
||||
loggerService.logError("Add HHC Order API Failed: $error, Status: $statusCode");
|
||||
},
|
||||
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||
int requestId = 0;
|
||||
if (response is Map) {
|
||||
requestId = response['response'];
|
||||
}
|
||||
try {
|
||||
apiResponse = GenericApiModel<int>(
|
||||
messageStatus: messageStatus,
|
||||
statusCode: statusCode,
|
||||
errorMessage: errorMessage,
|
||||
data: requestId,
|
||||
);
|
||||
|
||||
loggerService.logInfo("HHC Order added successfully: ProjectID=$projectID, OrderServiceID=$orderServiceID");
|
||||
} catch (e) {
|
||||
loggerService.logError("Error processing add HHC order response: ${e.toString()}");
|
||||
failure = DataParsingFailure(e.toString());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (failure != null) return Left(failure!);
|
||||
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||
return Right(apiResponse!);
|
||||
} catch (e) {
|
||||
loggerService.logError("Unknown error in addHhcOrder: ${e.toString()}");
|
||||
return Left(UnknownFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,515 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
|
||||
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
|
||||
|
||||
class HmgServicesViewModel extends ChangeNotifier {
|
||||
final HmgServicesRepo hmgServicesRepo;
|
||||
final BookAppointmentsRepo bookAppointmentsRepo;
|
||||
final ErrorHandlerService errorHandlerService;
|
||||
|
||||
HmgServicesViewModel({required this.bookAppointmentsRepo, required this.hmgServicesRepo, required this.errorHandlerService});
|
||||
|
||||
bool isCmcOrdersLoading = false;
|
||||
bool isCmcServicesLoading = false;
|
||||
bool isUpdatingOrder = false;
|
||||
bool isHospitalListLoading = false;
|
||||
|
||||
// HHC specific loading states
|
||||
bool isHhcOrdersLoading = false;
|
||||
bool isHhcServicesLoading = false;
|
||||
|
||||
List<GetCMCAllOrdersResponseModel> cmcOrdersList = [];
|
||||
List<GetCMCServicesResponseModel> cmcServicesList = [];
|
||||
List<HospitalsModel> hospitalsList = [];
|
||||
List<HospitalsModel> filteredHospitalsList = [];
|
||||
HospitalsModel? selectedHospital;
|
||||
|
||||
// HHC specific lists
|
||||
List<GetCMCAllOrdersResponseModel> hhcOrdersList = [];
|
||||
List<GetCMCServicesResponseModel> hhcServicesList = [];
|
||||
|
||||
// CMC order creation state
|
||||
HospitalsModel? selectedHospitalForOrder;
|
||||
GetCMCServicesResponseModel? selectedServiceForOrder;
|
||||
|
||||
// HHC order creation state (no hospital selection needed for home healthcare)
|
||||
GetCMCServicesResponseModel? selectedServiceForHhcOrder;
|
||||
|
||||
// HHC multiple services selection
|
||||
List<GetCMCServicesResponseModel> selectedHhcServices = [];
|
||||
|
||||
Future<void> getCmcOrdersList() async {
|
||||
cmcOrdersList.clear();
|
||||
isCmcOrdersLoading = true;
|
||||
notifyListeners();
|
||||
await getAllCmcOrders();
|
||||
}
|
||||
|
||||
// Helper to sort hospitals by distance (ascending). Safely converts distanceinkMS to double.
|
||||
void _sortHospitalsByDistance(List<HospitalsModel> list) {
|
||||
double toDouble(dynamic v) {
|
||||
if (v == null) return double.infinity;
|
||||
if (v is num) return v.toDouble();
|
||||
if (v is String) return double.tryParse(v) ?? double.infinity;
|
||||
return double.infinity;
|
||||
}
|
||||
|
||||
list.sort((a, b) {
|
||||
final da = toDouble(a.distanceInKilometers);
|
||||
final db = toDouble(b.distanceInKilometers);
|
||||
return da.compareTo(db);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> getAllCmcOrders({
|
||||
Function(dynamic)? onSuccess,
|
||||
Function(String)? onError,
|
||||
}) async {
|
||||
isCmcOrdersLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await hmgServicesRepo.getAllComprehensiveCheckupOrders();
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
isCmcOrdersLoading = false;
|
||||
notifyListeners();
|
||||
await errorHandlerService.handleError(failure: failure);
|
||||
if (onError != null) {
|
||||
onError(failure.toString());
|
||||
}
|
||||
},
|
||||
(apiResponse) {
|
||||
isCmcOrdersLoading = false;
|
||||
if (apiResponse.messageStatus == 1) {
|
||||
cmcOrdersList = apiResponse.data ?? [];
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
} else {
|
||||
notifyListeners();
|
||||
if (onError != null) {
|
||||
onError(apiResponse.errorMessage ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getAllCmcServices({required int patientID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
|
||||
isCmcServicesLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await hmgServicesRepo.getAllCmcServices(patientID: patientID);
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
isCmcServicesLoading = false;
|
||||
notifyListeners();
|
||||
await errorHandlerService.handleError(failure: failure);
|
||||
if (onError != null) {
|
||||
onError(failure.toString());
|
||||
}
|
||||
},
|
||||
(apiResponse) {
|
||||
isCmcServicesLoading = false;
|
||||
if (apiResponse.messageStatus == 1) {
|
||||
cmcServicesList = apiResponse.data ?? [];
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
} else {
|
||||
notifyListeners();
|
||||
if (onError != null) {
|
||||
onError(apiResponse.errorMessage ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> updateCmcPresOrder({
|
||||
required OrderUpdateRequestModel requestModel,
|
||||
Function(dynamic)? onSuccess,
|
||||
Function(String)? onError,
|
||||
}) async {
|
||||
isUpdatingOrder = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await hmgServicesRepo.updateCmcPresOrder(requestModel);
|
||||
|
||||
bool success = false;
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
isUpdatingOrder = false;
|
||||
notifyListeners();
|
||||
await errorHandlerService.handleError(failure: failure);
|
||||
if (onError != null) {
|
||||
onError(failure.toString());
|
||||
}
|
||||
},
|
||||
(apiResponse) {
|
||||
isUpdatingOrder = false;
|
||||
if (apiResponse.messageStatus == 1) {
|
||||
success = true;
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
} else {
|
||||
notifyListeners();
|
||||
if (onError != null) {
|
||||
onError(apiResponse.errorMessage ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
Future<void> getHospitalsList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
|
||||
isHospitalListLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await hmgServicesRepo.getHospitalsList();
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
isHospitalListLoading = false;
|
||||
notifyListeners();
|
||||
await errorHandlerService.handleError(failure: failure);
|
||||
if (onError != null) {
|
||||
onError(failure.toString());
|
||||
}
|
||||
},
|
||||
(apiResponse) {
|
||||
isHospitalListLoading = false;
|
||||
if (apiResponse.messageStatus == 1) {
|
||||
hospitalsList = apiResponse.data ?? [];
|
||||
filteredHospitalsList = List.from(hospitalsList);
|
||||
// ensure hospitals are sorted by distance before showing
|
||||
_sortHospitalsByDistance(filteredHospitalsList);
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
} else {
|
||||
notifyListeners();
|
||||
if (onError != null) {
|
||||
onError(apiResponse.errorMessage ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void filterHospitalsByString(String searchText, bool isArabic) {
|
||||
if (searchText.isEmpty) {
|
||||
filteredHospitalsList = List.from(hospitalsList);
|
||||
_sortHospitalsByDistance(filteredHospitalsList);
|
||||
} else {
|
||||
filteredHospitalsList = hospitalsList.where((HospitalsModel hospital) {
|
||||
final name = isArabic ? (hospital.nameN ?? '') : (hospital.name ?? '');
|
||||
return name.toLowerCase().contains(searchText.toLowerCase());
|
||||
}).toList();
|
||||
_sortHospitalsByDistance(filteredHospitalsList);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setSelectedHospital(HospitalsModel? hospital) {
|
||||
selectedHospital = hospital;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void clearHospitalSelection() {
|
||||
selectedHospital = null;
|
||||
filteredHospitalsList = List.from(hospitalsList);
|
||||
_sortHospitalsByDistance(filteredHospitalsList);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// CMC Order management methods
|
||||
void setSelectedHospitalForOrder(HospitalsModel? hospital) {
|
||||
selectedHospitalForOrder = hospital;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setSelectedServiceForOrder(GetCMCServicesResponseModel? service) {
|
||||
selectedServiceForOrder = service;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void clearOrderSelection() {
|
||||
selectedHospitalForOrder = null;
|
||||
selectedServiceForOrder = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get isOrderReadyToConfirm => selectedHospitalForOrder != null && selectedServiceForOrder != null;
|
||||
|
||||
Future<int?> addCmcOrder({
|
||||
required int projectID,
|
||||
required int orderServiceID,
|
||||
required List<PatientERCMCInsertServicesList> services,
|
||||
Function(int)? onSuccess,
|
||||
Function(String)? onError,
|
||||
}) async {
|
||||
isUpdatingOrder = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await hmgServicesRepo.addCmcOrder(
|
||||
projectID: projectID,
|
||||
orderServiceID: orderServiceID,
|
||||
services: services,
|
||||
);
|
||||
|
||||
int requestId = 0;
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
isUpdatingOrder = false;
|
||||
notifyListeners();
|
||||
await errorHandlerService.handleError(failure: failure);
|
||||
if (onError != null) {
|
||||
onError(failure.toString());
|
||||
}
|
||||
},
|
||||
(apiResponse) {
|
||||
isUpdatingOrder = false;
|
||||
if (apiResponse.messageStatus == 1) {
|
||||
requestId = apiResponse.data ?? 0;
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(requestId);
|
||||
}
|
||||
} else {
|
||||
notifyListeners();
|
||||
if (onError != null) {
|
||||
onError(apiResponse.errorMessage ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return requestId;
|
||||
}
|
||||
|
||||
// ******************* HOME HEALTHCARE APIs ********************
|
||||
|
||||
Future<void> getHhcOrdersList() async {
|
||||
hhcOrdersList.clear();
|
||||
isHhcOrdersLoading = true;
|
||||
notifyListeners();
|
||||
await getAllHhcOrders();
|
||||
}
|
||||
|
||||
Future<void> getAllHhcOrders({
|
||||
Function(dynamic)? onSuccess,
|
||||
Function(String)? onError,
|
||||
}) async {
|
||||
isHhcOrdersLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await hmgServicesRepo.getAllHomeHealthCareCheckupOrders();
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
isHhcOrdersLoading = false;
|
||||
notifyListeners();
|
||||
await errorHandlerService.handleError(failure: failure);
|
||||
if (onError != null) {
|
||||
onError(failure.toString());
|
||||
}
|
||||
},
|
||||
(apiResponse) {
|
||||
isHhcOrdersLoading = false;
|
||||
if (apiResponse.messageStatus == 1) {
|
||||
hhcOrdersList = apiResponse.data ?? [];
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
} else {
|
||||
notifyListeners();
|
||||
if (onError != null) {
|
||||
onError(apiResponse.errorMessage ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getAllHhcServices({required int patientID, Function(dynamic)? onSuccess, Function(String)? onError}) async {
|
||||
isHhcServicesLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await hmgServicesRepo.getAllHhcServices(patientID: patientID);
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
isHhcServicesLoading = false;
|
||||
notifyListeners();
|
||||
await errorHandlerService.handleError(failure: failure);
|
||||
if (onError != null) {
|
||||
onError(failure.toString());
|
||||
}
|
||||
},
|
||||
(apiResponse) {
|
||||
isHhcServicesLoading = false;
|
||||
if (apiResponse.messageStatus == 1) {
|
||||
hhcServicesList = apiResponse.data ?? [];
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
} else {
|
||||
notifyListeners();
|
||||
if (onError != null) {
|
||||
onError(apiResponse.errorMessage ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> updateHhcPresOrder({
|
||||
required OrderUpdateRequestModel requestModel,
|
||||
Function(dynamic)? onSuccess,
|
||||
Function(String)? onError,
|
||||
}) async {
|
||||
isUpdatingOrder = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await hmgServicesRepo.updateHhcPresOrder(requestModel);
|
||||
|
||||
bool success = false;
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
isUpdatingOrder = false;
|
||||
notifyListeners();
|
||||
await errorHandlerService.handleError(failure: failure);
|
||||
if (onError != null) {
|
||||
onError(failure.toString());
|
||||
}
|
||||
},
|
||||
(apiResponse) {
|
||||
isUpdatingOrder = false;
|
||||
if (apiResponse.messageStatus == 1) {
|
||||
success = true;
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(apiResponse);
|
||||
}
|
||||
} else {
|
||||
notifyListeners();
|
||||
if (onError != null) {
|
||||
onError(apiResponse.errorMessage ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
Future<int?> addHhcOrder({
|
||||
required int projectID,
|
||||
required int orderServiceID,
|
||||
required List<PatientERCMCInsertServicesList> services,
|
||||
Function(int)? onSuccess,
|
||||
Function(String)? onError,
|
||||
}) async {
|
||||
isUpdatingOrder = true;
|
||||
notifyListeners();
|
||||
|
||||
final result = await hmgServicesRepo.addHhcOrder(
|
||||
projectID: projectID,
|
||||
orderServiceID: orderServiceID,
|
||||
services: services,
|
||||
);
|
||||
|
||||
int requestId = 0;
|
||||
|
||||
result.fold(
|
||||
(failure) async {
|
||||
isUpdatingOrder = false;
|
||||
notifyListeners();
|
||||
await errorHandlerService.handleError(failure: failure);
|
||||
if (onError != null) {
|
||||
onError(failure.toString());
|
||||
}
|
||||
},
|
||||
(apiResponse) {
|
||||
isUpdatingOrder = false;
|
||||
if (apiResponse.messageStatus == 1) {
|
||||
requestId = apiResponse.data ?? 0;
|
||||
notifyListeners();
|
||||
if (onSuccess != null) {
|
||||
onSuccess(requestId);
|
||||
}
|
||||
} else {
|
||||
notifyListeners();
|
||||
if (onError != null) {
|
||||
onError(apiResponse.errorMessage ?? 'Unknown error');
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return requestId;
|
||||
}
|
||||
|
||||
// HHC Order management methods (no hospital selection for home healthcare)
|
||||
void setSelectedServiceForHhcOrder(GetCMCServicesResponseModel? service) {
|
||||
selectedServiceForHhcOrder = service;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void clearHhcOrderSelection() {
|
||||
selectedServiceForHhcOrder = null;
|
||||
selectedHhcServices.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get isHhcOrderReadyToConfirm => selectedServiceForHhcOrder != null;
|
||||
|
||||
// Multiple HHC services selection methods
|
||||
void toggleHhcServiceSelection(GetCMCServicesResponseModel service) {
|
||||
final index = selectedHhcServices.indexWhere((s) => s.iD == service.iD);
|
||||
if (index != -1) {
|
||||
selectedHhcServices.removeAt(index);
|
||||
} else {
|
||||
selectedHhcServices.add(service);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool isHhcServiceSelected(GetCMCServicesResponseModel service) {
|
||||
return selectedHhcServices.any((s) => s.iD == service.iD);
|
||||
}
|
||||
|
||||
double getHhcSelectedServicesTotal() {
|
||||
double total = 0.0;
|
||||
for (var service in selectedHhcServices) {
|
||||
total += (service.priceTotal ?? 0);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
void clearHhcServicesSelection() {
|
||||
selectedHhcServices.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
class CMCInsertPresOrderRequestModel {
|
||||
double? versionID;
|
||||
int? channel;
|
||||
int? languageID;
|
||||
String? iPAdress;
|
||||
String? generalid;
|
||||
int? patientOutSA;
|
||||
String? sessionID;
|
||||
bool? isDentalAllowedBackend;
|
||||
int? deviceTypeID;
|
||||
int? patientID;
|
||||
String? tokenID;
|
||||
int? patientTypeID;
|
||||
int? patientType;
|
||||
double? latitude;
|
||||
double? longitude;
|
||||
int? createdBy;
|
||||
int? orderServiceID;
|
||||
int? projectID;
|
||||
List<PatientERCMCInsertServicesList>? patientERCMCInsertServicesList;
|
||||
|
||||
CMCInsertPresOrderRequestModel(
|
||||
{this.versionID,
|
||||
this.channel,
|
||||
this.languageID,
|
||||
this.iPAdress,
|
||||
this.generalid,
|
||||
this.patientOutSA,
|
||||
this.sessionID,
|
||||
this.isDentalAllowedBackend,
|
||||
this.deviceTypeID,
|
||||
this.patientID,
|
||||
this.tokenID,
|
||||
this.patientTypeID,
|
||||
this.patientType,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
this.createdBy,
|
||||
this.orderServiceID,
|
||||
this.projectID,
|
||||
this.patientERCMCInsertServicesList});
|
||||
|
||||
CMCInsertPresOrderRequestModel.fromJson(Map<String, dynamic> json) {
|
||||
versionID = json['VersionID'];
|
||||
channel = json['Channel'];
|
||||
languageID = json['LanguageID'];
|
||||
iPAdress = json['IPAdress'];
|
||||
generalid = json['generalid'];
|
||||
patientOutSA = json['PatientOutSA'];
|
||||
sessionID = json['SessionID'];
|
||||
isDentalAllowedBackend = json['isDentalAllowedBackend'];
|
||||
deviceTypeID = json['DeviceTypeID'];
|
||||
patientID = json['PatientID'];
|
||||
tokenID = json['TokenID'];
|
||||
patientTypeID = json['PatientTypeID'];
|
||||
patientType = json['PatientType'];
|
||||
latitude = json['Latitude'];
|
||||
longitude = json['Longitude'];
|
||||
createdBy = json['CreatedBy'];
|
||||
orderServiceID = json['OrderServiceID'];
|
||||
projectID = json['ProjectId'];
|
||||
if (json['PatientER_CMC_InsertServicesList'] != null) {
|
||||
patientERCMCInsertServicesList = <PatientERCMCInsertServicesList>[];
|
||||
json['PatientER_CMC_InsertServicesList'].forEach((v) {
|
||||
patientERCMCInsertServicesList!.add(
|
||||
new PatientERCMCInsertServicesList.fromJson(v),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['VersionID'] = this.versionID;
|
||||
data['Channel'] = this.channel;
|
||||
data['LanguageID'] = this.languageID;
|
||||
data['IPAdress'] = this.iPAdress;
|
||||
data['generalid'] = this.generalid;
|
||||
data['isOutPatient'] = this.patientOutSA == 0 ? false : true;
|
||||
data['SessionID'] = this.sessionID;
|
||||
data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
|
||||
data['DeviceTypeID'] = this.deviceTypeID;
|
||||
data['TokenID'] = this.tokenID;
|
||||
data['PatientTypeID'] = this.patientTypeID;
|
||||
data['PatientType'] = this.patientType;
|
||||
data['latitude'] = this.latitude;
|
||||
data['longitude'] = this.longitude;
|
||||
// data['CreatedBy'] = this.createdBy;
|
||||
data['OrderServiceID'] = this.orderServiceID;
|
||||
data['ProjectID'] = this.projectID;
|
||||
if (this.patientERCMCInsertServicesList != null) {
|
||||
data['procedures'] = this.patientERCMCInsertServicesList!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class PatientERCMCInsertServicesList {
|
||||
int? recordID;
|
||||
String? serviceID;
|
||||
String? selectedServiceName;
|
||||
String? selectedServiceNameAR;
|
||||
dynamic price;
|
||||
dynamic vAT;
|
||||
dynamic totalPrice;
|
||||
|
||||
PatientERCMCInsertServicesList(
|
||||
{this.recordID, this.serviceID, this.selectedServiceName, this.selectedServiceNameAR, this.price, this.vAT, this.totalPrice});
|
||||
|
||||
PatientERCMCInsertServicesList.fromJson(Map<String, dynamic> json) {
|
||||
recordID = json['RecordID'];
|
||||
serviceID = json['ServiceID'];
|
||||
selectedServiceName = json['selectedServiceName'];
|
||||
selectedServiceNameAR = json['selectedServiceNameAR'];
|
||||
price = json['Price'];
|
||||
vAT = json['VAT'];
|
||||
totalPrice = json['TotalPrice'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['RecordID'] = this.recordID;
|
||||
data['ServiceID'] = this.serviceID;
|
||||
data['selectedServiceName'] = this.selectedServiceName;
|
||||
data['selectedServiceNameAR'] = this.selectedServiceNameAR;
|
||||
data['Price'] = this.price;
|
||||
data['VAT'] = this.vAT;
|
||||
data['TotalPrice'] = this.totalPrice;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
class CmcCreateServiceOrderReqModel {
|
||||
int? recordID;
|
||||
String? serviceID;
|
||||
String? selectedServiceName;
|
||||
String? selectedServiceNameAR;
|
||||
dynamic price;
|
||||
dynamic vAT;
|
||||
dynamic totalPrice;
|
||||
|
||||
CmcCreateServiceOrderReqModel({
|
||||
this.recordID,
|
||||
this.serviceID,
|
||||
this.selectedServiceName,
|
||||
this.selectedServiceNameAR,
|
||||
this.price,
|
||||
this.vAT,
|
||||
this.totalPrice,
|
||||
});
|
||||
|
||||
CmcCreateServiceOrderReqModel.fromJson(Map<String, dynamic> json) {
|
||||
recordID = json['RecordID'];
|
||||
serviceID = json['ServiceID'];
|
||||
selectedServiceName = json['selectedServiceName'];
|
||||
selectedServiceNameAR = json['selectedServiceNameAR'];
|
||||
price = json['Price'];
|
||||
vAT = json['VAT'];
|
||||
totalPrice = json['TotalPrice'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['RecordID'] = recordID;
|
||||
data['ServiceID'] = serviceID;
|
||||
data['selectedServiceName'] = selectedServiceName;
|
||||
data['selectedServiceNameAR'] = selectedServiceNameAR;
|
||||
data['Price'] = price;
|
||||
data['VAT'] = vAT;
|
||||
data['TotalPrice'] = totalPrice;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
class OrderUpdateRequestModel {
|
||||
double? versionID;
|
||||
int? channel;
|
||||
int? languageID;
|
||||
String? iPAdress;
|
||||
String? generalid;
|
||||
int? patientOutSA;
|
||||
String? sessionID;
|
||||
bool? isDentalAllowedBackend;
|
||||
int? deviceTypeID;
|
||||
int? patientID;
|
||||
String? tokenID;
|
||||
int? patientTypeID;
|
||||
int? patientType;
|
||||
int? presOrderID;
|
||||
int? presOrderStatus;
|
||||
int? editedBy;
|
||||
String? rejectionReason;
|
||||
|
||||
OrderUpdateRequestModel({
|
||||
this.versionID,
|
||||
this.channel,
|
||||
this.languageID,
|
||||
this.iPAdress,
|
||||
this.generalid,
|
||||
this.patientOutSA,
|
||||
this.sessionID,
|
||||
this.isDentalAllowedBackend,
|
||||
this.deviceTypeID,
|
||||
this.patientID,
|
||||
this.tokenID,
|
||||
this.patientTypeID,
|
||||
this.patientType,
|
||||
this.presOrderID,
|
||||
this.presOrderStatus,
|
||||
this.editedBy,
|
||||
this.rejectionReason,
|
||||
});
|
||||
|
||||
OrderUpdateRequestModel.fromJson(Map<String, dynamic> json) {
|
||||
versionID = json['VersionID'];
|
||||
channel = json['Channel'];
|
||||
languageID = json['LanguageID'];
|
||||
iPAdress = json['IPAdress'];
|
||||
generalid = json['generalid'];
|
||||
patientOutSA = json['PatientOutSA'];
|
||||
sessionID = json['SessionID'];
|
||||
isDentalAllowedBackend = json['isDentalAllowedBackend'];
|
||||
deviceTypeID = json['DeviceTypeID'];
|
||||
patientID = json['PatientID'];
|
||||
tokenID = json['TokenID'];
|
||||
patientTypeID = json['PatientTypeID'];
|
||||
patientType = json['PatientType'];
|
||||
presOrderID = json['PresOrderID'];
|
||||
presOrderStatus = json['PresOrderStatus'];
|
||||
editedBy = json['EditedBy'];
|
||||
rejectionReason = json['RejectionReason'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['VersionID'] = versionID;
|
||||
data['Channel'] = channel;
|
||||
data['LanguageID'] = languageID;
|
||||
data['IPAdress'] = iPAdress;
|
||||
data['generalid'] = generalid;
|
||||
data['PatientOutSA'] = patientOutSA;
|
||||
data['SessionID'] = sessionID;
|
||||
data['isDentalAllowedBackend'] = isDentalAllowedBackend;
|
||||
data['DeviceTypeID'] = deviceTypeID;
|
||||
data['PatientID'] = patientID;
|
||||
data['TokenID'] = tokenID;
|
||||
data['PatientTypeID'] = patientTypeID;
|
||||
data['PatientType'] = patientType;
|
||||
data['Id'] = presOrderID;
|
||||
data['ClickButton'] = 14;
|
||||
data['PresOrderStatus'] = presOrderStatus;
|
||||
data['EditedBy'] = editedBy;
|
||||
data['RejectionReason'] = rejectionReason;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,344 @@
|
||||
import 'dart:developer';
|
||||
|
||||
class GetCMCAllOrdersResponseModel {
|
||||
int? iD;
|
||||
int? patientId;
|
||||
int? patientOutSa;
|
||||
bool? isOutPatient;
|
||||
int? projectId;
|
||||
int? nearestProjectId;
|
||||
dynamic longitude;
|
||||
dynamic latitude;
|
||||
dynamic appointmentNo;
|
||||
dynamic dischargeId;
|
||||
int? statusId;
|
||||
int? serviceId;
|
||||
int? channel;
|
||||
Orderpayment? orderpayment;
|
||||
dynamic orderselectedservice;
|
||||
dynamic wforder;
|
||||
dynamic orderapprovalobj;
|
||||
String? created;
|
||||
dynamic createdBy;
|
||||
dynamic modified;
|
||||
dynamic modifiedBy;
|
||||
bool? isDeleted;
|
||||
String? statusText;
|
||||
int? paymentStatus;
|
||||
dynamic clientRequestid;
|
||||
dynamic paymentStatusText;
|
||||
String? projectName;
|
||||
String? nearestProjectName;
|
||||
dynamic paymentAmount;
|
||||
WFOrder? wFOrder;
|
||||
String? serviceText;
|
||||
bool? isSentForApproval;
|
||||
int? exaCartOrderId;
|
||||
bool? isTimer;
|
||||
int? timeSeconds;
|
||||
int? totalPendingSeconds;
|
||||
int? timeMinute;
|
||||
int? timeHour;
|
||||
int? timeTotalSeconds;
|
||||
int? timeTotalMinute;
|
||||
int? timeTotalHour;
|
||||
dynamic approvalStatus;
|
||||
bool? isActive;
|
||||
int? clickButton;
|
||||
List<dynamic>? procedures;
|
||||
dynamic pickupLocation;
|
||||
dynamic dropOffLocation;
|
||||
dynamic clinicName;
|
||||
dynamic doctorName;
|
||||
dynamic branch;
|
||||
dynamic time;
|
||||
dynamic notes;
|
||||
|
||||
GetCMCAllOrdersResponseModel(
|
||||
{this.iD,
|
||||
this.patientId,
|
||||
this.patientOutSa,
|
||||
this.isOutPatient,
|
||||
this.projectId,
|
||||
this.nearestProjectId,
|
||||
this.longitude,
|
||||
this.latitude,
|
||||
this.appointmentNo,
|
||||
this.dischargeId,
|
||||
this.statusId,
|
||||
this.serviceId,
|
||||
this.channel,
|
||||
this.orderpayment,
|
||||
this.orderselectedservice,
|
||||
this.wforder,
|
||||
this.orderapprovalobj,
|
||||
this.created,
|
||||
this.createdBy,
|
||||
this.modified,
|
||||
this.modifiedBy,
|
||||
this.isDeleted,
|
||||
this.statusText,
|
||||
this.paymentStatus,
|
||||
this.clientRequestid,
|
||||
this.paymentStatusText,
|
||||
this.projectName,
|
||||
this.nearestProjectName,
|
||||
this.paymentAmount,
|
||||
this.wFOrder,
|
||||
this.serviceText,
|
||||
this.isSentForApproval,
|
||||
this.exaCartOrderId,
|
||||
this.isTimer,
|
||||
this.timeSeconds,
|
||||
this.totalPendingSeconds,
|
||||
this.timeMinute,
|
||||
this.timeHour,
|
||||
this.timeTotalSeconds,
|
||||
this.timeTotalMinute,
|
||||
this.timeTotalHour,
|
||||
this.approvalStatus,
|
||||
this.isActive,
|
||||
this.clickButton,
|
||||
this.procedures,
|
||||
this.pickupLocation,
|
||||
this.dropOffLocation,
|
||||
this.clinicName,
|
||||
this.doctorName,
|
||||
this.branch,
|
||||
this.time,
|
||||
this.notes});
|
||||
|
||||
GetCMCAllOrdersResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
log("responseJson: $json");
|
||||
iD = json['ID'];
|
||||
patientId = json['PatientId'];
|
||||
patientOutSa = json['PatientOutSa'];
|
||||
isOutPatient = json['IsOutPatient'];
|
||||
projectId = json['ProjectId'];
|
||||
nearestProjectId = json['NearestProjectId'];
|
||||
longitude = json['Longitude'];
|
||||
latitude = json['Latitude'];
|
||||
appointmentNo = json['AppointmentNo'];
|
||||
dischargeId = json['DischargeId'];
|
||||
statusId = json['StatusId'];
|
||||
serviceId = json['ServiceId'];
|
||||
channel = json['Channel'];
|
||||
orderpayment = json['orderpayment'] != null ? Orderpayment.fromJson(json['orderpayment']) : null;
|
||||
orderselectedservice = json['orderselectedservice'];
|
||||
wforder = json['wforder'];
|
||||
orderapprovalobj = json['orderapprovalobj'];
|
||||
created = json['Created'];
|
||||
createdBy = json['CreatedBy'];
|
||||
modified = json['Modified'];
|
||||
modifiedBy = json['ModifiedBy'];
|
||||
isDeleted = json['IsDeleted'];
|
||||
statusText = json['StatusText'];
|
||||
paymentStatus = json['PaymentStatus'];
|
||||
clientRequestid = json['ClientRequestid'];
|
||||
paymentStatusText = json['PaymentStatusText'];
|
||||
projectName = json['ProjectName'];
|
||||
nearestProjectName = json['NearestProjectName'];
|
||||
paymentAmount = json['PaymentAmount'];
|
||||
wFOrder = json['WF_order'] != null ? WFOrder.fromJson(json['WF_order']) : null;
|
||||
serviceText = json['ServiceText'];
|
||||
isSentForApproval = json['isSentForApproval'];
|
||||
exaCartOrderId = json['ExaCart_OrderId'];
|
||||
isTimer = json['isTimer'];
|
||||
timeSeconds = json['TimeSeconds'];
|
||||
totalPendingSeconds = json['TotalPendingSeconds'];
|
||||
timeMinute = json['TimeMinute'];
|
||||
timeHour = json['TimeHour'];
|
||||
timeTotalSeconds = json['TimeTotalSeconds'];
|
||||
timeTotalMinute = json['TimeTotalMinute'];
|
||||
timeTotalHour = json['TimeTotalHour'];
|
||||
approvalStatus = json['ApprovalStatus'];
|
||||
isActive = json['isActive'];
|
||||
clickButton = json['ClickButton'];
|
||||
pickupLocation = json['PickupLocation'];
|
||||
dropOffLocation = json['DropOffLocation'];
|
||||
clinicName = json['clinicName'];
|
||||
doctorName = json['DoctorName'];
|
||||
branch = json['Branch'];
|
||||
time = json['Time'];
|
||||
notes = json['Notes'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['ID'] = iD;
|
||||
data['PatientId'] = patientId;
|
||||
data['PatientOutSa'] = patientOutSa;
|
||||
data['IsOutPatient'] = isOutPatient;
|
||||
data['ProjectId'] = projectId;
|
||||
data['NearestProjectId'] = nearestProjectId;
|
||||
data['Longitude'] = longitude;
|
||||
data['Latitude'] = latitude;
|
||||
data['AppointmentNo'] = appointmentNo;
|
||||
data['DischargeId'] = dischargeId;
|
||||
data['StatusId'] = statusId;
|
||||
data['ServiceId'] = serviceId;
|
||||
data['Channel'] = channel;
|
||||
if (orderpayment != null) {
|
||||
data['orderpayment'] = orderpayment!.toJson();
|
||||
}
|
||||
data['orderselectedservice'] = orderselectedservice;
|
||||
|
||||
data['wforder'] = wforder;
|
||||
data['orderapprovalobj'] = orderapprovalobj;
|
||||
data['Created'] = created;
|
||||
data['CreatedBy'] = createdBy;
|
||||
data['Modified'] = modified;
|
||||
data['ModifiedBy'] = modifiedBy;
|
||||
data['IsDeleted'] = isDeleted;
|
||||
data['StatusText'] = statusText;
|
||||
data['PaymentStatus'] = paymentStatus;
|
||||
data['ClientRequestid'] = clientRequestid;
|
||||
data['PaymentStatusText'] = paymentStatusText;
|
||||
data['ProjectName'] = projectName;
|
||||
data['NearestProjectName'] = nearestProjectName;
|
||||
data['PaymentAmount'] = paymentAmount;
|
||||
if (wFOrder != null) {
|
||||
data['WF_order'] = wFOrder!.toJson();
|
||||
}
|
||||
data['ServiceText'] = serviceText;
|
||||
data['isSentForApproval'] = isSentForApproval;
|
||||
data['ExaCart_OrderId'] = exaCartOrderId;
|
||||
data['isTimer'] = isTimer;
|
||||
data['TimeSeconds'] = timeSeconds;
|
||||
data['TotalPendingSeconds'] = totalPendingSeconds;
|
||||
data['TimeMinute'] = timeMinute;
|
||||
data['TimeHour'] = timeHour;
|
||||
data['TimeTotalSeconds'] = timeTotalSeconds;
|
||||
data['TimeTotalMinute'] = timeTotalMinute;
|
||||
data['TimeTotalHour'] = timeTotalHour;
|
||||
data['ApprovalStatus'] = approvalStatus;
|
||||
data['isActive'] = isActive;
|
||||
data['ClickButton'] = clickButton;
|
||||
data['PickupLocation'] = pickupLocation;
|
||||
data['DropOffLocation'] = dropOffLocation;
|
||||
data['clinicName'] = clinicName;
|
||||
data['DoctorName'] = doctorName;
|
||||
data['Branch'] = branch;
|
||||
data['Time'] = time;
|
||||
data['Notes'] = notes;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Orderpayment {
|
||||
int? iD;
|
||||
int? orderId;
|
||||
dynamic clientRequestId;
|
||||
dynamic totalAmount;
|
||||
int? paymentStatus;
|
||||
dynamic order;
|
||||
String? created;
|
||||
dynamic createdBy;
|
||||
dynamic modified;
|
||||
dynamic modifiedBy;
|
||||
bool? isDeleted;
|
||||
|
||||
Orderpayment(
|
||||
{this.iD,
|
||||
this.orderId,
|
||||
this.clientRequestId,
|
||||
this.totalAmount,
|
||||
this.paymentStatus,
|
||||
this.order,
|
||||
this.created,
|
||||
this.createdBy,
|
||||
this.modified,
|
||||
this.modifiedBy,
|
||||
this.isDeleted});
|
||||
|
||||
Orderpayment.fromJson(Map<String, dynamic> json) {
|
||||
iD = json['ID'];
|
||||
orderId = json['OrderId'];
|
||||
clientRequestId = json['ClientRequestId'];
|
||||
totalAmount = json['TotalAmount'];
|
||||
paymentStatus = json['PaymentStatus'];
|
||||
order = json['Order'];
|
||||
created = json['Created'];
|
||||
createdBy = json['CreatedBy'];
|
||||
modified = json['Modified'];
|
||||
modifiedBy = json['ModifiedBy'];
|
||||
isDeleted = json['IsDeleted'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['ID'] = iD;
|
||||
data['OrderId'] = orderId;
|
||||
data['ClientRequestId'] = clientRequestId;
|
||||
data['TotalAmount'] = totalAmount;
|
||||
data['PaymentStatus'] = paymentStatus;
|
||||
data['Order'] = order;
|
||||
data['Created'] = created;
|
||||
data['CreatedBy'] = createdBy;
|
||||
data['Modified'] = modified;
|
||||
data['ModifiedBy'] = modifiedBy;
|
||||
data['IsDeleted'] = isDeleted;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class WFOrder {
|
||||
dynamic wfButtonsDTO;
|
||||
int? iD;
|
||||
int? orderId;
|
||||
int? previousStep;
|
||||
int? nextStep;
|
||||
int? serviceId;
|
||||
dynamic order;
|
||||
String? created;
|
||||
dynamic createdBy;
|
||||
dynamic modified;
|
||||
dynamic modifiedBy;
|
||||
bool? isDeleted;
|
||||
|
||||
WFOrder(
|
||||
{this.wfButtonsDTO,
|
||||
this.iD,
|
||||
this.orderId,
|
||||
this.previousStep,
|
||||
this.nextStep,
|
||||
this.serviceId,
|
||||
this.order,
|
||||
this.created,
|
||||
this.createdBy,
|
||||
this.modified,
|
||||
this.modifiedBy,
|
||||
this.isDeleted});
|
||||
|
||||
WFOrder.fromJson(Map<String, dynamic> json) {
|
||||
wfButtonsDTO = json['wf_ButtonsDTO'];
|
||||
iD = json['ID'];
|
||||
orderId = json['OrderId'];
|
||||
previousStep = json['PreviousStep'];
|
||||
nextStep = json['NextStep'];
|
||||
serviceId = json['ServiceId'];
|
||||
order = json['Order'];
|
||||
created = json['Created'];
|
||||
createdBy = json['CreatedBy'];
|
||||
modified = json['Modified'];
|
||||
modifiedBy = json['ModifiedBy'];
|
||||
isDeleted = json['IsDeleted'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['wf_ButtonsDTO'] = wfButtonsDTO;
|
||||
data['ID'] = iD;
|
||||
data['OrderId'] = orderId;
|
||||
data['PreviousStep'] = previousStep;
|
||||
data['NextStep'] = nextStep;
|
||||
data['ServiceId'] = serviceId;
|
||||
data['Order'] = order;
|
||||
data['Created'] = created;
|
||||
data['CreatedBy'] = createdBy;
|
||||
data['Modified'] = modified;
|
||||
data['ModifiedBy'] = modifiedBy;
|
||||
data['IsDeleted'] = isDeleted;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
class GetCMCServicesResponseModel {
|
||||
int? iD;
|
||||
String? serviceID;
|
||||
int? orderServiceID;
|
||||
String? text;
|
||||
String? textN;
|
||||
dynamic price;
|
||||
dynamic priceVAT;
|
||||
dynamic priceTotal;
|
||||
bool? isEnabled;
|
||||
int? orderId;
|
||||
int? quantity;
|
||||
|
||||
GetCMCServicesResponseModel({
|
||||
this.iD,
|
||||
this.serviceID,
|
||||
this.orderServiceID,
|
||||
this.text,
|
||||
this.textN,
|
||||
this.price,
|
||||
this.priceVAT,
|
||||
this.priceTotal,
|
||||
this.isEnabled,
|
||||
this.orderId,
|
||||
this.quantity,
|
||||
});
|
||||
|
||||
GetCMCServicesResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
iD = json['ID'];
|
||||
serviceID = json['ServiceID'];
|
||||
orderServiceID = json['OrderServiceID'];
|
||||
text = json['Text'];
|
||||
textN = json['TextN'];
|
||||
price = json['Price'];
|
||||
priceVAT = json['PriceVAT'];
|
||||
priceTotal = json['PriceTotal'];
|
||||
isEnabled = json['IsEnabled'];
|
||||
orderId = json['OrderId'];
|
||||
quantity = json['Quantity'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = {};
|
||||
data['ID'] = this.iD;
|
||||
data['ServiceID'] = this.serviceID;
|
||||
data['OrderServiceID'] = this.orderServiceID;
|
||||
data['Text'] = this.text;
|
||||
data['TextN'] = this.textN;
|
||||
data['Price'] = this.price;
|
||||
data['PriceVAT'] = this.priceVAT;
|
||||
data['PriceTotal'] = this.priceTotal;
|
||||
data['IsEnabled'] = this.isEnabled;
|
||||
data['OrderId'] = this.orderId;
|
||||
data['Quantity'] = this.quantity;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class HmgServicesComponentModel {
|
||||
int action;
|
||||
String title;
|
||||
String subTitle;
|
||||
String icon;
|
||||
bool isLogin;
|
||||
bool isLocked;
|
||||
Color bgColor;
|
||||
Color textColor;
|
||||
String route;
|
||||
|
||||
HmgServicesComponentModel(
|
||||
this.action,
|
||||
this.title,
|
||||
this.subTitle,
|
||||
this.icon,
|
||||
this.isLogin, {
|
||||
this.isLocked = false,
|
||||
this.bgColor = Colors.white,
|
||||
this.textColor = Colors.black,
|
||||
this.route = '',
|
||||
});
|
||||
}
|
||||
@ -0,0 +1,255 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_ui_selection_helper.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class CmcOrderDetailPage extends StatefulWidget {
|
||||
const CmcOrderDetailPage({super.key});
|
||||
|
||||
@override
|
||||
State<CmcOrderDetailPage> createState() => _CmcOrderDetailPageState();
|
||||
}
|
||||
|
||||
class _CmcOrderDetailPageState extends State<CmcOrderDetailPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
scheduleMicrotask(() async {
|
||||
await hmgServicesViewModel.getCmcOrdersList();
|
||||
});
|
||||
}
|
||||
|
||||
Color _getStatusColor(int? statusId) {
|
||||
switch (statusId) {
|
||||
case 1: // Pending
|
||||
return const Color(0xffCC9B14);
|
||||
case 2: // Processing
|
||||
return const Color(0xff2E303A);
|
||||
case 3: // Completed
|
||||
return const Color(0xff359846);
|
||||
case 4: // Cancelled
|
||||
case 6: // Rejected
|
||||
case 7: // Rejected
|
||||
return const Color(0xffD02127);
|
||||
default:
|
||||
return AppColors.greyColor;
|
||||
}
|
||||
}
|
||||
|
||||
String _formatDate(String? dateString) {
|
||||
if (dateString == null) return '';
|
||||
try {
|
||||
final date = DateTime.parse(dateString);
|
||||
return DateFormat('MMM dd, yyyy').format(date);
|
||||
} catch (e) {
|
||||
return dateString;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildLoadingShimmer() {
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: 3,
|
||||
separatorBuilder: (_, __) => SizedBox(height: 12.h),
|
||||
itemBuilder: (context, index) {
|
||||
return _buildOrderCard(GetCMCAllOrdersResponseModel(), isLoading: true);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOrderCard(GetCMCAllOrdersResponseModel order, {bool isLoading = false}) {
|
||||
final statusColor = _getStatusColor(order.statusId);
|
||||
final canCancel = order.statusId == 1 || order.statusId == 2;
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.h,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Status and Date Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
|
||||
decoration: BoxDecoration(
|
||||
color: statusColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: (isLoading ? "Processing" : order.statusText ?? '')
|
||||
.toText12(
|
||||
color: statusColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
)
|
||||
.toShimmer2(isShow: isLoading),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
(isLoading ? "Jan 15, 2024" : _formatDate(order.created))
|
||||
.toText12(
|
||||
color: AppColors.textColorLight,
|
||||
fontWeight: FontWeight.w500,
|
||||
)
|
||||
.toShimmer2(isShow: isLoading),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
// Request ID
|
||||
Row(
|
||||
children: [
|
||||
if (!isLoading) ...[
|
||||
"Request ID:".needTranslation.toText14(
|
||||
color: AppColors.textColorLight,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
],
|
||||
(isLoading ? "12345" : "${order.iD ?? '-'}").toText16(isBold: true).toShimmer2(isShow: isLoading),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 12.h),
|
||||
|
||||
// Chips for Hospital, Service, and Amount
|
||||
Wrap(
|
||||
spacing: 6.w,
|
||||
runSpacing: 6.h,
|
||||
children: [
|
||||
// Hospital
|
||||
if (order.projectName != null || isLoading)
|
||||
AppCustomChipWidget(
|
||||
icon: AppAssets.hospital,
|
||||
labelText: isLoading ? "Hospital Name" : order.projectName ?? '-',
|
||||
).toShimmer2(isShow: isLoading),
|
||||
|
||||
// Service
|
||||
if (order.serviceText != null || isLoading)
|
||||
AppCustomChipWidget(
|
||||
icon: AppAssets.servicesBottom,
|
||||
labelText: isLoading ? "Service Name" : order.serviceText ?? '-',
|
||||
).toShimmer2(isShow: isLoading),
|
||||
],
|
||||
),
|
||||
|
||||
// Cancel Button
|
||||
if (canCancel || isLoading) ...[
|
||||
SizedBox(height: 16.h),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
text: "Cancel Order".needTranslation,
|
||||
onPressed: isLoading ? () {} : () => CmcUiSelectionHelper.showCancelConfirmationDialog(context: context, order: order),
|
||||
backgroundColor: AppColors.primaryRedColor,
|
||||
borderColor: AppColors.primaryRedColor,
|
||||
textColor: AppColors.whiteColor,
|
||||
fontSize: 14.f,
|
||||
fontWeight: FontWeight.w600,
|
||||
borderRadius: 10.r,
|
||||
height: 44.h,
|
||||
).toShimmer2(isShow: isLoading),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyState() {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 40.h),
|
||||
child: Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 12.r,
|
||||
hasShadow: false,
|
||||
),
|
||||
child: Utils.getNoDataWidget(
|
||||
context,
|
||||
noDataText: "You don't have any CMC orders yet.".needTranslation,
|
||||
isSmallWidget: true,
|
||||
width: 62.w,
|
||||
height: 62.h,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CollapsingListView(
|
||||
title: "CMC Orders".needTranslation,
|
||||
isLeading: true,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Consumer<HmgServicesViewModel>(
|
||||
builder: (context, viewModel, child) {
|
||||
if (viewModel.isCmcOrdersLoading) {
|
||||
return _buildLoadingShimmer();
|
||||
}
|
||||
|
||||
if (viewModel.cmcOrdersList.isEmpty) {
|
||||
return _buildEmptyState();
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: viewModel.cmcOrdersList.length,
|
||||
separatorBuilder: (_, __) => SizedBox(height: 12.h),
|
||||
itemBuilder: (context, index) {
|
||||
final order = viewModel.cmcOrdersList.reversed.toList()[index];
|
||||
|
||||
return AnimationConfiguration.staggeredList(
|
||||
position: index,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
child: SlideAnimation(
|
||||
verticalOffset: 100.0,
|
||||
child: FadeInAnimation(
|
||||
child: _buildOrderCard(order),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
).paddingSymmetrical(24.w, 0),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,515 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/route_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_ui_selection_helper.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
|
||||
import 'package:maps_launcher/maps_launcher.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class CmcSelectionReviewPage extends StatefulWidget {
|
||||
final GetCMCServicesResponseModel selectedService;
|
||||
final HospitalsModel? preSelectedHospital;
|
||||
|
||||
const CmcSelectionReviewPage({super.key, required this.selectedService, this.preSelectedHospital});
|
||||
|
||||
@override
|
||||
State<CmcSelectionReviewPage> createState() => _CmcSelectionReviewPageState();
|
||||
}
|
||||
|
||||
class _CmcSelectionReviewPageState extends State<CmcSelectionReviewPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Initialize ViewModel state with preselected hospital if provided
|
||||
if (widget.preSelectedHospital != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
hmgServicesViewModel.setSelectedHospitalForOrder(widget.preSelectedHospital);
|
||||
hmgServicesViewModel.setSelectedServiceForOrder(widget.selectedService);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appState = getIt.get<AppState>();
|
||||
final isArabic = appState.isArabic();
|
||||
|
||||
return CollapsingListView(
|
||||
title: "Summary".needTranslation,
|
||||
bottomChild: _buildBottomButton(),
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildOrderSummaryCard(isArabic),
|
||||
SizedBox(height: 16.h),
|
||||
_buildSelectedServiceCard(isArabic),
|
||||
SizedBox(height: 16.h),
|
||||
_buildPaymentSummary(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOrderSummaryCard(bool isArabic) {
|
||||
return Consumer<HmgServicesViewModel>(
|
||||
builder: (context, hmgServicesViewModel, child) {
|
||||
final selectedHospital = hmgServicesViewModel.selectedHospitalForOrder;
|
||||
final isLocationSelected = selectedHospital != null;
|
||||
|
||||
return Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 16.r,
|
||||
),
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Select Hospital".needTranslation,
|
||||
style: TextStyle(
|
||||
fontSize: 16.f,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.blackColor,
|
||||
letterSpacing: -0.5,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12.h),
|
||||
_buildHospitalSelector(isArabic, selectedHospital, isLocationSelected),
|
||||
if (isLocationSelected) ...[
|
||||
SizedBox(height: 16.h),
|
||||
_buildHospitalMap(selectedHospital),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHospitalSelector(bool isArabic, HospitalsModel? selectedHospital, bool isLocationSelected) {
|
||||
return InkWell(
|
||||
onTap: _showHospitalSelectionBottomSheet,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 14.h),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgScaffoldColor,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
border: Border.all(
|
||||
color: AppColors.greyColor.withAlpha(51),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
isLocationSelected && selectedHospital != null
|
||||
? (isArabic ? (selectedHospital.nameN ?? selectedHospital.name ?? '') : (selectedHospital.name ?? ''))
|
||||
: "Select Hospital".needTranslation,
|
||||
style: TextStyle(
|
||||
fontSize: 14.f,
|
||||
fontWeight: isLocationSelected ? FontWeight.w600 : FontWeight.w400,
|
||||
color: isLocationSelected ? AppColors.blackColor : AppColors.greyTextColor,
|
||||
letterSpacing: -0.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.keyboard_arrow_down,
|
||||
color: AppColors.greyTextColor,
|
||||
size: 24.h,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHospitalMap(HospitalsModel selectedHospital) {
|
||||
final String lat = selectedHospital.latitude ?? "0.0";
|
||||
final String lng = selectedHospital.longitude ?? "0.0";
|
||||
|
||||
log("selectedHospital: $lng and $lat");
|
||||
|
||||
if (lat == "0.0" || lng == "0.0") return SizedBox.shrink();
|
||||
|
||||
final String staticMapUrl =
|
||||
"https://maps.googleapis.com/maps/api/staticmap?center=$lat,$lng&zoom=16&size=600x300&maptype=roadmap&markers=color:red%7C$lat,$lng&key=AIzaSyCyDbWUM9d_sBUGIE8PcuShzPaqO08NSC8";
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
child: Image.network(
|
||||
staticMapUrl,
|
||||
height: 200.h,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return Container(
|
||||
height: 200.h,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgScaffoldColor,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: AppColors.primaryRedColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Container(
|
||||
height: 200.h,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bgScaffoldColor,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.error_outline,
|
||||
size: 48.h,
|
||||
color: AppColors.greyTextColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 12.h,
|
||||
right: 12.w,
|
||||
child: InkWell(
|
||||
onTap: () => _launchDirections(selectedHospital),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: BorderRadius.circular(1000.r),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Color.fromARGB(26, 0, 0, 0),
|
||||
blurRadius: 8,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Utils.buildSvgWithAssets(
|
||||
icon: AppAssets.directions_icon,
|
||||
width: 16.w,
|
||||
height: 16.h,
|
||||
),
|
||||
SizedBox(width: 6.w),
|
||||
Text(
|
||||
"Get Directions".needTranslation,
|
||||
style: TextStyle(
|
||||
fontSize: 12.f,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.blackColor,
|
||||
letterSpacing: -0.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSelectedServiceCard(bool isArabic) {
|
||||
final serviceName = isArabic ? (widget.selectedService.textN ?? widget.selectedService.text ?? '') : (widget.selectedService.text ?? '');
|
||||
final price = widget.selectedService.priceTotal ?? 0.0;
|
||||
|
||||
return Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 16.r,
|
||||
),
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Selected Service".needTranslation.toText14(
|
||||
weight: FontWeight.w600,
|
||||
color: AppColors.greyTextColor,
|
||||
letterSpacing: -0.4,
|
||||
),
|
||||
SizedBox(height: 6.h),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: serviceName.toText16(weight: FontWeight.w700, color: AppColors.blackColor, letterSpacing: -0.5)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPaymentSummary() {
|
||||
// Use selected service from widget
|
||||
final service = widget.selectedService;
|
||||
|
||||
log("service: ${service.toJson()}");
|
||||
|
||||
final double amountBeforeTax = service.price ?? 0.0;
|
||||
final double taxAmount = service.priceVAT ?? 0.0;
|
||||
final double totalAmount = service.priceTotal ?? (amountBeforeTax + taxAmount);
|
||||
|
||||
return Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.h,
|
||||
hasShadow: false,
|
||||
),
|
||||
child: Consumer<PayfortViewModel>(builder: (context, payfortVM, child) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 24.h),
|
||||
"Total amount to pay".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
|
||||
SizedBox(height: 17.h),
|
||||
|
||||
// Amount before tax
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"Amount before tax".needTranslation.toText14(isBold: true),
|
||||
Utils.getPaymentAmountWithSymbol(
|
||||
amountBeforeTax.toString().toText16(isBold: true),
|
||||
AppColors.blackColor,
|
||||
13,
|
||||
isSaudiCurrency: true,
|
||||
),
|
||||
],
|
||||
).paddingSymmetrical(24.h, 0.h),
|
||||
|
||||
// VAT (use label VAT 15% if desired)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor),
|
||||
Utils.getPaymentAmountWithSymbol(
|
||||
taxAmount.toString().toText14(isBold: true, color: AppColors.greyTextColor),
|
||||
AppColors.greyTextColor,
|
||||
13,
|
||||
isSaudiCurrency: true,
|
||||
),
|
||||
],
|
||||
).paddingSymmetrical(24.h, 0.h),
|
||||
SizedBox(height: 17.h),
|
||||
// Total Amount
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
"".needTranslation.toText14(isBold: true),
|
||||
Utils.getPaymentAmountWithSymbol(
|
||||
totalAmount.toString().toText24(isBold: true),
|
||||
AppColors.blackColor,
|
||||
17,
|
||||
isSaudiCurrency: true,
|
||||
),
|
||||
],
|
||||
).paddingSymmetrical(24.h, 0.h),
|
||||
|
||||
SizedBox(height: 16.h),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBottomButton() {
|
||||
return Consumer<HmgServicesViewModel>(
|
||||
builder: (context, hmgServicesViewModel, child) {
|
||||
final isLocationSelected = hmgServicesViewModel.selectedHospitalForOrder != null;
|
||||
|
||||
return SafeArea(
|
||||
top: false,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Color.fromARGB(13, 0, 0, 0),
|
||||
blurRadius: 8,
|
||||
offset: Offset(0, -2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: CustomButton(
|
||||
text: "Confirm".needTranslation,
|
||||
onPressed: () {
|
||||
isLocationSelected ? _handleConfirm() : null;
|
||||
},
|
||||
textColor: AppColors.whiteColor,
|
||||
backgroundColor: isLocationSelected ? AppColors.successColor : AppColors.greyColor,
|
||||
borderRadius: 12.r,
|
||||
borderColor: Colors.transparent,
|
||||
borderWidth: 0,
|
||||
padding: EdgeInsets.symmetric(vertical: 14.h),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _showHospitalSelectionBottomSheet() {
|
||||
CmcUiSelectionHelper.showHospitalSelectionBottomSheet(context: context, onHospitalSelected: (hospital) => context.pop());
|
||||
}
|
||||
|
||||
void _launchDirections(HospitalsModel selectedHospital) {
|
||||
final double lat = double.parse(selectedHospital.latitude ?? "0.0");
|
||||
final double lng = double.parse(selectedHospital.longitude ?? "0.0");
|
||||
|
||||
if (lat != 0.0 && lng != 0.0) {
|
||||
MapsLauncher.launchCoordinates(
|
||||
lat,
|
||||
lng,
|
||||
selectedHospital.name ?? "Hospital",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
showSuccessBottomSheet(int requestId, HmgServicesViewModel hmgServicesViewModel) {
|
||||
return showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
children: [
|
||||
Utils.getSuccessWidget(loadingText: "Your request has been successfully submitted.".needTranslation),
|
||||
Row(
|
||||
children: [
|
||||
"Here is your request #: ".needTranslation.toText14(
|
||||
color: AppColors.textColorLight,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
("$requestId").toText16(isBold: true),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 24.h),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 56.h,
|
||||
text: LocaleKeys.ok.tr(),
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
context.pop();
|
||||
hmgServicesViewModel.getAllCmcOrders();
|
||||
},
|
||||
textColor: AppColors.whiteColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
isCloseButtonVisible: false,
|
||||
isDismissible: false,
|
||||
isFullScreen: false,
|
||||
);
|
||||
}
|
||||
|
||||
void _handleConfirm() {
|
||||
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
final selectedHospital = hmgServicesViewModel.selectedHospitalForOrder;
|
||||
|
||||
if (selectedHospital == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text("Please select a hospital to continue".needTranslation),
|
||||
backgroundColor: AppColors.errorColor,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final selectedService = widget.selectedService;
|
||||
return showCommonBottomSheetWithoutHeight(
|
||||
title: LocaleKeys.notice.tr(context: context),
|
||||
context,
|
||||
child: Utils.getWarningWidget(
|
||||
loadingText: "Are you sure you want to submit this request?".needTranslation,
|
||||
isShowActionButtons: true,
|
||||
onCancelTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onConfirmTap: () async {
|
||||
Navigator.pop(context);
|
||||
LoaderBottomSheet.showLoader();
|
||||
|
||||
// Create the services list
|
||||
final servicesList = [
|
||||
PatientERCMCInsertServicesList(
|
||||
recordID: selectedService.iD,
|
||||
serviceID: selectedService.serviceID,
|
||||
selectedServiceName: selectedService.text,
|
||||
selectedServiceNameAR: selectedService.textN,
|
||||
price: selectedService.price,
|
||||
vAT: selectedService.priceVAT,
|
||||
totalPrice: selectedService.priceTotal,
|
||||
),
|
||||
];
|
||||
|
||||
await hmgServicesViewModel.addCmcOrder(
|
||||
projectID: selectedHospital.mainProjectID ?? 0,
|
||||
orderServiceID: selectedService.orderServiceID ?? 3,
|
||||
services: servicesList,
|
||||
onSuccess: (requestId) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
showSuccessBottomSheet(requestId, hmgServicesViewModel);
|
||||
},
|
||||
onError: (err) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
// showCommonBottomSheetWithoutHeight(context, child: Utils.getErrorWidget(loadingText: err), callBackFunc: () {});
|
||||
},
|
||||
);
|
||||
}),
|
||||
callBackFunc: () {},
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,405 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/cmc_order_detail_page.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/cmc_selection_review_page.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_ui_selection_helper.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/media_viewer/full_screen_image_viewer.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/radio_list_tile_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
class ComprehensiveCheckupPage extends StatefulWidget {
|
||||
const ComprehensiveCheckupPage({super.key});
|
||||
|
||||
@override
|
||||
State<ComprehensiveCheckupPage> createState() => _ComprehensiveCheckupPageState();
|
||||
}
|
||||
|
||||
class _ComprehensiveCheckupPageState extends State<ComprehensiveCheckupPage> {
|
||||
int? _selectedServiceId;
|
||||
GetCMCServicesResponseModel? _selectedService;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final HmgServicesViewModel hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
final AppState appState = getIt.get<AppState>();
|
||||
|
||||
scheduleMicrotask(() async {
|
||||
final user = appState.getAuthenticatedUser();
|
||||
if (user != null) {
|
||||
await hmgServicesViewModel.getAllCmcOrders();
|
||||
await hmgServicesViewModel.getAllCmcServices(patientID: user.patientId ?? 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
GetCMCAllOrdersResponseModel? _getPendingOrder(List<GetCMCAllOrdersResponseModel> orders) {
|
||||
if (orders.isEmpty) return null;
|
||||
|
||||
// Find pending or processing orders (status 1 or 2)
|
||||
for (var order in orders) {
|
||||
if (order.statusId == 1 || order.statusId == 2) {
|
||||
return order;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget _buildPendingOrderCard(GetCMCAllOrdersResponseModel order) {
|
||||
int status = order.statusId ?? 0;
|
||||
String statusDisp = order.statusText ?? "";
|
||||
Color statusColor;
|
||||
|
||||
if (status == 1) {
|
||||
// pending
|
||||
statusColor = AppColors.statusPendingColor;
|
||||
} else if (status == 2) {
|
||||
// processing
|
||||
statusColor = AppColors.statusProcessingColor;
|
||||
} else if (status == 3) {
|
||||
// completed
|
||||
statusColor = AppColors.statusCompletedColor;
|
||||
} else {
|
||||
// cancel / rejected
|
||||
statusColor = AppColors.statusRejectedColor;
|
||||
}
|
||||
|
||||
final canCancel = order.statusId == 1 || order.statusId == 2;
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
margin: EdgeInsets.all(16.w),
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.h,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Status and Date Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
|
||||
decoration: BoxDecoration(
|
||||
color: statusColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: statusDisp.toText12(
|
||||
color: statusColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
if (order.created != null)
|
||||
DateFormat('MMM dd, yyyy').format(DateTime.parse(order.created!)).toText12(
|
||||
color: AppColors.textColorLight,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
// Request ID
|
||||
Row(
|
||||
children: [
|
||||
"Request ID:".needTranslation.toText14(color: AppColors.textColorLight, weight: FontWeight.w500),
|
||||
SizedBox(width: 4.w),
|
||||
"${order.iD ?? '-'}".toText16(isBold: true),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 12.h),
|
||||
|
||||
// Chips for Hospital, Service, and Amount
|
||||
Wrap(
|
||||
spacing: 6.w,
|
||||
runSpacing: 6.h,
|
||||
children: [
|
||||
// Hospital
|
||||
if (order.projectName != null)
|
||||
AppCustomChipWidget(
|
||||
icon: AppAssets.hospital,
|
||||
labelText: order.projectName ?? '-',
|
||||
),
|
||||
|
||||
// Service
|
||||
if (order.serviceText != null)
|
||||
AppCustomChipWidget(
|
||||
icon: AppAssets.file_icon,
|
||||
labelText: order.serviceText ?? '-',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
// Info message
|
||||
Container(
|
||||
padding: EdgeInsets.all(12.w),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.infoBannerBgColor,
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
border: Border.all(
|
||||
color: AppColors.infoBannerBorderColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.info_outline,
|
||||
size: 20.w,
|
||||
color: AppColors.infoBannerIconColor,
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: "You have a pending order. Please wait for it to be processed.".needTranslation.toText12(
|
||||
color: AppColors.infoBannerTextColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (canCancel) ...[
|
||||
SizedBox(height: 16.h),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
text: "Cancel Order".needTranslation,
|
||||
onPressed: () => CmcUiSelectionHelper.showCancelConfirmationDialog(context: context, order: order),
|
||||
backgroundColor: AppColors.primaryRedColor,
|
||||
borderColor: AppColors.primaryRedColor,
|
||||
textColor: AppColors.whiteColor,
|
||||
fontSize: 14.f,
|
||||
fontWeight: FontWeight.w600,
|
||||
borderRadius: 10.r,
|
||||
height: 44.h,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildServiceSelectionList(List<GetCMCServicesResponseModel> services) {
|
||||
if (services.isEmpty) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(24.h),
|
||||
child: Text(
|
||||
'No services available'.needTranslation,
|
||||
style: TextStyle(
|
||||
fontSize: 16.h,
|
||||
color: AppColors.greyTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 16.h),
|
||||
Text(
|
||||
'Select a Service'.needTranslation,
|
||||
style: TextStyle(
|
||||
fontSize: 20.h,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.blackColor,
|
||||
letterSpacing: -0.8,
|
||||
),
|
||||
).paddingOnly(left: 16.w, right: 16.w),
|
||||
ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: services.length,
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
final service = services[index];
|
||||
final serviceName = service.text ?? service.textN ?? '';
|
||||
final price = service.priceTotal ?? 0.0;
|
||||
return RadioListTileWidget<int>(
|
||||
value: service.iD ?? 0,
|
||||
groupValue: _selectedServiceId,
|
||||
title: serviceName,
|
||||
subtitleWidget: Utils.getPaymentAmountWithSymbol(
|
||||
isExpanded: false,
|
||||
price.toString().toText14(),
|
||||
AppColors.blackColor,
|
||||
14,
|
||||
isSaudiCurrency: true,
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedServiceId = value;
|
||||
_selectedService = service;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
// Illustration image below the services list similar to the old implementation
|
||||
SizedBox(height: 12.h),
|
||||
Builder(builder: (context) {
|
||||
final appStateLocal = getIt.get<AppState>();
|
||||
final String imagePath = appStateLocal.isArabic() ? AppAssets.comprehensiveCheckupAr : AppAssets.comprehensiveCheckupEn;
|
||||
return Stack(
|
||||
children: [
|
||||
Image.asset(
|
||||
imagePath,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
).paddingAll(16.w),
|
||||
Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color.fromARGB(51, 0, 0, 0),
|
||||
borderRadius: BorderRadius.circular(1000.r),
|
||||
),
|
||||
margin: EdgeInsets.all(16.h),
|
||||
child: IconButton(
|
||||
icon: Icon(
|
||||
Icons.zoom_in,
|
||||
color: Colors.white,
|
||||
size: 26.w,
|
||||
),
|
||||
padding: EdgeInsets.all(10.h),
|
||||
onPressed: () => _showFullScreenImage(context, imagePath, isSvg: false),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _proceedWithSelectedService() {
|
||||
if (_selectedService != null) {
|
||||
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
|
||||
// Store selected service in ViewModel
|
||||
hmgServicesViewModel.setSelectedServiceForOrder(_selectedService);
|
||||
hmgServicesViewModel.getHospitalsList();
|
||||
// Show hospital selection bottom sheet using common helper
|
||||
CmcUiSelectionHelper.showHospitalSelectionBottomSheet(
|
||||
context: context,
|
||||
onHospitalSelected: (hospital) {
|
||||
Navigator.of(context).pushReplacement(
|
||||
CustomPageRoute(
|
||||
page: CmcSelectionReviewPage(selectedService: _selectedService!, preSelectedHospital: hospital),
|
||||
direction: AxisDirection.left,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildLoadingShimmer() {
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.all(16.w),
|
||||
itemCount: 10,
|
||||
separatorBuilder: (_, __) => SizedBox(height: 12.h),
|
||||
itemBuilder: (context, index) {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: Colors.grey[300]!,
|
||||
highlightColor: Colors.grey[100]!,
|
||||
child: Container(
|
||||
height: 80.h,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CollapsingListView(
|
||||
title: "Comprehensive Checkup".needTranslation,
|
||||
history: () => Navigator.of(context).push(CustomPageRoute(page: CmcOrderDetailPage(), direction: AxisDirection.up)),
|
||||
bottomChild: Consumer<HmgServicesViewModel>(
|
||||
builder: (context, hmgServicesViewModel, child) {
|
||||
if (hmgServicesViewModel.isCmcOrdersLoading || hmgServicesViewModel.isCmcServicesLoading) return SizedBox.shrink();
|
||||
final pendingOrder = _getPendingOrder(hmgServicesViewModel.cmcOrdersList);
|
||||
if (pendingOrder == null && _selectedServiceId != null) {
|
||||
return SafeArea(
|
||||
top: false,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 12.h),
|
||||
child: CustomButton(
|
||||
borderWidth: 0,
|
||||
text: "Next".needTranslation,
|
||||
onPressed: _proceedWithSelectedService,
|
||||
textColor: AppColors.whiteColor,
|
||||
borderRadius: 12.r,
|
||||
borderColor: Colors.transparent,
|
||||
padding: EdgeInsets.symmetric(vertical: 14.h),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
child: Consumer<HmgServicesViewModel>(
|
||||
builder: (context, hmgServicesViewModel, child) {
|
||||
if (hmgServicesViewModel.isCmcOrdersLoading || hmgServicesViewModel.isCmcServicesLoading) {
|
||||
return _buildLoadingShimmer();
|
||||
}
|
||||
final pendingOrder = _getPendingOrder(hmgServicesViewModel.cmcOrdersList);
|
||||
if (pendingOrder != null) {
|
||||
return _buildPendingOrderCard(pendingOrder);
|
||||
} else {
|
||||
return _buildServiceSelectionList(hmgServicesViewModel.cmcServicesList);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showFullScreenImage(BuildContext context, String path, {bool isSvg = false}) {
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (_) => FullScreenImageViewer(isSvg: isSvg, path: path)));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
class CmcHospitalBottomSheetBody extends StatelessWidget {
|
||||
final Function(HospitalsModel) onHospitalSelected;
|
||||
|
||||
const CmcHospitalBottomSheetBody({super.key, required this.onHospitalSelected});
|
||||
|
||||
Widget _buildLoadingShimmer() {
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: 4,
|
||||
separatorBuilder: (_, __) => SizedBox(height: 12.h),
|
||||
itemBuilder: (context, index) {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: Colors.grey[300]!,
|
||||
highlightColor: Colors.grey[100]!,
|
||||
child: Container(
|
||||
height: 80.h,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appState = getIt.get<AppState>();
|
||||
final bool isArabic = appState.isArabic();
|
||||
final bool isLocationEnabled = (appState.userLat != 0) && (appState.userLong != 0);
|
||||
|
||||
return Consumer(
|
||||
builder: (BuildContext context, HmgServicesViewModel hmgServicesViewModel, Widget? child) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Choose your preferred hospital for the service".needTranslation.toText14(
|
||||
weight: FontWeight.w400,
|
||||
color: AppColors.greyTextColor,
|
||||
letterSpacing: -0.4,
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
TextInputWidget(
|
||||
labelText: LocaleKeys.search.tr(),
|
||||
hintText: LocaleKeys.searchHospital.tr(),
|
||||
onChange: (value) {
|
||||
hmgServicesViewModel.filterHospitalsByString(value ?? '', isArabic);
|
||||
},
|
||||
isEnable: true,
|
||||
prefix: null,
|
||||
autoFocus: false,
|
||||
isBorderAllowed: false,
|
||||
keyboardType: TextInputType.text,
|
||||
isAllowLeadingIcon: true,
|
||||
selectionType: SelectionTypeEnum.search,
|
||||
padding: EdgeInsets.symmetric(vertical: ResponsiveExtension(10).h, horizontal: ResponsiveExtension(15).h),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.4,
|
||||
child: hmgServicesViewModel.isHospitalListLoading
|
||||
? _buildLoadingShimmer()
|
||||
: hmgServicesViewModel.filteredHospitalsList.isEmpty
|
||||
? Center(
|
||||
child: "No hospitals Found".needTranslation.toText16(weight: FontWeight.w500, color: AppColors.greyTextColor),
|
||||
)
|
||||
: ListView.separated(
|
||||
itemCount: hmgServicesViewModel.filteredHospitalsList.length,
|
||||
separatorBuilder: (context, index) => SizedBox(height: 12.h),
|
||||
itemBuilder: (context, index) {
|
||||
final hospital = hmgServicesViewModel.filteredHospitalsList[index];
|
||||
return CmcHospitalListItem(
|
||||
hospital: hospital,
|
||||
isLocationEnabled: isLocationEnabled,
|
||||
onPress: () {
|
||||
hmgServicesViewModel.setSelectedHospital(hospital);
|
||||
onHospitalSelected(hospital);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||
|
||||
class CmcHospitalListItem extends StatelessWidget {
|
||||
final HospitalsModel hospital;
|
||||
final VoidCallback onPress;
|
||||
final bool isLocationEnabled;
|
||||
|
||||
const CmcHospitalListItem({
|
||||
super.key,
|
||||
required this.hospital,
|
||||
required this.onPress,
|
||||
this.isLocationEnabled = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appState = getIt.get<AppState>();
|
||||
final bool isArabic = appState.isArabic();
|
||||
final String hospitalName = isArabic ? (hospital.nameN ?? hospital.name ?? '') : (hospital.name ?? '');
|
||||
|
||||
return InkWell(
|
||||
onTap: onPress,
|
||||
child: DecoratedBox(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 20.h,
|
||||
hasShadow: false,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 8.h,
|
||||
children: [
|
||||
_buildHospitalName(hospitalName),
|
||||
_buildDistanceInfo(),
|
||||
],
|
||||
),
|
||||
),
|
||||
Transform.flip(
|
||||
flipX: isArabic,
|
||||
child: Utils.buildSvgWithAssets(
|
||||
icon: AppAssets.forward_arrow_icon_small,
|
||||
iconColor: AppColors.blackColor,
|
||||
width: 18.h,
|
||||
height: 13.h,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
],
|
||||
).paddingSymmetrical(16.h, 16.h),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHospitalName(String hospitalName) {
|
||||
return Row(
|
||||
children: [
|
||||
Utils.buildSvgWithAssets(
|
||||
icon: (hospital.isHMC == true) ? AppAssets.hmc : AppAssets.hmg,
|
||||
).paddingOnly(right: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
hospitalName,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
color: AppColors.blackColor,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDistanceInfo() {
|
||||
log("hospital: ${hospital.distanceInKilometers}");
|
||||
final distanceText = hospital.distanceInKilometers != null ? hospital.distanceInKilometers!.toStringAsFixed(1) : "0";
|
||||
|
||||
return Row(
|
||||
spacing: 4.w,
|
||||
children: [
|
||||
Visibility(
|
||||
visible: (hospital.distanceInKilometers != null && hospital.distanceInKilometers! > 0),
|
||||
child: AppCustomChipWidget(
|
||||
labelText: "$distanceText km".needTranslation,
|
||||
icon: AppAssets.location_red,
|
||||
iconColor: AppColors.errorColor,
|
||||
backgroundColor: AppColors.secondaryLightRedColor,
|
||||
textColor: AppColors.errorColor,
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: (hospital.distanceInKilometers == null || hospital.distanceInKilometers == 0),
|
||||
child: AppCustomChipWidget(
|
||||
labelText: " Distance not available".needTranslation,
|
||||
textColor: AppColors.blackColor,
|
||||
),
|
||||
),
|
||||
// Visibility(
|
||||
// visible: !isLocationEnabled,
|
||||
// child: AppCustomChipWidget(
|
||||
// labelText: "Location turned off".needTranslation,
|
||||
// deleteIcon: AppAssets.location_unavailable,
|
||||
// deleteIconSize: Size(9.w, 12.h),
|
||||
// textColor: AppColors.blackColor,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/route_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
|
||||
|
||||
class CmcUiSelectionHelper {
|
||||
static void showHospitalSelectionBottomSheet({
|
||||
required BuildContext context,
|
||||
required Function(dynamic) onHospitalSelected,
|
||||
}) {
|
||||
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
title: "Select Hospital".needTranslation,
|
||||
child: CmcHospitalBottomSheetBody(
|
||||
onHospitalSelected: (hospital) {
|
||||
hmgServicesViewModel.setSelectedHospitalForOrder(hospital);
|
||||
onHospitalSelected(hospital);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static void showCancelConfirmationDialog({
|
||||
required BuildContext context,
|
||||
required GetCMCAllOrdersResponseModel order,
|
||||
}) {
|
||||
final HmgServicesViewModel hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
|
||||
return showCommonBottomSheetWithoutHeight(
|
||||
title: LocaleKeys.notice.tr(context: context),
|
||||
context,
|
||||
child: Utils.getWarningWidget(
|
||||
loadingText: "Are you sure you want to cancel this order?".needTranslation,
|
||||
isShowActionButtons: true,
|
||||
onCancelTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onConfirmTap: () async {
|
||||
Navigator.pop(context);
|
||||
LoaderBottomSheet.showLoader();
|
||||
|
||||
final requestModel = OrderUpdateRequestModel(
|
||||
presOrderID: order.iD,
|
||||
rejectionReason: "Cancelled by user",
|
||||
presOrderStatus: 4, // Cancelled status
|
||||
editedBy: 3,
|
||||
);
|
||||
|
||||
await hmgServicesViewModel.updateCmcPresOrder(
|
||||
requestModel: requestModel,
|
||||
onSuccess: (_) async {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
children: [
|
||||
Utils.getSuccessWidget(loadingText: "Order has been cancelled successfully".needTranslation),
|
||||
SizedBox(height: 24.h),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 56.h,
|
||||
text: LocaleKeys.ok.tr(),
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
context.pop();
|
||||
hmgServicesViewModel.getAllCmcOrders();
|
||||
},
|
||||
textColor: AppColors.whiteColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
isCloseButtonVisible: false,
|
||||
isDismissible: false,
|
||||
isFullScreen: false,
|
||||
);
|
||||
},
|
||||
onError: (error) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
callBackFunc: () {},
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/e_referral/new_referral.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class EReferralPage extends StatefulWidget {
|
||||
const EReferralPage({super.key});
|
||||
|
||||
@override
|
||||
_EReferralPageState createState() => _EReferralPageState();
|
||||
}
|
||||
|
||||
class _EReferralPageState extends State<EReferralPage>
|
||||
{
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
bool isNewReferral = true;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.bgScaffoldColor,
|
||||
body: CollapsingListView(
|
||||
title:"E Referral".needTranslation,
|
||||
child: SingleChildScrollView(
|
||||
child: Consumer<PrescriptionsViewModel>(builder: (context, model, child) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 16.h),
|
||||
Row(
|
||||
children: [
|
||||
CustomButton(
|
||||
text: "New Referral".needTranslation,
|
||||
onPressed: () {
|
||||
isNewReferral =true;
|
||||
setState(() {
|
||||
|
||||
});
|
||||
},
|
||||
backgroundColor: model.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
|
||||
borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2),
|
||||
textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
borderRadius: 10,
|
||||
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
|
||||
height: 40.h,
|
||||
),
|
||||
SizedBox(width: 8.h),
|
||||
CustomButton(
|
||||
text: "Search Referral".needTranslation,
|
||||
onPressed: () {
|
||||
isNewReferral =false;
|
||||
},
|
||||
backgroundColor: model.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
|
||||
borderColor: model.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor,
|
||||
textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
borderRadius: 10,
|
||||
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
|
||||
height: 40.h,
|
||||
),
|
||||
],
|
||||
).paddingSymmetrical(24.h, 0.h),
|
||||
SizedBox(height: 20.h),
|
||||
isNewReferral ? NewEReferral() : SizedBox(),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,172 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
|
||||
class NewEReferral extends StatefulWidget {
|
||||
NewEReferral();
|
||||
|
||||
@override
|
||||
_NewEReferralState createState() => _NewEReferralState();
|
||||
}
|
||||
|
||||
class _NewEReferralState extends State<NewEReferral> with TickerProviderStateMixin {
|
||||
late PageController _controller;
|
||||
int _currentIndex = 0;
|
||||
int pageSelected = 2;
|
||||
|
||||
// CreateEReferralRequestModel createEReferralRequestModel = new CreateEReferralRequestModel();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = new PageController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
changePageViewIndex(pageIndex) {
|
||||
_controller.jumpToPage(pageIndex);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
height: double.infinity,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(left: 12,right: 12,top: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: showProgress(
|
||||
title: "Requester Info".needTranslation,
|
||||
status: _currentIndex == 0
|
||||
? "InProgress".needTranslation
|
||||
: _currentIndex > 0
|
||||
? "Completed".needTranslation
|
||||
: "Locked".needTranslation,
|
||||
color: _currentIndex == 0 ? AppColors.infoColor : AppColors.successColor,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: showProgress(
|
||||
title:"Patient Info".needTranslation,
|
||||
status: _currentIndex == 1
|
||||
? "InProgress".needTranslation
|
||||
: _currentIndex > 1
|
||||
? "Completed".needTranslation
|
||||
: "Locked".needTranslation,
|
||||
color: _currentIndex == 1
|
||||
? AppColors.infoColor
|
||||
: _currentIndex > 1
|
||||
? AppColors.successColor
|
||||
: AppColors.greyColor,
|
||||
),
|
||||
),
|
||||
showProgress(
|
||||
title: "Other Info".needTranslation,
|
||||
status: _currentIndex == 2 ? "InProgress".needTranslation :"Locked".needTranslation,
|
||||
color: _currentIndex == 2
|
||||
? AppColors.infoColor
|
||||
: _currentIndex > 3
|
||||
? AppColors.successColor
|
||||
: AppColors.greyColor,
|
||||
isNeedBorder: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: PageView(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
controller: _controller,
|
||||
onPageChanged: (index) {
|
||||
setState(() {
|
||||
_currentIndex = index;
|
||||
});
|
||||
},
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: <Widget>[
|
||||
// NewEReferralStepOnePage(
|
||||
// changePageViewIndex: changePageViewIndex,
|
||||
// createEReferralRequestModel: createEReferralRequestModel,
|
||||
// ),
|
||||
// NewEReferralStepTowPage(
|
||||
// changePageViewIndex: changePageViewIndex,
|
||||
// createEReferralRequestModel: createEReferralRequestModel,
|
||||
// ),
|
||||
// NewEReferralStepThreePage(
|
||||
// changePageViewIndex: changePageViewIndex,
|
||||
// createEReferralRequestModel: createEReferralRequestModel,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget showProgress({required String title, required String status, required Color color, bool isNeedBorder = true}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 26,
|
||||
height: 26,
|
||||
// decoration: containerRadius(color, 200),
|
||||
child: Icon(
|
||||
Icons.done,
|
||||
color: Colors.white,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
if (isNeedBorder)
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child:Divider(),
|
||||
)),
|
||||
],
|
||||
),
|
||||
// mHeight(8),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.44,
|
||||
),
|
||||
),
|
||||
// mHeight(2),
|
||||
Container(
|
||||
padding: EdgeInsets.all(5),
|
||||
// decoration: containerRadius(color.withOpacity(0.2), 4),
|
||||
child: Text(
|
||||
status,
|
||||
style: TextStyle(
|
||||
fontSize: 8,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.32,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,636 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.dart';
|
||||
import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart';
|
||||
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart';
|
||||
import 'package:hmg_patient_app_new/features/location/PlacePrediction.dart';
|
||||
import 'package:hmg_patient_app_new/features/location/location_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/AddressItem.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/appointment_bottom_sheet.dart' show AppointmentBottomSheet;
|
||||
import 'package:hmg_patient_app_new/presentation/emergency_services/widgets/location_input_bottom_sheet.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/CustomSwitch.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/ExpandableBottomSheet.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/map/HMSMap.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/map/map.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../../widgets/common_bottom_sheet.dart';
|
||||
|
||||
class RrtMapScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
floatingActionButton: Visibility(
|
||||
visible: context.watch<EmergencyServicesViewModel>().bottomSheetType ==
|
||||
BottomSheetType.FIXED,
|
||||
child: Padding(
|
||||
padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h),
|
||||
child: DecoratedBox(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor, borderRadius: 12.h),
|
||||
child: Utils.buildSvgWithAssets(
|
||||
icon: AppAssets.locate_me, width: 24.h, height: 24.h)
|
||||
.paddingAll(12.h)
|
||||
.onPress(() {
|
||||
context
|
||||
.read<EmergencyServicesViewModel>()
|
||||
.moveToCurrentLocation();
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomSheet: ExpandableBottomSheet(
|
||||
bottomSheetType:
|
||||
context.watch<EmergencyServicesViewModel>().bottomSheetType,
|
||||
children: {
|
||||
BottomSheetType.EXPANDED: ExpanedBottomSheet(context),
|
||||
BottomSheetType.FIXED: FixedBottomSheet(context),
|
||||
},
|
||||
).paddingAll(16.h),
|
||||
body: Stack(
|
||||
children: [
|
||||
if (context.read<EmergencyServicesViewModel>().isGMSAvailable )
|
||||
GMSMap(
|
||||
currentLocation:
|
||||
context.read<EmergencyServicesViewModel>().getGMSLocation(),
|
||||
onCameraMoved: (value) => context
|
||||
.read<LocationViewModel>()
|
||||
.handleGMSMapCameraMoved(value),
|
||||
onCameraIdle:
|
||||
context.read<LocationViewModel>().handleOnCameraIdle,
|
||||
myLocationEnabled: true,
|
||||
inputController:
|
||||
context.read<EmergencyServicesViewModel>().gmsController,
|
||||
showCenterMarker: true,
|
||||
)
|
||||
else
|
||||
HMSMap(
|
||||
currentLocation:
|
||||
context.read<EmergencyServicesViewModel>().getHMSLocation(),
|
||||
onCameraMoved: (value) => context
|
||||
.read<LocationViewModel>()
|
||||
.handleHMSMapCameraMoved(value),
|
||||
onCameraIdle:
|
||||
context.read<LocationViewModel>().handleOnCameraIdle,
|
||||
myLocationEnabled: false,
|
||||
inputController:
|
||||
context.read<EmergencyServicesViewModel>().hmsController,
|
||||
showCenterMarker: true,
|
||||
),
|
||||
Align(
|
||||
alignment: AlignmentDirectional.topStart,
|
||||
child: Utils.buildSvgWithAssets(
|
||||
icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h)
|
||||
.onPress(() {
|
||||
context
|
||||
.read<EmergencyServicesViewModel>()
|
||||
.flushPickupInformation();
|
||||
Navigator.pop(context);
|
||||
}),
|
||||
).paddingOnly(top: 51.h, left: 24.h),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget FixedBottomSheet(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onVerticalDragUpdate: (details){
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 24.h,
|
||||
children: [
|
||||
inputFields(context),
|
||||
SizedBox(
|
||||
height: 200.h,
|
||||
child: DecoratedBox(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.scaffoldBgColor,
|
||||
customBorder: BorderRadius.only(
|
||||
topLeft: Radius.circular(24.h),
|
||||
topRight: Radius.circular(24.h),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 24.h,
|
||||
children: [
|
||||
Column(
|
||||
spacing: 4.h,
|
||||
children: [
|
||||
"Select Pickup Details".needTranslation.toText21(
|
||||
weight: FontWeight.w600,
|
||||
color: AppColors.textColor,
|
||||
),
|
||||
" Please select the details of pickup"
|
||||
.needTranslation
|
||||
.toText12(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.greyTextColor,
|
||||
)
|
||||
],
|
||||
),
|
||||
CustomButton(
|
||||
text: "Submit Request".needTranslation,
|
||||
onPressed: () {
|
||||
LocationViewModel locationViewModel = context.read<LocationViewModel>();
|
||||
GeocodeResponse? response = locationViewModel.geocodeResponse;
|
||||
PlaceDetails? placeDetails = locationViewModel.placeDetails;
|
||||
PlacePrediction? placePrediction = locationViewModel.selectedPrediction;
|
||||
context.read<EmergencyServicesViewModel>().submitRRTRequest(response?.results.first, placeDetails, placePrediction);
|
||||
})
|
||||
],
|
||||
).paddingOnly(top: 24.h, bottom: 32.h, left: 24.h, right: 24.h),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget ExpanedBottomSheet(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onVerticalDragUpdate: (details){
|
||||
if(details.delta.dy>0){
|
||||
context.read<EmergencyServicesViewModel>().updateBottomSheetState(BottomSheetType.FIXED);
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
DecoratedBox(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.scaffoldBgColor,
|
||||
customBorder: BorderRadius.only(
|
||||
topLeft: Radius.circular(24.h),
|
||||
topRight: Radius.circular(24.h),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 16.h,
|
||||
children: [
|
||||
|
||||
hospitalAndPickUpSection(context),
|
||||
|
||||
|
||||
],
|
||||
).paddingOnly(top: 24.h, bottom: 32.h,left: 24.h, right: 24.h),
|
||||
|
||||
bottomPriceContent(context)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
locationsSections(BuildContext context) {
|
||||
return DecoratedBox(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.h,
|
||||
),
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
ListView.separated(
|
||||
separatorBuilder: (_, __) => Column(
|
||||
children: [
|
||||
SizedBox(height: 16.h),
|
||||
Divider(
|
||||
color: AppColors.bottomNAVBorder,
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
],
|
||||
),
|
||||
shrinkWrap: true,
|
||||
itemCount: 3,
|
||||
itemBuilder: (__, index) {
|
||||
if (index ==
|
||||
2) // todo means the end of the list so handle as per the viewmodel
|
||||
{
|
||||
return CustomButton(
|
||||
height: 40.h,
|
||||
backgroundColor: AppColors.lightRedButtonColor,
|
||||
borderColor: Colors.transparent,
|
||||
text: "Add new address".needTranslation,
|
||||
textColor: AppColors.primaryRedColor,
|
||||
iconColor: AppColors.primaryRedColor,
|
||||
onPressed: () {},
|
||||
icon: AppAssets.add_icon);
|
||||
} else {
|
||||
return AddressItem(
|
||||
isSelected: index == 0,
|
||||
address:
|
||||
"Flat No 301, Building No 12, Palm Spring Apartment, Sector 45, Gurugram, Haryana 122003",
|
||||
title: index == 0
|
||||
? "Home".needTranslation
|
||||
: "Work".needTranslation,
|
||||
onTap: () {},
|
||||
);
|
||||
}
|
||||
})
|
||||
]).paddingAll(16.h),
|
||||
);
|
||||
}
|
||||
|
||||
hospitalAndPickUpSection(BuildContext context) {
|
||||
return DecoratedBox(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.r,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 16.h,
|
||||
children: [
|
||||
// Row(
|
||||
// children: [
|
||||
// hospitalAndPickUpItemContent(
|
||||
// title: "Select Hospital".needTranslation,
|
||||
// subTitle: "hospital".needTranslation,
|
||||
// leadingIcon: AppAssets.hospital,
|
||||
// ),
|
||||
// Utils.buildSvgWithAssets(icon: AppAssets.down_cheveron,
|
||||
// width: 24.h, height: 24.h)
|
||||
// .paddingAll(16.h)
|
||||
// ],
|
||||
// ).onPress((){
|
||||
// showHospitalBottomSheet(context);
|
||||
// }),
|
||||
// SizedBox(height: 16.h),
|
||||
// Divider(
|
||||
// color: AppColors.bottomNAVBorder,
|
||||
// height: 1,
|
||||
// thickness: 1,
|
||||
// ),
|
||||
// SizedBox(height: 16.h),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
hospitalAndPickUpItemContent(
|
||||
title: "Pick".needTranslation,
|
||||
subTitle: "Inside the home".needTranslation,
|
||||
leadingIcon: AppAssets.pickup_bed,
|
||||
),
|
||||
CustomSwitch(
|
||||
value: context
|
||||
.watch<EmergencyServicesViewModel>()
|
||||
.pickupFromInsideTheLocation,
|
||||
onChanged: (value){
|
||||
context
|
||||
.read<EmergencyServicesViewModel>()
|
||||
.updateThePickupPlaceFromLocation(value);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
hospitalAndPickUpItemContent(
|
||||
title: '',
|
||||
subTitle: "Have any appointment".needTranslation,
|
||||
leadingIcon: AppAssets.appointment_checkin_icon,
|
||||
),
|
||||
CustomSwitch(
|
||||
value: context
|
||||
.watch<EmergencyServicesViewModel>()
|
||||
.haveAnAppointment,
|
||||
onChanged: (value) async {
|
||||
// if (value) {
|
||||
// openAppointmentList(context);
|
||||
// }
|
||||
await context.read<EmergencyServicesViewModel>()
|
||||
.updateAppointment(value);
|
||||
if (context.read<EmergencyServicesViewModel>().appointments?.isNotEmpty == true) {
|
||||
openAppointmentList(context);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
).paddingAll(16.h),
|
||||
);
|
||||
}
|
||||
|
||||
leadingIcon(String leadingIcon) {
|
||||
return Container(
|
||||
height: 40.h,
|
||||
width: 40.h,
|
||||
margin: EdgeInsets.only(right: 10.h),
|
||||
padding: EdgeInsets.all(8.h),
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
borderRadius: 12.h,
|
||||
color: AppColors.greyColor,
|
||||
),
|
||||
child: Utils.buildSvgWithAssets(icon: leadingIcon),
|
||||
);
|
||||
}
|
||||
|
||||
hospitalAndPickUpItemContent({
|
||||
required String title,
|
||||
required String subTitle,
|
||||
required String leadingIcon,
|
||||
}) {
|
||||
return Expanded(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
this.leadingIcon(leadingIcon),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Visibility(
|
||||
visible: title.isNotEmpty,
|
||||
child: Column(
|
||||
children: [
|
||||
title.toText12(
|
||||
color: AppColors.greyTextColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
SizedBox(height: 2.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
subTitle.toText14(
|
||||
color: AppColors.textColor,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bottomPriceContent(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
DecoratedBox(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.scaffoldBgColor,
|
||||
customBorder: BorderRadius.only(
|
||||
topLeft: Radius.circular(24.h),
|
||||
topRight: Radius.circular(24.h),
|
||||
),
|
||||
hasShadow: true
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 12.h,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 4.h,
|
||||
children: [
|
||||
"Total amount to pay".needTranslation.toText18(
|
||||
weight: FontWeight.w600,
|
||||
color: AppColors.textColor,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Utils.buildSvgWithAssets(icon: AppAssets.warning,
|
||||
height: 18.h, width: 18.h),
|
||||
SizedBox(width: 4.h,),
|
||||
"Amount will be paid at the hospital"
|
||||
.needTranslation
|
||||
.toText12(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.greyTextColor,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Utils.getPaymentAmountWithSymbol(
|
||||
(Utils.formatNumberToInternationalFormat(context
|
||||
.read<EmergencyServicesViewModel>()
|
||||
.getTotalPrice() ??
|
||||
0))
|
||||
.toText24(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textColor,
|
||||
letterSpacing: -2),
|
||||
AppColors.blackColor,
|
||||
17.h)
|
||||
|
||||
// Utils.getPaymentAmountWithSymbol2(context.read<EmergencyServicesViewModel>().selectedTransportOption?.priceTotal??"0", letterSpacing: -2)
|
||||
],
|
||||
),
|
||||
CustomButton(
|
||||
text: "Submit Request".needTranslation,
|
||||
onPressed: () {
|
||||
LocationViewModel locationViewModel = context.read<LocationViewModel>();
|
||||
GeocodeResponse? response = locationViewModel.geocodeResponse;
|
||||
PlaceDetails? placeDetails = locationViewModel.placeDetails;
|
||||
PlacePrediction? placePrediction = locationViewModel.selectedPrediction;
|
||||
context.read<EmergencyServicesViewModel>().submitAmbulanceRequest(response?.results.first, placeDetails, placePrediction);
|
||||
})
|
||||
],
|
||||
).paddingOnly(top: 24.h, bottom: 12.h, left: 24.h, right: 24.h),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
showHospitalBottomSheet(BuildContext context){
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
title:
|
||||
LocaleKeys.selectHospital.tr(),
|
||||
context,
|
||||
child: Consumer<EmergencyServicesViewModel>(
|
||||
builder:(_,vm,__)=> HospitalBottomSheetBody(
|
||||
searchText: vm.searchController,
|
||||
displayList: vm.displayList,
|
||||
onFacilityClicked: (value) {
|
||||
vm.setSelectedFacility(value);
|
||||
vm.getDisplayList();
|
||||
},
|
||||
onHospitalClicked: (hospital) {
|
||||
Navigator.pop(context);
|
||||
vm.setSelectedHospital(hospital);
|
||||
},
|
||||
onHospitalSearch: (value) {
|
||||
vm.searchHospitals(value ?? "");
|
||||
},
|
||||
selectedFacility:
|
||||
vm.selectedFacility,
|
||||
hmcCount: vm.hmcCount,
|
||||
hmgCount: vm.hmgCount,
|
||||
),
|
||||
),
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
hasBottomPadding: false,
|
||||
backgroundColor: AppColors.bottomSheetBgColor,
|
||||
callBackFunc: () {
|
||||
context.read<EmergencyServicesViewModel>().clearSearchText();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
///it will show the places field first and then hospital field
|
||||
PlaceFirstThanHospitalField(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 16.h,
|
||||
children: [
|
||||
textPlaceInput(context),
|
||||
hospitalField(context),
|
||||
],
|
||||
).paddingOnly(right: 24.h, left: 24.h);
|
||||
}
|
||||
|
||||
HospitalFieldFirstThanPlaces(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 16.h,
|
||||
children: [hospitalField(context), textPlaceInput(context)],
|
||||
).paddingOnly(right: 24.h, left: 24.h);
|
||||
}
|
||||
|
||||
textPlaceInput(context) {
|
||||
return Consumer<LocationViewModel>(builder: (_, vm, __) {
|
||||
print(
|
||||
"the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}");
|
||||
return SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
child: TextInputWidget(
|
||||
labelText: "Enter Pickup Location Manually".needTranslation,
|
||||
hintText: "Enter Pickup Location".needTranslation,
|
||||
controller: TextEditingController(
|
||||
text: vm.geocodeResponse?.results.first.formattedAddress ??
|
||||
vm.selectedPrediction?.description,
|
||||
),
|
||||
leadingIcon: AppAssets.location_pickup,
|
||||
isAllowLeadingIcon: true,
|
||||
isEnable: false,
|
||||
prefix: null,
|
||||
autoFocus: false,
|
||||
isBorderAllowed: false,
|
||||
keyboardType: TextInputType.text,
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: ResponsiveExtension(10).h,
|
||||
horizontal: ResponsiveExtension(15).h,
|
||||
),
|
||||
).onPress(() {
|
||||
openLocationInputBottomSheet(context);
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
///decide which field to show first based on the selected calling place
|
||||
inputFields(BuildContext context) {
|
||||
return textPlaceInput(context);
|
||||
}
|
||||
|
||||
openLocationInputBottomSheet(BuildContext context) {
|
||||
context.read<LocationViewModel>().flushSearchPredictions();
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
title: "".needTranslation,
|
||||
context,
|
||||
child: SizedBox(
|
||||
height: MediaQuery.sizeOf(context).height * .8,
|
||||
child: LocationInputBottomSheet(),
|
||||
),
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
hasBottomPadding: false,
|
||||
backgroundColor: AppColors.bottomSheetBgColor,
|
||||
callBackFunc: () {},
|
||||
);
|
||||
}
|
||||
|
||||
hospitalField(BuildContext context) {
|
||||
return DecoratedBox(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor, borderRadius: 12.h),
|
||||
child: Row(
|
||||
children: [
|
||||
hospitalAndPickUpItemContent(
|
||||
title: "Select Hospital".needTranslation,
|
||||
subTitle: context
|
||||
.read<EmergencyServicesViewModel>()
|
||||
.getSelectedHospitalName() ??
|
||||
"Select Hospital".needTranslation,
|
||||
leadingIcon: AppAssets.hospital,
|
||||
),
|
||||
Utils.buildSvgWithAssets(
|
||||
icon: AppAssets.down_cheveron, width: 24.h, height: 24.h)
|
||||
.paddingAll(16.h)
|
||||
],
|
||||
).onPress(() {
|
||||
print("the item is clicked");
|
||||
showHospitalBottomSheet(context);
|
||||
}).paddingSymmetrical(
|
||||
10.w,
|
||||
12.h,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void openAppointmentList(BuildContext context) {
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
title: "Select Appointment".needTranslation,
|
||||
context,
|
||||
child: SizedBox(
|
||||
height: MediaQuery.sizeOf(context).height * .5,
|
||||
child: AppointmentBottomSheet(
|
||||
list: context.read<EmergencyServicesViewModel>().appointments!,
|
||||
onAppointmentSelection: (appointment) {
|
||||
Navigator.pop(context);
|
||||
context.read<EmergencyServicesViewModel>().setSelectedAppointment(appointment);
|
||||
}),
|
||||
),
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
hasBottomPadding: false,
|
||||
backgroundColor: AppColors.bottomSheetBgColor,
|
||||
callBackFunc: () {
|
||||
context.read<EmergencyServicesViewModel>().checkHasAppointment();
|
||||
},
|
||||
);
|
||||
// );
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class TermsAndCondition extends StatelessWidget {
|
||||
final String termsAndCondition;
|
||||
const TermsAndCondition({super.key, required this.termsAndCondition});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
children: [
|
||||
|
||||
Expanded(
|
||||
child: CollapsingListView(
|
||||
title: "Terms And Condition".needTranslation,
|
||||
child:DecoratedBox(decoration:RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 20.h,
|
||||
hasShadow: true,
|
||||
),child: HtmlWidget(termsAndCondition).paddingAll(16.h)).paddingAll(12.h)))]));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/AmbulanceRequestOrdersModel.dart';
|
||||
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/RRTServiceData.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/emergency_services/history/widget/RequestStatus.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../../../core/utils/utils.dart';
|
||||
|
||||
|
||||
class RRTItem extends StatelessWidget {
|
||||
final GetCMCAllOrdersResponseModel order;
|
||||
|
||||
const RRTItem({super.key, required this.order});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DecoratedBox(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: Colors.white,
|
||||
hasShadow: true,
|
||||
customBorder: BorderRadius.all(
|
||||
Radius.circular(20.h),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 8.h,
|
||||
children: [
|
||||
RequestStatus(status: order.statusId ?? 0),
|
||||
"Req ID: ${order.iD}".toText16(color: AppColors.textColor, weight: FontWeight.w600),
|
||||
Row(
|
||||
spacing: 4.w,
|
||||
children: [
|
||||
chip( Utils.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(order.time)), AppAssets.calendar, AppColors.blackBgColor),
|
||||
chip("Rapid Response Team(RRT)".needTranslation, AppAssets.ic_rrt_vehicle, AppColors.blackBgColor),
|
||||
],
|
||||
),
|
||||
|
||||
if (order.statusId == 1 || order.statusId == 2)
|
||||
CustomButton(
|
||||
text: "Cancel Request".needTranslation,
|
||||
onPressed: () async {
|
||||
openCancelOrderBottomSheet(context);
|
||||
},
|
||||
backgroundColor: AppColors.primaryRedColor,
|
||||
borderColor: AppColors.primaryRedColor,
|
||||
textColor: Colors.white,
|
||||
icon: AppAssets.cancel,
|
||||
height: 40.h,
|
||||
iconSize: 18.w,
|
||||
),
|
||||
],
|
||||
).paddingAll(16.h),
|
||||
).paddingOnly(bottom: 16.h);
|
||||
}
|
||||
|
||||
chip(String title, String iconString, Color iconColor) {
|
||||
return AppCustomChipWidget(
|
||||
labelText: title,
|
||||
icon: iconString,
|
||||
iconColor: iconColor,
|
||||
iconSize: 12.h,
|
||||
);
|
||||
}
|
||||
|
||||
openCancelOrderBottomSheet(BuildContext context) {
|
||||
context.read<EmergencyServicesViewModel>().cancelRRTOrder(order.iD);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/hmg_services_component_model.dart';
|
||||
import 'package:hmg_patient_app_new/services/navigation_service.dart';
|
||||
|
||||
class ServiceGridViewItem extends StatelessWidget {
|
||||
final HmgServicesComponentModel hmgServiceComponentModel;
|
||||
final int index;
|
||||
final bool isHomePage;
|
||||
final bool isLocked;
|
||||
|
||||
const ServiceGridViewItem(this.hmgServiceComponentModel, this.index, this.isHomePage, {super.key, this.isLocked = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () => getIt.get<NavigationService>().pushPageRoute(hmgServiceComponentModel.route),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 48.h,
|
||||
width: 48.w,
|
||||
padding: EdgeInsets.zero,
|
||||
margin: EdgeInsets.zero,
|
||||
decoration: BoxDecoration(
|
||||
color: hmgServiceComponentModel.bgColor,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
),
|
||||
child: Utils.buildSvgWithAssets(
|
||||
icon: hmgServiceComponentModel.icon,
|
||||
height: 21.h,
|
||||
width: 21.w,
|
||||
fit: BoxFit.none,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5.h),
|
||||
hmgServiceComponentModel.title.toText12(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: hmgServiceComponentModel.textColor,
|
||||
maxLine: 1,
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,248 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/home_health_care/widgets/hhc_ui_selection_helper.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class HhcOrderDetailPage extends StatefulWidget {
|
||||
const HhcOrderDetailPage({super.key});
|
||||
|
||||
@override
|
||||
State<HhcOrderDetailPage> createState() => _HhcOrderDetailPageState();
|
||||
}
|
||||
|
||||
class _HhcOrderDetailPageState extends State<HhcOrderDetailPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
scheduleMicrotask(() async {
|
||||
await hmgServicesViewModel.getAllHhcOrders();
|
||||
});
|
||||
}
|
||||
|
||||
Color _getStatusColor(int? statusId) {
|
||||
switch (statusId) {
|
||||
case 1: // Pending
|
||||
return const Color(0xffCC9B14);
|
||||
case 2: // Processing
|
||||
return const Color(0xff2E303A);
|
||||
case 3: // Completed
|
||||
return const Color(0xff359846);
|
||||
case 4: // Cancelled
|
||||
case 6: // Rejected
|
||||
case 7: // Rejected
|
||||
return const Color(0xffD02127);
|
||||
default:
|
||||
return AppColors.greyColor;
|
||||
}
|
||||
}
|
||||
|
||||
String _formatDate(String? dateString) {
|
||||
if (dateString == null) return '';
|
||||
try {
|
||||
final date = DateTime.parse(dateString);
|
||||
return DateFormat('MMM dd, yyyy').format(date);
|
||||
} catch (e) {
|
||||
return dateString;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildLoadingShimmer() {
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: 3,
|
||||
separatorBuilder: (_, __) => SizedBox(height: 12.h),
|
||||
itemBuilder: (context, index) {
|
||||
return _buildOrderCard(GetCMCAllOrdersResponseModel(), isLoading: true);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOrderCard(GetCMCAllOrdersResponseModel order, {bool isLoading = false}) {
|
||||
final statusColor = _getStatusColor(order.statusId);
|
||||
final canCancel = order.statusId == 1 || order.statusId == 2;
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.h,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Status and Date Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
|
||||
decoration: BoxDecoration(
|
||||
color: statusColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: (isLoading ? "Processing" : order.statusText ?? '')
|
||||
.toText12(
|
||||
color: statusColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
)
|
||||
.toShimmer2(isShow: isLoading),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
(isLoading ? "Jan 15, 2024" : _formatDate(order.created))
|
||||
.toText12(
|
||||
color: AppColors.textColorLight,
|
||||
fontWeight: FontWeight.w500,
|
||||
)
|
||||
.toShimmer2(isShow: isLoading),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
// Request ID
|
||||
Row(
|
||||
children: [
|
||||
if (!isLoading) ...[
|
||||
"Request ID:".needTranslation.toText14(
|
||||
color: AppColors.textColorLight,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
],
|
||||
(isLoading ? "12345" : "${order.iD ?? '-'}").toText16(isBold: true).toShimmer2(isShow: isLoading),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 12.h),
|
||||
|
||||
// Chips for Hospital, Service, and Amount
|
||||
Wrap(
|
||||
spacing: 6.w,
|
||||
runSpacing: 6.h,
|
||||
children: [
|
||||
// Service
|
||||
if (order.serviceText != null || isLoading)
|
||||
AppCustomChipWidget(
|
||||
icon: AppAssets.servicesBottom,
|
||||
labelText: isLoading ? "Service Name" : order.serviceText ?? '-',
|
||||
).toShimmer2(isShow: isLoading),
|
||||
],
|
||||
),
|
||||
|
||||
// Cancel Button
|
||||
if (canCancel || isLoading) ...[
|
||||
SizedBox(height: 16.h),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
text: "Cancel Order".needTranslation,
|
||||
onPressed: isLoading ? () {} : () => HhcUiSelectionHelper.showCancelConfirmationDialog(context: context, order: order),
|
||||
backgroundColor: AppColors.primaryRedColor,
|
||||
borderColor: AppColors.primaryRedColor,
|
||||
textColor: AppColors.whiteColor,
|
||||
fontSize: 14.f,
|
||||
fontWeight: FontWeight.w600,
|
||||
borderRadius: 10.r,
|
||||
height: 44.h,
|
||||
).toShimmer2(isShow: isLoading),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyState() {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 40.h),
|
||||
child: Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 12.r,
|
||||
hasShadow: false,
|
||||
),
|
||||
child: Utils.getNoDataWidget(
|
||||
context,
|
||||
noDataText: "You don't have any Home Health Care orders yet.".needTranslation,
|
||||
isSmallWidget: true,
|
||||
width: 62.w,
|
||||
height: 62.h,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CollapsingListView(
|
||||
title: "HHC Orders".needTranslation,
|
||||
isLeading: true,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Consumer<HmgServicesViewModel>(
|
||||
builder: (context, viewModel, child) {
|
||||
if (viewModel.isHhcOrdersLoading) {
|
||||
return _buildLoadingShimmer();
|
||||
}
|
||||
|
||||
if (viewModel.hhcOrdersList.isEmpty) {
|
||||
return _buildEmptyState();
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: viewModel.hhcOrdersList.length,
|
||||
separatorBuilder: (_, __) => SizedBox(height: 12.h),
|
||||
itemBuilder: (context, index) {
|
||||
final order = viewModel.hhcOrdersList.reversed.toList()[index];
|
||||
|
||||
return AnimationConfiguration.staggeredList(
|
||||
position: index,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
child: SlideAnimation(
|
||||
verticalOffset: 100.0,
|
||||
child: FadeInAnimation(
|
||||
child: _buildOrderCard(order),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
).paddingSymmetrical(24.w, 0),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,414 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_order_detail_page.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_selection_review_page.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/home_health_care/widgets/hhc_ui_selection_helper.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
class HhcProceduresPage extends StatefulWidget {
|
||||
const HhcProceduresPage({super.key});
|
||||
|
||||
@override
|
||||
State<HhcProceduresPage> createState() => _HhcProceduresPageState();
|
||||
}
|
||||
|
||||
class _HhcProceduresPageState extends State<HhcProceduresPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final HmgServicesViewModel hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
final AppState appState = getIt.get<AppState>();
|
||||
|
||||
scheduleMicrotask(() async {
|
||||
final user = appState.getAuthenticatedUser();
|
||||
if (user != null) {
|
||||
// Clear previous selections when entering the page
|
||||
hmgServicesViewModel.clearHhcServicesSelection();
|
||||
await hmgServicesViewModel.getAllHhcOrders();
|
||||
await hmgServicesViewModel.getAllHhcServices(patientID: user.patientId ?? 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
GetCMCAllOrdersResponseModel? _getPendingOrder(List<GetCMCAllOrdersResponseModel> orders) {
|
||||
if (orders.isEmpty) return null;
|
||||
|
||||
// Find pending or processing orders (status 1 or 2)
|
||||
for (var order in orders) {
|
||||
if (order.statusId == 1 || order.statusId == 2) {
|
||||
return order;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget _buildPendingOrderCard(GetCMCAllOrdersResponseModel order) {
|
||||
int status = order.statusId ?? 0;
|
||||
String statusDisp = order.statusText ?? "";
|
||||
Color statusColor;
|
||||
|
||||
if (status == 1) {
|
||||
// pending
|
||||
statusColor = AppColors.statusPendingColor;
|
||||
} else if (status == 2) {
|
||||
// processing
|
||||
statusColor = AppColors.statusProcessingColor;
|
||||
} else if (status == 3) {
|
||||
// completed
|
||||
statusColor = AppColors.statusCompletedColor;
|
||||
} else {
|
||||
// cancel / rejected
|
||||
statusColor = AppColors.statusRejectedColor;
|
||||
}
|
||||
|
||||
final canCancel = order.statusId == 1 || order.statusId == 2;
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
margin: EdgeInsets.all(16.w),
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.h,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Status and Date Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
|
||||
decoration: BoxDecoration(
|
||||
color: statusColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: statusDisp.toText12(
|
||||
color: statusColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
if (order.created != null)
|
||||
DateFormat('MMM dd, yyyy').format(DateTime.parse(order.created!)).toText12(
|
||||
color: AppColors.textColorLight,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
// Request ID
|
||||
Row(
|
||||
children: [
|
||||
"Request ID:".needTranslation.toText14(color: AppColors.textColorLight, weight: FontWeight.w500),
|
||||
SizedBox(width: 4.w),
|
||||
"${order.iD ?? '-'}".toText16(isBold: true),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 12.h),
|
||||
|
||||
// Info message
|
||||
Container(
|
||||
padding: EdgeInsets.all(12.w),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.infoBannerBgColor,
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
border: Border.all(
|
||||
color: AppColors.infoBannerBorderColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.info_outline,
|
||||
size: 20.w,
|
||||
color: AppColors.infoBannerIconColor,
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: "You have a pending order. Please wait for it to be processed.".needTranslation.toText12(
|
||||
color: AppColors.infoBannerTextColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (canCancel) ...[
|
||||
SizedBox(height: 16.h),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
text: "Cancel Order".needTranslation,
|
||||
onPressed: () => HhcUiSelectionHelper.showCancelConfirmationDialog(context: context, order: order),
|
||||
backgroundColor: AppColors.primaryRedColor,
|
||||
borderColor: AppColors.primaryRedColor,
|
||||
textColor: AppColors.whiteColor,
|
||||
fontSize: 14.f,
|
||||
fontWeight: FontWeight.w600,
|
||||
borderRadius: 10.r,
|
||||
height: 44.h,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildServiceSelectionList(List<GetCMCServicesResponseModel> services) {
|
||||
if (services.isEmpty) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(24.h),
|
||||
child: Text(
|
||||
'No services available'.needTranslation,
|
||||
style: TextStyle(
|
||||
fontSize: 16.h,
|
||||
color: AppColors.greyTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Consumer<HmgServicesViewModel>(
|
||||
builder: (context, viewModel, child) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 8.h),
|
||||
if (viewModel.selectedHhcServices.isNotEmpty) ...[
|
||||
SizedBox(height: 16.h),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.w),
|
||||
padding: EdgeInsets.all(16.w),
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.primaryRedColor.withValues(alpha: 0.1),
|
||||
borderRadius: 16.r,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Selected Services".needTranslation.toText12(
|
||||
color: AppColors.textColorLight,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
"${viewModel.selectedHhcServices.length} service(s) selected".toText14(
|
||||
isBold: true,
|
||||
weight: FontWeight.bold,
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
"Total Amount".needTranslation.toText12(
|
||||
color: AppColors.textColorLight,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
Utils.getPaymentAmountWithSymbol(
|
||||
viewModel.getHhcSelectedServicesTotal().toStringAsFixed(2).toText16(
|
||||
isBold: true,
|
||||
weight: FontWeight.bold,
|
||||
color: AppColors.primaryRedColor,
|
||||
),
|
||||
AppColors.primaryRedColor,
|
||||
14,
|
||||
isSaudiCurrency: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
SizedBox(height: 16.h),
|
||||
Text(
|
||||
'Select Services'.needTranslation,
|
||||
style: TextStyle(
|
||||
fontSize: 20.h,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.blackColor,
|
||||
letterSpacing: -0.8,
|
||||
),
|
||||
).paddingOnly(left: 16.w, right: 16.w),
|
||||
SizedBox(height: 12.h),
|
||||
ListView.builder(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.w),
|
||||
itemCount: services.length,
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
final service = services[index];
|
||||
final isSelected = viewModel.isHhcServiceSelected(service);
|
||||
final isArabic = getIt.get<AppState>().isArabic();
|
||||
final serviceName = isArabic ? (service.textN ?? service.text ?? '') : (service.text ?? '');
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
margin: EdgeInsets.only(bottom: 12.h),
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 16.r,
|
||||
hasShadow: true,
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () => viewModel.toggleHhcServiceSelection(service),
|
||||
borderRadius: BorderRadius.circular(16.r),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: isSelected,
|
||||
onChanged: (v) => viewModel.toggleHhcServiceSelection(service),
|
||||
activeColor: AppColors.primaryRedColor,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
serviceName.toText16(
|
||||
weight: FontWeight.w400,
|
||||
color: AppColors.blackColor,
|
||||
maxlines: 2,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
// Summary Section
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _proceedWithSelectedService() {
|
||||
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
if (hmgServicesViewModel.selectedHhcServices.isNotEmpty) {
|
||||
hmgServicesViewModel.setSelectedServiceForHhcOrder(hmgServicesViewModel.selectedHhcServices.first);
|
||||
Navigator.of(context).pushReplacement(
|
||||
CustomPageRoute(
|
||||
page: HhcSelectionReviewPage(selectedServices: hmgServicesViewModel.selectedHhcServices),
|
||||
direction: AxisDirection.left,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildLoadingShimmer() {
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.all(16.w),
|
||||
itemCount: 10,
|
||||
separatorBuilder: (_, __) => SizedBox(height: 12.h),
|
||||
itemBuilder: (context, index) {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: Colors.grey[300]!,
|
||||
highlightColor: Colors.grey[100]!,
|
||||
child: Container(
|
||||
height: 80.h,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CollapsingListView(
|
||||
title: "Home Health Care".needTranslation,
|
||||
history: () => Navigator.of(context).push(CustomPageRoute(page: HhcOrderDetailPage(), direction: AxisDirection.up)),
|
||||
bottomChild: Consumer<HmgServicesViewModel>(
|
||||
builder: (context, hmgServicesViewModel, child) {
|
||||
if (hmgServicesViewModel.isHhcOrdersLoading || hmgServicesViewModel.isHhcServicesLoading) return SizedBox.shrink();
|
||||
final pendingOrder = _getPendingOrder(hmgServicesViewModel.hhcOrdersList);
|
||||
if (pendingOrder == null && hmgServicesViewModel.selectedHhcServices.isNotEmpty) {
|
||||
return SafeArea(
|
||||
top: false,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 12.h),
|
||||
child: CustomButton(
|
||||
borderWidth: 0,
|
||||
text: "Next".needTranslation,
|
||||
onPressed: _proceedWithSelectedService,
|
||||
textColor: AppColors.whiteColor,
|
||||
borderRadius: 12.r,
|
||||
borderColor: Colors.transparent,
|
||||
padding: EdgeInsets.symmetric(vertical: 14.h),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
child: Consumer<HmgServicesViewModel>(
|
||||
builder: (context, hmgServicesViewModel, child) {
|
||||
if (hmgServicesViewModel.isHhcOrdersLoading || hmgServicesViewModel.isHhcServicesLoading) {
|
||||
return _buildLoadingShimmer();
|
||||
}
|
||||
final pendingOrder = _getPendingOrder(hmgServicesViewModel.hhcOrdersList);
|
||||
if (pendingOrder != null) {
|
||||
return _buildPendingOrderCard(pendingOrder);
|
||||
} else {
|
||||
return _buildServiceSelectionList(hmgServicesViewModel.hhcServicesList);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,244 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/route_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
|
||||
import 'package:maps_launcher/maps_launcher.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class HhcSelectionReviewPage extends StatefulWidget {
|
||||
final List<GetCMCServicesResponseModel> selectedServices;
|
||||
|
||||
const HhcSelectionReviewPage({super.key, required this.selectedServices});
|
||||
|
||||
@override
|
||||
State<HhcSelectionReviewPage> createState() => _HhcSelectionReviewPageState();
|
||||
}
|
||||
|
||||
class _HhcSelectionReviewPageState extends State<HhcSelectionReviewPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Initialize ViewModel state with selected services
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
if (widget.selectedServices.isNotEmpty) {
|
||||
hmgServicesViewModel.setSelectedServiceForHhcOrder(widget.selectedServices.first);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appState = getIt.get<AppState>();
|
||||
final isArabic = appState.isArabic();
|
||||
|
||||
return CollapsingListView(
|
||||
title: "Summary".needTranslation,
|
||||
bottomChild: _buildBottomButton(),
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSelectedServicesCard(isArabic),
|
||||
SizedBox(height: 16.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSelectedServicesCard(bool isArabic) {
|
||||
return Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 16.r,
|
||||
),
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Selected Services".needTranslation.toText14(
|
||||
weight: FontWeight.w600,
|
||||
color: AppColors.greyTextColor,
|
||||
letterSpacing: -0.4,
|
||||
),
|
||||
SizedBox(height: 12.h),
|
||||
...widget.selectedServices.map((service) {
|
||||
final serviceName = isArabic ? (service.textN ?? service.text ?? '') : (service.text ?? '');
|
||||
final price = service.priceTotal ?? 0.0;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: 4.h),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: serviceName.toText14(
|
||||
weight: FontWeight.w600,
|
||||
color: AppColors.blackColor,
|
||||
letterSpacing: -0.5,
|
||||
maxlines: 2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBottomButton() {
|
||||
return SafeArea(
|
||||
top: false,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Color.fromARGB(13, 0, 0, 0),
|
||||
blurRadius: 8,
|
||||
offset: Offset(0, -2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: CustomButton(
|
||||
text: "Confirm".needTranslation,
|
||||
onPressed: _handleConfirm,
|
||||
textColor: AppColors.whiteColor,
|
||||
backgroundColor: AppColors.successColor,
|
||||
borderRadius: 12.r,
|
||||
borderColor: Colors.transparent,
|
||||
borderWidth: 0,
|
||||
padding: EdgeInsets.symmetric(vertical: 14.h),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _launchDirections(HospitalsModel selectedHospital) {
|
||||
final double lat = double.parse(selectedHospital.latitude ?? "0.0");
|
||||
final double lng = double.parse(selectedHospital.longitude ?? "0.0");
|
||||
|
||||
if (lat != 0.0 && lng != 0.0) {
|
||||
MapsLauncher.launchCoordinates(
|
||||
lat,
|
||||
lng,
|
||||
selectedHospital.name ?? "Hospital",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
showSuccessBottomSheet(int requestId, HmgServicesViewModel hmgServicesViewModel) {
|
||||
return showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
children: [
|
||||
Utils.getSuccessWidget(loadingText: "Your request has been successfully submitted.".needTranslation),
|
||||
Row(
|
||||
children: [
|
||||
"Here is your request #: ".needTranslation.toText14(
|
||||
color: AppColors.textColorLight,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
("$requestId").toText16(isBold: true),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 24.h),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 56.h,
|
||||
text: LocaleKeys.ok.tr(),
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
context.pop();
|
||||
hmgServicesViewModel.getAllHhcOrders();
|
||||
},
|
||||
textColor: AppColors.whiteColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
isCloseButtonVisible: false,
|
||||
isDismissible: false,
|
||||
isFullScreen: false,
|
||||
);
|
||||
}
|
||||
|
||||
void _handleConfirm() {
|
||||
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
final appState = getIt.get<AppState>();
|
||||
|
||||
return showCommonBottomSheetWithoutHeight(
|
||||
title: LocaleKeys.notice.tr(context: context),
|
||||
context,
|
||||
child: Utils.getWarningWidget(
|
||||
loadingText: "Are you sure you want to submit this request?".needTranslation,
|
||||
isShowActionButtons: true,
|
||||
onCancelTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onConfirmTap: () async {
|
||||
Navigator.pop(context);
|
||||
LoaderBottomSheet.showLoader();
|
||||
|
||||
// Create the services list from all selected services
|
||||
final servicesList = widget.selectedServices.map((selectedService) {
|
||||
return PatientERCMCInsertServicesList(
|
||||
recordID: selectedService.iD,
|
||||
serviceID: selectedService.serviceID,
|
||||
selectedServiceName: selectedService.text,
|
||||
selectedServiceNameAR: selectedService.textN,
|
||||
price: selectedService.price,
|
||||
vAT: selectedService.priceVAT,
|
||||
totalPrice: selectedService.priceTotal,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
// For HHC, we don't need hospital selection, use a default projectID or 0
|
||||
await hmgServicesViewModel.addHhcOrder(
|
||||
projectID: 0,
|
||||
// HHC doesn't require hospital/project selection
|
||||
orderServiceID: widget.selectedServices.first.orderServiceID ?? 4,
|
||||
// HHC service ID
|
||||
services: servicesList,
|
||||
onSuccess: (requestId) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
showSuccessBottomSheet(requestId, hmgServicesViewModel);
|
||||
},
|
||||
onError: (err) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
},
|
||||
);
|
||||
}),
|
||||
callBackFunc: () {},
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/route_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class HhcUiSelectionHelper {
|
||||
static void showCancelConfirmationDialog({
|
||||
required BuildContext context,
|
||||
required GetCMCAllOrdersResponseModel order,
|
||||
}) {
|
||||
final HmgServicesViewModel hmgServicesViewModel = context.read<HmgServicesViewModel>();
|
||||
|
||||
return showCommonBottomSheetWithoutHeight(
|
||||
title: LocaleKeys.notice.tr(context: context),
|
||||
context,
|
||||
child: Utils.getWarningWidget(
|
||||
loadingText: "Are you sure you want to cancel this order?".needTranslation,
|
||||
isShowActionButtons: true,
|
||||
onCancelTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onConfirmTap: () async {
|
||||
Navigator.pop(context);
|
||||
LoaderBottomSheet.showLoader();
|
||||
|
||||
final requestModel = OrderUpdateRequestModel(
|
||||
presOrderID: order.iD,
|
||||
rejectionReason: "Cancelled by user",
|
||||
presOrderStatus: 4, // Cancelled status
|
||||
editedBy: 3,
|
||||
);
|
||||
|
||||
await hmgServicesViewModel.updateHhcPresOrder(
|
||||
requestModel: requestModel,
|
||||
onSuccess: (_) async {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
child: Column(
|
||||
children: [
|
||||
Utils.getSuccessWidget(loadingText: "Order has been cancelled successfully".needTranslation),
|
||||
SizedBox(height: 24.h),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomButton(
|
||||
height: 56.h,
|
||||
text: LocaleKeys.ok.tr(),
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
hmgServicesViewModel.getAllHhcOrders();
|
||||
},
|
||||
textColor: AppColors.whiteColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
isCloseButtonVisible: false,
|
||||
isDismissible: false,
|
||||
isFullScreen: false,
|
||||
);
|
||||
},
|
||||
onError: (error) {
|
||||
LoaderBottomSheet.hideLoader();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
callBackFunc: () {},
|
||||
isFullScreen: false,
|
||||
isCloseButtonVisible: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,154 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||
import 'package:sizer/sizer.dart';
|
||||
|
||||
class SearchRadiologyContent extends StatefulWidget {
|
||||
final List<String> radiologySuggestionsList;
|
||||
|
||||
const SearchRadiologyContent({super.key, required this.radiologySuggestionsList});
|
||||
|
||||
@override
|
||||
State<SearchRadiologyContent> createState() => _SearchRadiologyContentContentState();
|
||||
}
|
||||
|
||||
class _SearchRadiologyContentContentState extends State<SearchRadiologyContent> {
|
||||
TextEditingController searchEditingController = TextEditingController();
|
||||
List<String> filteredSuggestions = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
filteredSuggestions = List.from(widget.radiologySuggestionsList);
|
||||
|
||||
// Listen for changes in the search field
|
||||
searchEditingController.addListener(() {
|
||||
filterSuggestions();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
searchEditingController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void filterSuggestions() {
|
||||
final query = searchEditingController.text.toLowerCase();
|
||||
|
||||
if (query.isEmpty) {
|
||||
setState(() {
|
||||
filteredSuggestions = List.from(widget.radiologySuggestionsList);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
filteredSuggestions = widget.radiologySuggestionsList.where((suggestion) => suggestion.toLowerCase().contains(query)).toList();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CollapsingListView(
|
||||
title: LocaleKeys.radiology.tr(),
|
||||
isClose: true,
|
||||
bottomChild: Container(
|
||||
color: Colors.white,
|
||||
padding: EdgeInsets.all(ResponsiveExtension(20).h),
|
||||
child: CustomButton(
|
||||
text: LocaleKeys.search.tr(),
|
||||
icon: AppAssets.search_icon,
|
||||
iconColor: Colors.white,
|
||||
onPressed: () => Navigator.pop(context, searchEditingController.text),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24, right: 24, top: 24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextInputWidget(
|
||||
labelText: "Search radiology results",
|
||||
hintText: "Type test description",
|
||||
controller: searchEditingController,
|
||||
isEnable: true,
|
||||
prefix: null,
|
||||
autoFocus: false,
|
||||
isBorderAllowed: false,
|
||||
keyboardType: TextInputType.text,
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: ResponsiveExtension(9).h,
|
||||
horizontal: ResponsiveExtension(15).h,
|
||||
),
|
||||
),
|
||||
SizedBox(height: ResponsiveExtension(20).h),
|
||||
if (filteredSuggestions.isNotEmpty) ...[
|
||||
"Suggestions".toText16(isBold: true),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
SingleChildScrollView(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.only(left: 24, right: 24, bottom: 24, top: 16),
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.start,
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children: filteredSuggestions
|
||||
.map((label) => SuggestionChip(
|
||||
label: label,
|
||||
onTap: () {
|
||||
searchEditingController.text = label;
|
||||
},
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SuggestionChip extends StatelessWidget {
|
||||
final String label;
|
||||
final bool isSelected;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const SuggestionChip({
|
||||
super.key,
|
||||
required this.label,
|
||||
this.isSelected = false,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? AppColors.primaryRedColor : AppColors.whiteColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: label.toText12(
|
||||
color: isSelected ? Colors.white : Colors.black87,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
|
||||
class ServicesPage extends StatelessWidget {
|
||||
const ServicesPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.bgScaffoldColor,
|
||||
appBar: AppBar(
|
||||
title: const Text('Appointments'),
|
||||
backgroundColor: AppColors.bgScaffoldColor,
|
||||
),
|
||||
body: const Center(
|
||||
child: Text(
|
||||
'Appointments Page',
|
||||
style: TextStyle(fontSize: 24),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
|
||||
class FullScreenImageViewer extends StatelessWidget {
|
||||
final bool isSvg;
|
||||
final String path;
|
||||
|
||||
const FullScreenImageViewer({super.key, required this.isSvg, required this.path});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.black,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.close, color: AppColors.whiteColor),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.black,
|
||||
body: Center(
|
||||
child: InteractiveViewer(
|
||||
child: isSvg
|
||||
? SvgPicture.asset(path, width: double.infinity, fit: BoxFit.contain)
|
||||
: Image.asset(width: double.infinity, path, fit: BoxFit.contain),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
|
||||
class RadioListTileWidget<T> extends StatelessWidget {
|
||||
final T value;
|
||||
final T? groupValue;
|
||||
final String title;
|
||||
final Widget? subtitleWidget;
|
||||
final String? subtitle;
|
||||
final ValueChanged<T?> onChanged;
|
||||
final bool enabled;
|
||||
|
||||
const RadioListTileWidget({
|
||||
super.key,
|
||||
required this.value,
|
||||
required this.groupValue,
|
||||
required this.title,
|
||||
this.subtitleWidget,
|
||||
this.subtitle,
|
||||
required this.onChanged,
|
||||
this.enabled = true,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool isSelected = value == groupValue;
|
||||
|
||||
return InkWell(
|
||||
onTap: enabled ? () => onChanged(value) : null,
|
||||
child: Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 24.h,
|
||||
hasShadow: false,
|
||||
),
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.w, vertical: 6.h),
|
||||
padding: EdgeInsets.all(16.h),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 20.h,
|
||||
height: 20.h,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: isSelected ? AppColors.primaryRedColor : const Color(0xff9E9E9E),
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: isSelected
|
||||
? Center(
|
||||
child: Container(
|
||||
width: 10.h,
|
||||
height: 10.h,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColors.primaryRedColor,
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
SizedBox(width: 16.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
title.toText15(weight: FontWeight.w500, letterSpacing: -0.64),
|
||||
if (subtitleWidget != null) ...[subtitleWidget!],
|
||||
if (subtitle != null) ...[
|
||||
SizedBox(height: 4.h),
|
||||
title.toText13(
|
||||
weight: FontWeight.w400,
|
||||
color: AppColors.greyTextColor,
|
||||
letterSpacing: -0.56,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/int_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MoviesShimmerWidget extends StatelessWidget {
|
||||
const MoviesShimmerWidget({super.key});
|
||||
class CommonShimmerWidget extends StatelessWidget {
|
||||
const CommonShimmerWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||