Merge pull request 'dev_sultan' (#119) from dev_sultan into master
Reviewed-on: https://34.17.182.140/Haroon6138/HMG_Patient_App_New/pulls/119pull/123/head
commit
ceeeea59e3
@ -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