Merge branch 'master' into haroon_dev
commit
bee4aafc44
@ -0,0 +1,134 @@
|
|||||||
|
// dart
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_details_resp_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart';
|
||||||
|
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
|
||||||
|
|
||||||
|
import 'models/resp_models/rate_appointment_resp_model.dart';
|
||||||
|
|
||||||
|
class AppointmentRatingViewModel extends ChangeNotifier {
|
||||||
|
final MyAppointmentsRepo myAppointmentsRepo;
|
||||||
|
final ErrorHandlerService errorHandlerService;
|
||||||
|
final AppState appState;
|
||||||
|
List<RateAppointmentRespModel> appointmentRatedList = [];
|
||||||
|
AppointmentDetails? appointmentDetails;
|
||||||
|
AppointmentRatingViewModel({
|
||||||
|
required this.myAppointmentsRepo,
|
||||||
|
required this.errorHandlerService,
|
||||||
|
required this.appState,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
String title = "";
|
||||||
|
String subTitle = "";
|
||||||
|
bool isRateClinic = false;
|
||||||
|
|
||||||
|
Future<void> getLastRatingAppointment({Function(dynamic)? onSuccess, Function(String)? onError}) async {
|
||||||
|
final result = await myAppointmentsRepo.getLastRatingAppointment();
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(failure) async => await errorHandlerService.handleError(failure: failure),
|
||||||
|
(apiResponse) {
|
||||||
|
if (apiResponse.messageStatus == 2) {
|
||||||
|
onError?.call(apiResponse.errorMessage ?? 'Unknown error');
|
||||||
|
} else if (apiResponse.messageStatus == 1) {
|
||||||
|
appointmentRatedList = apiResponse.data ?? [];
|
||||||
|
notifyListeners();
|
||||||
|
if (onSuccess != null) {
|
||||||
|
onSuccess(apiResponse.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getAppointmentDetails(int appointmentID, int projectID, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
|
||||||
|
final result = await myAppointmentsRepo.getAppointmentDetails(appointmentID, projectID);
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(failure) async => await errorHandlerService.handleError(failure: failure),
|
||||||
|
(apiResponse) {
|
||||||
|
if (apiResponse.messageStatus == 2) {
|
||||||
|
onError?.call(apiResponse.errorMessage ?? 'Unknown error');
|
||||||
|
} else if (apiResponse.messageStatus == 1) {
|
||||||
|
appointmentDetails = apiResponse.data ?? AppointmentDetails();
|
||||||
|
notifyListeners();
|
||||||
|
if (onSuccess != null) {
|
||||||
|
onSuccess(apiResponse.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Future<void> submitDoctorRating( {required int docRate, required String docNote,Function(dynamic)? onSuccess, Function(String)? onError}) async {
|
||||||
|
final result = await myAppointmentsRepo.sendDoctorRate(
|
||||||
|
docRate,
|
||||||
|
appointmentDetails!.appointmentNo!,
|
||||||
|
appointmentDetails!.projectID!,
|
||||||
|
appointmentDetails!.doctorID!,
|
||||||
|
appointmentDetails!.clinicID!,
|
||||||
|
docNote,
|
||||||
|
appointmentDetails!.appointmentDate!,
|
||||||
|
appointmentDetails!.doctorName,
|
||||||
|
appointmentDetails!.projectName,
|
||||||
|
appointmentDetails!.clinicName
|
||||||
|
);
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(failure) async => await errorHandlerService.handleError(failure: failure),
|
||||||
|
(apiResponse) {
|
||||||
|
if (apiResponse.messageStatus == 2) {
|
||||||
|
onError?.call(apiResponse.errorMessage ?? 'Unknown error');
|
||||||
|
} else if (apiResponse.messageStatus == 1) {
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
if (onSuccess != null) {
|
||||||
|
// onSuccess(apiResponse.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> submitClinicRating( { required int clinicRate, required String clinicNote, Function(dynamic)? onSuccess, Function(String)? onError}) async {
|
||||||
|
final result = await myAppointmentsRepo.sendAppointmentRate(
|
||||||
|
clinicRate,
|
||||||
|
appointmentDetails!.appointmentNo!,
|
||||||
|
appointmentDetails!.projectID!,
|
||||||
|
appointmentDetails!.doctorID!,
|
||||||
|
appointmentDetails!.clinicID!,
|
||||||
|
clinicNote
|
||||||
|
);
|
||||||
|
|
||||||
|
result.fold(
|
||||||
|
(failure) async => await errorHandlerService.handleError(failure: failure),
|
||||||
|
(apiResponse) {
|
||||||
|
if (apiResponse.messageStatus == 2) {
|
||||||
|
onError?.call(apiResponse.errorMessage ?? 'Unknown error');
|
||||||
|
} else if (apiResponse.messageStatus == 1) {
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
if (onSuccess != null) {
|
||||||
|
// onSuccess(apiResponse.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSubTitle(String value) {
|
||||||
|
this.subTitle = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTitle(String value) {
|
||||||
|
this.title = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
void setClinicOrDoctor(bool value){
|
||||||
|
this.isRateClinic = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
class AppointmentRate {
|
||||||
|
int? rate;
|
||||||
|
int? appointmentNo;
|
||||||
|
int? projectID;
|
||||||
|
int? doctorID;
|
||||||
|
int? clinicID;
|
||||||
|
String? note;
|
||||||
|
String? mobileNumber;
|
||||||
|
int? createdBy;
|
||||||
|
int? editedBy;
|
||||||
|
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;
|
||||||
|
|
||||||
|
AppointmentRate(
|
||||||
|
{this.rate,
|
||||||
|
this.appointmentNo,
|
||||||
|
this.projectID,
|
||||||
|
this.doctorID,
|
||||||
|
this.clinicID,
|
||||||
|
this.note,
|
||||||
|
this.mobileNumber,
|
||||||
|
this.createdBy,
|
||||||
|
this.editedBy,
|
||||||
|
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});
|
||||||
|
|
||||||
|
AppointmentRate.fromJson(Map<String, dynamic> json) {
|
||||||
|
rate = json['Rate'];
|
||||||
|
appointmentNo = json['AppointmentNo'];
|
||||||
|
projectID = json['ProjectID'];
|
||||||
|
doctorID = json['DoctorID'];
|
||||||
|
clinicID = json['ClinicID'];
|
||||||
|
note = json['Note'];
|
||||||
|
mobileNumber = json['MobileNumber'];
|
||||||
|
createdBy = json['CreatedBy'];
|
||||||
|
editedBy = json['EditedBy'];
|
||||||
|
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'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['Rate'] = this.rate;
|
||||||
|
data['AppointmentNo'] = this.appointmentNo;
|
||||||
|
data['ProjectID'] = this.projectID;
|
||||||
|
data['DoctorID'] = this.doctorID;
|
||||||
|
data['ClinicID'] = this.clinicID;
|
||||||
|
data['Note'] = this.note;
|
||||||
|
data['MobileNumber'] = this.mobileNumber;
|
||||||
|
data['CreatedBy'] = this.createdBy;
|
||||||
|
data['EditedBy'] = this.editedBy;
|
||||||
|
data['VersionID'] = this.versionID;
|
||||||
|
data['Channel'] = this.channel;
|
||||||
|
data['LanguageID'] = this.languageID;
|
||||||
|
data['IPAdress'] = this.iPAdress;
|
||||||
|
data['generalid'] = this.generalid;
|
||||||
|
data['PatientOutSA'] = this.patientOutSA;
|
||||||
|
data['SessionID'] = this.sessionID;
|
||||||
|
data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
|
||||||
|
data['DeviceTypeID'] = this.deviceTypeID;
|
||||||
|
data['PatientID'] = this.patientID;
|
||||||
|
data['TokenID'] = this.tokenID;
|
||||||
|
data['PatientTypeID'] = this.patientTypeID;
|
||||||
|
data['PatientType'] = this.patientType;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
class AppointmentDetails {
|
||||||
|
String? setupID;
|
||||||
|
int? projectID;
|
||||||
|
int? patientID;
|
||||||
|
int? appointmentNo;
|
||||||
|
int? clinicID;
|
||||||
|
int? doctorID;
|
||||||
|
dynamic startTime;
|
||||||
|
dynamic endTime;
|
||||||
|
dynamic appointmentDate;
|
||||||
|
dynamic clinicName;
|
||||||
|
dynamic doctorImageURL;
|
||||||
|
dynamic doctorName;
|
||||||
|
dynamic projectName;
|
||||||
|
|
||||||
|
AppointmentDetails(
|
||||||
|
{this.setupID,
|
||||||
|
this.projectID,
|
||||||
|
this.patientID,
|
||||||
|
this.appointmentNo,
|
||||||
|
this.clinicID,
|
||||||
|
this.doctorID,
|
||||||
|
this.startTime,
|
||||||
|
this.endTime,
|
||||||
|
this.appointmentDate,
|
||||||
|
this.clinicName,
|
||||||
|
this.doctorImageURL,
|
||||||
|
this.doctorName,
|
||||||
|
this.projectName});
|
||||||
|
|
||||||
|
AppointmentDetails.fromJson(Map<String, dynamic> json) {
|
||||||
|
setupID = json['SetupID'];
|
||||||
|
projectID = json['ProjectID'];
|
||||||
|
patientID = json['PatientID'];
|
||||||
|
appointmentNo = json['AppointmentNo'];
|
||||||
|
clinicID = json['ClinicID'];
|
||||||
|
doctorID = json['DoctorID'];
|
||||||
|
startTime = json['StartTime'];
|
||||||
|
endTime = json['EndTime'];
|
||||||
|
appointmentDate = json['AppointmentDate'];
|
||||||
|
clinicName = json['ClinicName'];
|
||||||
|
doctorImageURL = json['DoctorImageURL'];
|
||||||
|
doctorName = json['DoctorName'];
|
||||||
|
projectName = json['ProjectName'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['SetupID'] = this.setupID;
|
||||||
|
data['ProjectID'] = this.projectID;
|
||||||
|
data['PatientID'] = this.patientID;
|
||||||
|
data['AppointmentNo'] = this.appointmentNo;
|
||||||
|
data['ClinicID'] = this.clinicID;
|
||||||
|
data['DoctorID'] = this.doctorID;
|
||||||
|
data['StartTime'] = this.startTime;
|
||||||
|
data['EndTime'] = this.endTime;
|
||||||
|
data['AppointmentDate'] = this.appointmentDate;
|
||||||
|
data['ClinicName'] = this.clinicName;
|
||||||
|
data['DoctorImageURL'] = this.doctorImageURL;
|
||||||
|
data['DoctorName'] = this.doctorName;
|
||||||
|
data['ProjectName'] = this.projectName;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,160 @@
|
|||||||
|
class RateAppointmentRespModel {
|
||||||
|
String? setupID;
|
||||||
|
int? projectID;
|
||||||
|
int? appointmentNo;
|
||||||
|
String? appointmentDate;
|
||||||
|
String? appointmentDateN;
|
||||||
|
int? appointmentType;
|
||||||
|
String? bookDate;
|
||||||
|
int? patientType;
|
||||||
|
int? patientID;
|
||||||
|
int? clinicID;
|
||||||
|
int? doctorID;
|
||||||
|
String? endDate;
|
||||||
|
String? startTime;
|
||||||
|
String? endTime;
|
||||||
|
int? status;
|
||||||
|
int? visitType;
|
||||||
|
int? visitFor;
|
||||||
|
int? patientStatusType;
|
||||||
|
int? companyID;
|
||||||
|
int? bookedBy;
|
||||||
|
String? bookedOn;
|
||||||
|
int? confirmedBy;
|
||||||
|
String? confirmedOn;
|
||||||
|
int? arrivalChangedBy;
|
||||||
|
String? arrivedOn;
|
||||||
|
int? editedBy;
|
||||||
|
String? editedOn;
|
||||||
|
dynamic doctorName;
|
||||||
|
String? doctorNameN;
|
||||||
|
String? statusDesc;
|
||||||
|
String? statusDescN;
|
||||||
|
bool? vitalStatus;
|
||||||
|
dynamic vitalSignAppointmentNo;
|
||||||
|
int? episodeID;
|
||||||
|
String? doctorTitle;
|
||||||
|
bool? isAppoitmentLiveCare;
|
||||||
|
|
||||||
|
RateAppointmentRespModel(
|
||||||
|
{this.setupID,
|
||||||
|
this.projectID,
|
||||||
|
this.appointmentNo,
|
||||||
|
this.appointmentDate,
|
||||||
|
this.appointmentDateN,
|
||||||
|
this.appointmentType,
|
||||||
|
this.bookDate,
|
||||||
|
this.patientType,
|
||||||
|
this.patientID,
|
||||||
|
this.clinicID,
|
||||||
|
this.doctorID,
|
||||||
|
this.endDate,
|
||||||
|
this.startTime,
|
||||||
|
this.endTime,
|
||||||
|
this.status,
|
||||||
|
this.visitType,
|
||||||
|
this.visitFor,
|
||||||
|
this.patientStatusType,
|
||||||
|
this.companyID,
|
||||||
|
this.bookedBy,
|
||||||
|
this.bookedOn,
|
||||||
|
this.confirmedBy,
|
||||||
|
this.confirmedOn,
|
||||||
|
this.arrivalChangedBy,
|
||||||
|
this.arrivedOn,
|
||||||
|
this.editedBy,
|
||||||
|
this.editedOn,
|
||||||
|
this.doctorName,
|
||||||
|
this.doctorNameN,
|
||||||
|
this.statusDesc,
|
||||||
|
this.statusDescN,
|
||||||
|
this.vitalStatus,
|
||||||
|
this.vitalSignAppointmentNo,
|
||||||
|
this.episodeID,
|
||||||
|
this.doctorTitle,
|
||||||
|
this.isAppoitmentLiveCare});
|
||||||
|
|
||||||
|
RateAppointmentRespModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
try {
|
||||||
|
setupID = json['SetupID'];
|
||||||
|
projectID = json['ProjectID'];
|
||||||
|
appointmentNo = json['AppointmentNo'];
|
||||||
|
appointmentDate = json['AppointmentDate'];
|
||||||
|
appointmentDateN = json['AppointmentDateN'];
|
||||||
|
appointmentType = json['AppointmentType'];
|
||||||
|
bookDate = json['BookDate'];
|
||||||
|
patientType = json['PatientType'];
|
||||||
|
patientID = json['PatientID'];
|
||||||
|
clinicID = json['ClinicID'];
|
||||||
|
doctorID = json['DoctorID'];
|
||||||
|
endDate = json['EndDate'];
|
||||||
|
startTime = json['StartTime'];
|
||||||
|
endTime = json['EndTime'];
|
||||||
|
status = json['Status'];
|
||||||
|
visitType = json['VisitType'];
|
||||||
|
visitFor = json['VisitFor'];
|
||||||
|
patientStatusType = json['PatientStatusType'];
|
||||||
|
companyID = json['CompanyID'];
|
||||||
|
bookedBy = json['BookedBy'];
|
||||||
|
bookedOn = json['BookedOn'];
|
||||||
|
confirmedBy = json['ConfirmedBy'];
|
||||||
|
confirmedOn = json['ConfirmedOn'];
|
||||||
|
arrivalChangedBy = json['ArrivalChangedBy'];
|
||||||
|
arrivedOn = json['ArrivedOn'];
|
||||||
|
editedBy = json['EditedBy'];
|
||||||
|
editedOn = json['EditedOn'];
|
||||||
|
doctorName = json['DoctorName'];
|
||||||
|
doctorNameN = json['DoctorNameN'];
|
||||||
|
statusDesc = json['StatusDesc'];
|
||||||
|
statusDescN = json['StatusDescN'];
|
||||||
|
vitalStatus = json['VitalStatus'];
|
||||||
|
vitalSignAppointmentNo = json['VitalSignAppointmentNo'];
|
||||||
|
episodeID = json['EpisodeID'];
|
||||||
|
doctorTitle = json['DoctorTitle'];
|
||||||
|
isAppoitmentLiveCare = json['IsAppoitmentLiveCare'];
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['SetupID'] = this.setupID;
|
||||||
|
data['ProjectID'] = this.projectID;
|
||||||
|
data['AppointmentNo'] = this.appointmentNo;
|
||||||
|
data['AppointmentDate'] = this.appointmentDate;
|
||||||
|
data['AppointmentDateN'] = this.appointmentDateN;
|
||||||
|
data['AppointmentType'] = this.appointmentType;
|
||||||
|
data['BookDate'] = this.bookDate;
|
||||||
|
data['PatientType'] = this.patientType;
|
||||||
|
data['PatientID'] = this.patientID;
|
||||||
|
data['ClinicID'] = this.clinicID;
|
||||||
|
data['DoctorID'] = this.doctorID;
|
||||||
|
data['EndDate'] = this.endDate;
|
||||||
|
data['StartTime'] = this.startTime;
|
||||||
|
data['EndTime'] = this.endTime;
|
||||||
|
data['Status'] = this.status;
|
||||||
|
data['VisitType'] = this.visitType;
|
||||||
|
data['VisitFor'] = this.visitFor;
|
||||||
|
data['PatientStatusType'] = this.patientStatusType;
|
||||||
|
data['CompanyID'] = this.companyID;
|
||||||
|
data['BookedBy'] = this.bookedBy;
|
||||||
|
data['BookedOn'] = this.bookedOn;
|
||||||
|
data['ConfirmedBy'] = this.confirmedBy;
|
||||||
|
data['ConfirmedOn'] = this.confirmedOn;
|
||||||
|
data['ArrivalChangedBy'] = this.arrivalChangedBy;
|
||||||
|
data['ArrivedOn'] = this.arrivedOn;
|
||||||
|
data['EditedBy'] = this.editedBy;
|
||||||
|
data['EditedOn'] = this.editedOn;
|
||||||
|
data['DoctorName'] = this.doctorName;
|
||||||
|
data['DoctorNameN'] = this.doctorNameN;
|
||||||
|
data['StatusDesc'] = this.statusDesc;
|
||||||
|
data['StatusDescN'] = this.statusDescN;
|
||||||
|
data['VitalStatus'] = this.vitalStatus;
|
||||||
|
data['VitalSignAppointmentNo'] = this.vitalSignAppointmentNo;
|
||||||
|
data['EpisodeID'] = this.episodeID;
|
||||||
|
data['DoctorTitle'] = this.doctorTitle;
|
||||||
|
data['IsAppoitmentLiveCare'] = this.isAppoitmentLiveCare;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,212 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.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/features/my_appointments/appointment_rating_view_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/rate_appointment/widget/doctor_row.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';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class RateAppointmentClinic extends StatefulWidget {
|
||||||
|
|
||||||
|
late final String? doctorNote;
|
||||||
|
late final int? doctorRate;
|
||||||
|
|
||||||
|
RateAppointmentClinic({this.doctorRate, this.doctorNote});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_RateAppointmentClinicState createState() => _RateAppointmentClinicState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RateAppointmentClinicState extends State<RateAppointmentClinic> {
|
||||||
|
final formKey = GlobalKey<FormState>();
|
||||||
|
String note = "";
|
||||||
|
int rating = 5;
|
||||||
|
AppointmentRatingViewModel? appointmentRatingViewModel;
|
||||||
|
MyAppointmentsViewModel? myAppointmentsViewModel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
|
||||||
|
appointmentRatingViewModel = Provider.of<AppointmentRatingViewModel>(context, listen: false);
|
||||||
|
|
||||||
|
// Make the sheet a fixed height and keep content scrollable while pinning buttons to bottom
|
||||||
|
final sheetHeight = ResponsiveExtension.screenHeight * 0.60;
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
height: sheetHeight,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Scrollable content
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 0.0, left: 0, right: 0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
// Doctor row
|
||||||
|
Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||||
|
color: AppColors.whiteColor,
|
||||||
|
borderRadius: 24.r,
|
||||||
|
hasShadow: false,
|
||||||
|
),
|
||||||
|
child: BuildDoctorRow(
|
||||||
|
isForClinic: true,
|
||||||
|
appointmentDetails: appointmentRatingViewModel!.appointmentDetails,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Rate clinic box
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||||
|
color: AppColors.whiteColor,
|
||||||
|
borderRadius: 24.r,
|
||||||
|
hasShadow: false,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
|
||||||
|
"Rate Clinic".needTranslation.toText16(isBold: true),
|
||||||
|
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
...List.generate(
|
||||||
|
5,
|
||||||
|
(index) => rating == (index + 1)
|
||||||
|
? Container(
|
||||||
|
margin: EdgeInsets.only(left: 3.0, right: 3.0),
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
rating = index + 1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
iconSize: 35,
|
||||||
|
icon: SvgPicture.asset('assets/images/svg/rate_${index + 1}.svg', colorFilter: getColors(rating)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
rating = index + 1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
iconSize: 35,
|
||||||
|
icon: SvgPicture.asset('assets/images/svg/rate_${index + 1}.svg'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 12),
|
||||||
|
|
||||||
|
// Extra content area (keeps any other widgets that were previously below)
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 20),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// Placeholder for in-content widgets if needed in future
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Add bottom spacing so last content isn't obscured by the fixed buttons
|
||||||
|
SizedBox(height: 12),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
),
|
||||||
|
|
||||||
|
// Bottom action buttons pinned to bottom of the sheet
|
||||||
|
SafeArea(
|
||||||
|
top: false,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric( vertical: 12.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: CustomButton(
|
||||||
|
text: "Back".needTranslation,
|
||||||
|
backgroundColor: Color(0xffFEE9EA),
|
||||||
|
borderColor: Color(0xffFEE9EA),
|
||||||
|
textColor: Color(0xffED1C2B),
|
||||||
|
onPressed: () {
|
||||||
|
appointmentRatingViewModel!.setTitle("Rate Doctor".needTranslation);
|
||||||
|
appointmentRatingViewModel!.setSubTitle("How was your last visit with doctor?".needTranslation);
|
||||||
|
appointmentRatingViewModel!.setClinicOrDoctor(false);
|
||||||
|
setState(() {
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: CustomButton(
|
||||||
|
text: "Submit".needTranslation,
|
||||||
|
onPressed: () {
|
||||||
|
|
||||||
|
submitRating();
|
||||||
|
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorFilter getColors(int rating){
|
||||||
|
|
||||||
|
switch(rating){
|
||||||
|
case 5:
|
||||||
|
return ColorFilter.mode(AppColors.bgGreenColor, BlendMode.srcIn);
|
||||||
|
case 4:
|
||||||
|
return ColorFilter.mode(Colors.greenAccent, BlendMode.srcIn);
|
||||||
|
case 3:
|
||||||
|
return ColorFilter.mode(AppColors.warningLightColor, BlendMode.srcIn);
|
||||||
|
case 2:
|
||||||
|
return ColorFilter.mode(Colors.orange, BlendMode.srcIn);
|
||||||
|
case 1:
|
||||||
|
return ColorFilter.mode(AppColors.primaryRedColor, BlendMode.srcIn);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ColorFilter.mode(AppColors.greyColor, BlendMode.srcIn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submitRating() async{
|
||||||
|
LoaderBottomSheet.showLoader();
|
||||||
|
await appointmentRatingViewModel!.submitDoctorRating(docRate: widget.doctorRate!, docNote: widget.doctorNote!);
|
||||||
|
await appointmentRatingViewModel!.submitClinicRating(clinicRate: rating, clinicNote: note);
|
||||||
|
LoaderBottomSheet.hideLoader();
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,225 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
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/features/book_appointments/book_appointments_view_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/my_appointments/appointment_rating_view_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/rate_appointment/rate_appointment_clinic.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/rate_appointment/widget/doctor_row.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/transitions/fade_page.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class RateAppointmentDoctor extends StatefulWidget {
|
||||||
|
|
||||||
|
bool isFromRegistration;
|
||||||
|
|
||||||
|
RateAppointmentDoctor({Key? key, this.isFromRegistration = false}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_RateAppointmentDoctorState createState() => _RateAppointmentDoctorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
|
||||||
|
final formKey = GlobalKey<FormState>();
|
||||||
|
String note = "";
|
||||||
|
int rating = 5;
|
||||||
|
|
||||||
|
// ProjectViewModel? projectViewModel;
|
||||||
|
AppointmentRatingViewModel? appointmentRatingViewModel;
|
||||||
|
MyAppointmentsViewModel? myAppointmentsViewModel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
|
||||||
|
appointmentRatingViewModel = Provider.of<AppointmentRatingViewModel>(context, listen: false);
|
||||||
|
|
||||||
|
final sheetHeight = ResponsiveExtension.screenHeight * 0.60;
|
||||||
|
|
||||||
|
return Selector<AppointmentRatingViewModel, bool>(
|
||||||
|
selector: (_, vm) => vm.isRateClinic,
|
||||||
|
builder: (context, isRateClinic, child) => isRateClinic
|
||||||
|
? RateAppointmentClinic(doctorNote: note, doctorRate: rating,)
|
||||||
|
: SizedBox(
|
||||||
|
height: sheetHeight,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Scrollable main content
|
||||||
|
Expanded(
|
||||||
|
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 0.0, left: 0, right: 0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
// Doctor row
|
||||||
|
Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||||
|
color: AppColors.whiteColor,
|
||||||
|
borderRadius: 24.r,
|
||||||
|
hasShadow: false,
|
||||||
|
),
|
||||||
|
child: BuildDoctorRow(
|
||||||
|
isForClinic: false,
|
||||||
|
appointmentDetails: appointmentRatingViewModel!.appointmentDetails,
|
||||||
|
)),
|
||||||
|
|
||||||
|
SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Rating box
|
||||||
|
Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||||
|
color: AppColors.whiteColor,
|
||||||
|
borderRadius: 24.r,
|
||||||
|
hasShadow: false,
|
||||||
|
),
|
||||||
|
width: double.infinity,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
|
||||||
|
"Please rate the doctor".needTranslation.toText16(isBold: true),
|
||||||
|
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
...List.generate(
|
||||||
|
5,
|
||||||
|
(index) => AnimatedSwitcher(
|
||||||
|
duration: Duration(milliseconds: 1000),
|
||||||
|
switchInCurve: Curves.elasticOut,
|
||||||
|
switchOutCurve: Curves.elasticIn,
|
||||||
|
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||||
|
return ScaleTransition(child: child, scale: animation);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
key: ValueKey<int>(rating),
|
||||||
|
child: IconButton(
|
||||||
|
iconSize: 45.0,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
rating = index + 1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
color: rating >= (index + 1)
|
||||||
|
? Color.fromRGBO(255, 186, 0, 1.0)
|
||||||
|
: Colors.grey[400],
|
||||||
|
icon: Icon(rating >= (index + 1) ? Icons.star : Icons.star)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 12),
|
||||||
|
|
||||||
|
// Note text field
|
||||||
|
Container(
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||||
|
color: AppColors.whiteColor,
|
||||||
|
borderRadius: 24.r,
|
||||||
|
hasShadow: false,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
child: TextField(
|
||||||
|
|
||||||
|
maxLines: 4,
|
||||||
|
decoration: InputDecoration.collapsed(
|
||||||
|
hintText: "Notes".needTranslation,
|
||||||
|
hintStyle: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xff2B353E),
|
||||||
|
letterSpacing: -0.64,
|
||||||
|
height: 23 / 16)),
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
note = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
))),
|
||||||
|
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
),
|
||||||
|
|
||||||
|
// Bottom action buttons pinned to bottom
|
||||||
|
SafeArea(
|
||||||
|
top: false,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: CustomButton(
|
||||||
|
text: "Later".needTranslation,
|
||||||
|
backgroundColor: Color(0xffFEE9EA),
|
||||||
|
borderColor: Color(0xffFEE9EA),
|
||||||
|
textColor: Color(0xffED1C2B),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: CustomButton(
|
||||||
|
text: "Next".needTranslation,
|
||||||
|
onPressed: () {
|
||||||
|
// Set up clinic rating and show clinic rating view
|
||||||
|
appointmentRatingViewModel!.setTitle("Rate Clinic".needTranslation);
|
||||||
|
appointmentRatingViewModel!.setSubTitle("How was your appointment?".needTranslation);
|
||||||
|
appointmentRatingViewModel!.setClinicOrDoctor(true);
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
// DoctorList getDoctorObject(AppointmentRateViewModel model) {
|
||||||
|
// DoctorList doctor = new DoctorList();
|
||||||
|
//
|
||||||
|
// doctor.name = model.appointmentDetails.doctorName;
|
||||||
|
// doctor.doctorImageURL = model.appointmentDetails.doctorImageURL;
|
||||||
|
// doctor.clinicName = model.appointmentDetails.clinicName;
|
||||||
|
// doctor.projectName = model.appointmentDetails.projectName;
|
||||||
|
// doctor.date = model.appointmentDetails.appointmentDate;
|
||||||
|
// doctor.actualDoctorRate = 5;
|
||||||
|
//
|
||||||
|
// return doctor;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/date_util.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/features/my_appointments/appointment_rating_view_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_details_resp_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/rate_appointment_resp_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||||
|
|
||||||
|
class BuildDoctorRow extends StatelessWidget {
|
||||||
|
bool isForClinic = false;
|
||||||
|
AppointmentDetails? appointmentDetails;
|
||||||
|
|
||||||
|
BuildDoctorRow({super.key, required this.isForClinic, this.appointmentDetails});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
return Padding(padding: EdgeInsets.all(16),child:Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Image.network(
|
||||||
|
isForClinic ? 'https://hmgwebservices.com/Images/Hospitals/${appointmentDetails!.projectID}.jpg' : appointmentDetails!.doctorImageURL ,
|
||||||
|
width: 63.h,
|
||||||
|
height: 63.h,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
).circle(100),
|
||||||
|
SizedBox(width: 16.h),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
(isForClinic ? appointmentDetails!.projectName : appointmentDetails!.doctorName)!.toString()
|
||||||
|
.toText16(isBold: true, maxlines: 1),
|
||||||
|
|
||||||
|
SizedBox(height: 8.h),
|
||||||
|
|
||||||
|
|
||||||
|
isForClinic ? Wrap(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
spacing: 3.h,
|
||||||
|
runSpacing: 4.h,
|
||||||
|
children: [
|
||||||
|
AppCustomChipWidget(
|
||||||
|
|
||||||
|
labelText:
|
||||||
|
appointmentDetails!.clinicName.toString(),
|
||||||
|
|
||||||
|
),
|
||||||
|
AppCustomChipWidget(
|
||||||
|
icon: AppAssets.ic_date_filter,
|
||||||
|
labelText:
|
||||||
|
DateUtil.formatDateToDate(DateUtil.convertStringToDate(appointmentDetails!.appointmentDate), false),
|
||||||
|
|
||||||
|
),
|
||||||
|
|
||||||
|
AppCustomChipWidget(
|
||||||
|
icon: AppAssets.appointment_time_icon,
|
||||||
|
labelText:
|
||||||
|
appointmentDetails!.startTime.substring(0, appointmentDetails!.startTime.length - 3),
|
||||||
|
|
||||||
|
),
|
||||||
|
|
||||||
|
]
|
||||||
|
) : Wrap(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
spacing: 3.h,
|
||||||
|
runSpacing: 4.h,
|
||||||
|
children: [
|
||||||
|
AppCustomChipWidget(
|
||||||
|
|
||||||
|
labelText:
|
||||||
|
appointmentDetails!.projectName.toString(),
|
||||||
|
|
||||||
|
|
||||||
|
),
|
||||||
|
AppCustomChipWidget(
|
||||||
|
|
||||||
|
labelText:
|
||||||
|
appointmentDetails!.clinicName.toString(),
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue