fixed parking qr

pull/152/head
Fatimah.Alshammari 2 months ago
parent 11de01e079
commit 8ae81d2b24

@ -207,7 +207,7 @@ var GET_APPOINTMENT_DETAILS_BY_NO = 'Services/MobileNotifications.svc/REST/GetAp
var NEW_RATE_APPOINTMENT_URL = "Services/Doctors.svc/REST/AppointmentsRating_InsertAppointmentRate"; var NEW_RATE_APPOINTMENT_URL = "Services/Doctors.svc/REST/AppointmentsRating_InsertAppointmentRate";
var NEW_RATE_DOCTOR_URL = "Services/Doctors.svc/REST/DoctorsRating_InsertDoctorRate"; var NEW_RATE_DOCTOR_URL = "Services/Doctors.svc/REST/DoctorsRating_InsertDoctorRate";
var GET_QR_PARKING = 'Services/SWP.svc/REST/GetQRParkingByID'; //var GET_QR_PARKING = 'Services/SWP.svc/REST/GetQRParkingByID';
//URL to get clinic list //URL to get clinic list
var GET_CLINICS_LIST_URL = "Services/lists.svc/REST/GetClinicCentralized"; var GET_CLINICS_LIST_URL = "Services/lists.svc/REST/GetClinicCentralized";
@ -700,7 +700,7 @@ var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatb
class ApiConsts { class ApiConsts {
static const maxSmallScreen = 660; static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@ -828,6 +828,7 @@ class ApiConsts {
static final String getTermsConditions = 'Services/Patients.svc/Rest/GetUserTermsAndConditions'; static final String getTermsConditions = 'Services/Patients.svc/Rest/GetUserTermsAndConditions';
static final String getMonthlyReports = 'Services/Patients.svc/Rest/UpdatePateintHealthSummaryReport'; static final String getMonthlyReports = 'Services/Patients.svc/Rest/UpdatePateintHealthSummaryReport';
static final String updatePatientEmail = 'Services/Patients.svc/Rest/UpdatePateintEmail'; static final String updatePatientEmail = 'Services/Patients.svc/Rest/UpdatePateintEmail';
static final String getQrParkingDetails = 'Services/SWP.svc/REST/GetQRParkingByID';
// Ancillary Order Apis // Ancillary Order Apis
static final String getOnlineAncillaryOrderList = 'Services/Doctors.svc/REST/GetOnlineAncillaryOrderList'; static final String getOnlineAncillaryOrderList = 'Services/Doctors.svc/REST/GetOnlineAncillaryOrderList';

@ -41,6 +41,7 @@ import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart'; import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
import 'package:hmg_patient_app_new/features/qr_parking/qr_parking_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_provider.dart'; import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_provider.dart';
@ -63,6 +64,7 @@ import 'package:local_auth/local_auth.dart';
import 'package:logger/web.dart'; import 'package:logger/web.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../features/qr_parking/qr_parking_view_model.dart';
import '../presentation/health_calculators_and_converts/health_calculator_view_model.dart'; import '../presentation/health_calculators_and_converts/health_calculator_view_model.dart';
import '../features/active_prescriptions/active_prescriptions_repo.dart'; import '../features/active_prescriptions/active_prescriptions_repo.dart';
@ -140,6 +142,15 @@ class AppDependencies {
getIt.registerFactory<TermsConditionsViewModel>(() => TermsConditionsViewModel(termsConditionsRepo: getIt<TermsConditionsRepo>(), errorHandlerService: getIt<ErrorHandlerService>(), getIt.registerFactory<TermsConditionsViewModel>(() => TermsConditionsViewModel(termsConditionsRepo: getIt<TermsConditionsRepo>(), errorHandlerService: getIt<ErrorHandlerService>(),
),); ),);
getIt.registerLazySingleton<MonthlyReportsRepo>(() => MonthlyReportsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<MonthlyReportsRepo>(() => MonthlyReportsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<QrParkingRepo>(() => QrParkingRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerFactory<QrParkingViewModel>(
() => QrParkingViewModel(
qrParkingRepo: getIt<QrParkingRepo>(),
errorHandlerService: getIt<ErrorHandlerService>(),
cacheService: getIt<CacheService>(),
),
);
// ViewModels // ViewModels
// Global/shared VMs LazySingleton // Global/shared VMs LazySingleton
@ -245,6 +256,14 @@ class AppDependencies {
activePrescriptionsRepo: getIt() activePrescriptionsRepo: getIt()
), ),
); );
getIt.registerFactory<QrParkingViewModel>(
() => QrParkingViewModel(
qrParkingRepo: getIt<QrParkingRepo>(),
errorHandlerService: getIt<ErrorHandlerService>(),
cacheService: getIt<CacheService>(),
),
);
// Screen-specific VMs Factory // Screen-specific VMs Factory
// getIt.registerFactory<BookAppointmentsViewModel>( // getIt.registerFactory<BookAppointmentsViewModel>(

@ -0,0 +1,183 @@
class QrParkingResponseModel {
dynamic totalRecords;
dynamic nRowID;
int? qRParkingID;
String? description;
String? descriptionN;
dynamic qRCompare;
dynamic qRValue;
String? imagePath;
bool? isActive;
int? parkingID;
int? branchID;
int? companyID;
int? buildingID;
int? rowID;
int? gateID;
int? floorID;
dynamic imagePath1;
int? createdBy;
String? createdOn;
dynamic editedBy;
dynamic editedOn;
String? parkingDescription;
String? parkingDescriptionN;
String? gateDescription;
String? gateDescriptionN;
String? branchDescription;
String? branchDescriptionN;
String? companyDescription;
String? companyDescriptionN;
String? rowDescription;
String? rowDescriptionN;
String? floorDescription;
String? floorDescriptionN;
String? buildingDescription;
String? buildingDescriptionN;
String? qRParkingCode;
String? parkingCode;
double? latitude;
double? longitude;
String? qRImageStr;
QrParkingResponseModel({
this.totalRecords,
this.nRowID,
this.qRParkingID,
this.description,
this.descriptionN,
this.qRCompare,
this.qRValue,
this.imagePath,
this.isActive,
this.parkingID,
this.branchID,
this.companyID,
this.buildingID,
this.rowID,
this.gateID,
this.floorID,
this.imagePath1,
this.createdBy,
this.createdOn,
this.editedBy,
this.editedOn,
this.parkingDescription,
this.parkingDescriptionN,
this.gateDescription,
this.gateDescriptionN,
this.branchDescription,
this.branchDescriptionN,
this.companyDescription,
this.companyDescriptionN,
this.rowDescription,
this.rowDescriptionN,
this.floorDescription,
this.floorDescriptionN,
this.buildingDescription,
this.buildingDescriptionN,
this.qRParkingCode,
this.parkingCode,
this.latitude,
this.longitude,
this.qRImageStr,
});
QrParkingResponseModel.fromJson(Map<String, dynamic> json) {
totalRecords = json['TotalRecords'];
nRowID = json['nRowID'];
qRParkingID = json['QRParkingID'];
description = json['Description'];
descriptionN = json['DescriptionN'];
qRCompare = json['QRCompare'];
qRValue = json['QRValue'];
imagePath = json['ImagePath'];
isActive = json['IsActive'];
parkingID = json['ParkingID'];
branchID = json['BranchID'];
companyID = json['CompanyID'];
buildingID = json['BuildingID'];
rowID = json['RowID'];
gateID = json['GateID'];
floorID = json['FloorID'];
imagePath1 = json['ImagePath1'];
createdBy = json['CreatedBy'];
createdOn = json['CreatedOn'];
editedBy = json['EditedBy'];
editedOn = json['EditedOn'];
parkingDescription = json['ParkingDescription'];
parkingDescriptionN = json['ParkingDescriptionN'];
gateDescription = json['GateDescription'];
gateDescriptionN = json['GateDescriptionN'];
branchDescription = json['BranchDescription'];
branchDescriptionN = json['BranchDescriptionN'];
companyDescription = json['CompanyDescription'];
companyDescriptionN = json['CompanyDescriptionN'];
rowDescription = json['RowDescription'];
rowDescriptionN = json['RowDescriptionN'];
floorDescription = json['FloorDescription'];
floorDescriptionN = json['FloorDescriptionN'];
buildingDescription = json['BuildingDescription'];
buildingDescriptionN = json['BuildingDescriptionN'];
qRParkingCode = json['QRParkingCode'];
parkingCode = json['ParkingCode'];
latitude = _toDouble(json['Latitude']);
longitude = _toDouble(json['Longitude']);
qRImageStr = json['QRImageStr'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['TotalRecords'] = totalRecords;
data['nRowID'] = nRowID;
data['QRParkingID'] = qRParkingID;
data['Description'] = description;
data['DescriptionN'] = descriptionN;
data['QRCompare'] = qRCompare;
data['QRValue'] = qRValue;
data['ImagePath'] = imagePath;
data['IsActive'] = isActive;
data['ParkingID'] = parkingID;
data['BranchID'] = branchID;
data['CompanyID'] = companyID;
data['BuildingID'] = buildingID;
data['RowID'] = rowID;
data['GateID'] = gateID;
data['FloorID'] = floorID;
data['ImagePath1'] = imagePath1;
data['CreatedBy'] = createdBy;
data['CreatedOn'] = createdOn;
data['EditedBy'] = editedBy;
data['EditedOn'] = editedOn;
data['ParkingDescription'] = parkingDescription;
data['ParkingDescriptionN'] = parkingDescriptionN;
data['GateDescription'] = gateDescription;
data['GateDescriptionN'] = gateDescriptionN;
data['BranchDescription'] = branchDescription;
data['BranchDescriptionN'] = branchDescriptionN;
data['CompanyDescription'] = companyDescription;
data['CompanyDescriptionN'] = companyDescriptionN;
data['RowDescription'] = rowDescription;
data['RowDescriptionN'] = rowDescriptionN;
data['FloorDescription'] = floorDescription;
data['FloorDescriptionN'] = floorDescriptionN;
data['BuildingDescription'] = buildingDescription;
data['BuildingDescriptionN'] = buildingDescriptionN;
data['QRParkingCode'] = qRParkingCode;
data['ParkingCode'] = parkingCode;
data['Latitude'] = latitude;
data['Longitude'] = longitude;
data['QRImageStr'] = qRImageStr;
return data;
}
static double? _toDouble(dynamic v) {
if (v == null) return null;
if (v is double) return v;
if (v is int) return v.toDouble();
return double.tryParse(v.toString());
}
}

@ -0,0 +1,74 @@
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/features/qr_parking/models/qr_parking_response_model.dart';
import '../../core/api/api_client.dart';
import '../../core/api_consts.dart';
import '../../core/common_models/generic_api_model.dart';
import '../../core/exceptions/api_failure.dart';
import '../../services/logger_service.dart';
abstract class QrParkingRepo {
Future<Either<Failure, GenericApiModel<List<QrParkingResponseModel>>>>
getQrParking({
required int qrParkingId,
});
}
class QrParkingRepoImp implements QrParkingRepo {
final ApiClient apiClient;
final LoggerService loggerService;
QrParkingRepoImp({
required this.loggerService,
required this.apiClient,
});
@override
Future<Either<Failure, GenericApiModel<List<QrParkingResponseModel>>>>
getQrParking({required int qrParkingId}) async {
try {
GenericApiModel<List<QrParkingResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getQrParkingDetails, // GetQRParkingByID
body: {'QRParkingID': qrParkingId},
onFailure: (error, statusCode,
{messageStatus, failureType}) {
failure = failureType ??
StatusCodeFailure("$error ($statusCode)");
},
onSuccess: (response, statusCode,
{messageStatus, errorMessage}) {
final list =
(response['List_SWP_QRParkingModel'] as List?) ?? [];
final res = list
.map((e) => QrParkingResponseModel.fromJson(
Map<String, dynamic>.from(e),
))
.toList();
apiResponse = GenericApiModel<List<QrParkingResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: res,
);
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) {
return Left(ServerFailure("Unknown error"));
}
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -0,0 +1,144 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:barcode_scan2/barcode_scan2.dart';
import 'package:hmg_patient_app_new/features/qr_parking/qr_parking_repo.dart';
import '../../services/cache_service.dart';
import '../../services/error_handler_service.dart';
import 'models/qr_parking_response_model.dart';
class QrParkingViewModel extends ChangeNotifier {
final QrParkingRepo qrParkingRepo;
final ErrorHandlerService errorHandlerService;
final CacheService cacheService;
String IS_GO_TO_PARKING = 'IS_GO_TO_PARKING';
bool isLoading = false;
String? error;
bool isSavePark = false;
QrParkingResponseModel? qrParkingModel;
List<QrParkingResponseModel> qrParkingList = [];
QrParkingViewModel({
required this.qrParkingRepo,
required this.errorHandlerService,
required this.cacheService,
});
Future<QrParkingResponseModel?> scanAndGetParking() async {
try {
error = null;
isLoading = true;
notifyListeners();
final result = await BarcodeScanner.scan();
if (result.type != ResultType.Barcode) {
isLoading = false;
notifyListeners();
return null;
}
final raw = result.rawContent.trim();
if (raw.isEmpty) {
error = "Invalid QR Code";
isLoading = false;
notifyListeners();
return null;
}
final qrParkingId = _extractQrParkingId(raw);
if (qrParkingId == null) {
error = "Invalid QR Code";
isLoading = false;
notifyListeners();
return null;
}
final apiResult =
await qrParkingRepo.getQrParking(qrParkingId: qrParkingId);
final model = apiResult.fold(
(failure) {
errorHandlerService.handleError(failure: failure);
error = failure.toString();
return null;
},
(apiResponse) {
qrParkingList = apiResponse.data ?? [];
if (qrParkingList.isNotEmpty) {
return qrParkingList.first;
}
error = "Invalid Qr Code";
return null;
},
);
if (model != null) {
qrParkingModel = model;
isSavePark = true;
await cacheService.saveObject(
key: IS_GO_TO_PARKING,
value: model.toJson(),
);
}
isLoading = false;
notifyListeners();
return model;
} catch (e) {
error = "Scan error";
isLoading = false;
notifyListeners();
return null;
}
}
/// Load saved parking
Future<void> getIsSaveParking() async {
isLoading = true;
notifyListeners();
final parking =
await cacheService.getObject(key: IS_GO_TO_PARKING);
if (parking != null) {
isSavePark = true;
qrParkingModel = QrParkingResponseModel.fromJson(
Map<String, dynamic>.from(parking),
);
} else {
isSavePark = false;
qrParkingModel = null;
}
isLoading = false;
notifyListeners();
}
/// Reset parking
Future<void> clearParking() async {
await cacheService.remove(key: IS_GO_TO_PARKING);
isSavePark = false;
qrParkingModel = null;
notifyListeners();
}
int? _extractQrParkingId(String raw) {
try {
if (raw.startsWith("{")) {
final data = jsonDecode(raw);
return int.tryParse(data['QRParkingID'].toString());
}
return int.tryParse(raw);
} catch (_) {
return null;
}
}
}

@ -21,7 +21,7 @@ import 'package:hmg_patient_app_new/presentation/hmg_services/services_view.dart
import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart'; import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/presentation/parking/paking_page.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
@ -31,10 +31,9 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:hmg_patient_app_new/presentation/parking/paking_page.dart';
import '../../core/dependencies.dart' show getIt; import '../../core/dependencies.dart' show getIt;
import '../../features/qr_parking/qr_parking_view_model.dart';
class ServicesPage extends StatelessWidget { class ServicesPage extends StatelessWidget {
ServicesPage({super.key}); ServicesPage({super.key});
@ -89,30 +88,18 @@ class ServicesPage extends StatelessWidget {
route: null, onTap: () async { route: null, onTap: () async {
LoaderBottomSheet.showLoader(loadingText: "Fetching Data..."); LoaderBottomSheet.showLoader(loadingText: "Fetching Data...");
await bloodDonationViewModel.getRegionSelectedClinics(onSuccess: (val) async { await bloodDonationViewModel.getRegionSelectedClinics(onSuccess: (val) async {
// await bloodDonationViewModel.getPatientBloodGroupDetails(onSuccess: (val) { await bloodDonationViewModel.getPatientBloodGroupDetails(onSuccess: (val) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
Navigator.of(GetIt.instance<NavigationService>().navigatorKey.currentContext!).push( Navigator.of(GetIt.instance<NavigationService>().navigatorKey.currentContext!).push(
CustomPageRoute( CustomPageRoute(
page: BloodDonationPage(), page: BloodDonationPage(),
), ),
); );
// }, onError: (err) { });
// LoaderBottomSheet.hideLoader();
// });
}, onError: (err) { }, onError: (err) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
}); });
}), }),
HmgServicesComponentModel(
11,
"Covid 19 Test".needTranslation,
"".needTranslation,
AppAssets.covid19icon,
bgColor: AppColors.covid29Color,
true,
route: AppRoutes.covid19Test,
)
// HmgServicesComponentModel( // HmgServicesComponentModel(
// 3, // 3,
// "Home Health Care".needTranslation, // "Home Health Care".needTranslation,
@ -176,30 +163,8 @@ class ServicesPage extends StatelessWidget {
route: AppRoutes.smartWatches, route: AppRoutes.smartWatches,
// route: AppRoutes.huaweiHealthExample, // route: AppRoutes.huaweiHealthExample,
), ),
// HmgServicesComponentModel(
// 12,
// "Latest News".needTranslation,
// "".needTranslation,
// AppAssets.news,
// true,
// bgColor: AppColors.bgGreenColor,
// textColor: AppColors.blackColor,
// route: "https://twitter.com/HMG",
// isExternalLink: true,
// ),
HmgServicesComponentModel(
12,
"Monthly Reports".needTranslation,
"".needTranslation,
AppAssets.report_icon,
true,
bgColor: AppColors.bgGreenColor,
textColor: AppColors.blackColor,
route: AppRoutes.monthlyReports,
),
]; ];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bloodDonationViewModel = Provider.of<BloodDonationViewModel>(context); bloodDonationViewModel = Provider.of<BloodDonationViewModel>(context);
@ -467,43 +432,37 @@ class ServicesPage extends StatelessWidget {
), ),
SizedBox(width: 16.w), SizedBox(width: 16.w),
Expanded( Expanded(
child: InkWell( child: Container(
onTap: () { decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
Navigator.push( color: AppColors.whiteColor,
context, borderRadius: 12.h,
MaterialPageRoute( hasShadow: false,
builder: (_) => ParkingPage(), ),
), child: Padding(
); padding: EdgeInsets.all(16.h),
}, child: Row(
child: Container( children: [
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( Utils.buildSvgWithAssets(
color: AppColors.whiteColor, icon: AppAssets.car_parking_icon,
borderRadius: 12.h, width: 32.w,
hasShadow: false, height: 32.h,
), fit: BoxFit.contain,
child: Padding( ),
padding: EdgeInsets.all(16.h), SizedBox(width: 8.w),
child: Row( "Car Parking".needTranslation.toText12(fontWeight: FontWeight.w500)
children: [ ],
Utils.buildSvgWithAssets( ).onPress(() {
icon: AppAssets.car_parking_icon, Navigator.push(
width: 32.w, context,
height: 32.h, MaterialPageRoute(
fit: BoxFit.contain, builder: (_) => ChangeNotifierProvider(
), create: (_) => getIt<QrParkingViewModel>(),
SizedBox(width: 8.w), child: const ParkingPage(),
"Car Parking".needTranslation.toText12(fontWeight: FontWeight.w500)
],
).onPress(() {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ParkingPage(),
), ),
); ),
}), );
),
}),
), ),
), ),
), ),

@ -1069,7 +1069,9 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
svgIcon: AppAssets.monthly_reports_icon, svgIcon: AppAssets.monthly_reports_icon,
isLargeText: true, isLargeText: true,
iconSize: 36.h, iconSize: 36.h,
), ).onPress(() {
Navigator.pushNamed(context, AppRoutes.monthlyReports);
}),
MedicalFileCard( MedicalFileCard(
label: "Medical Reports".needTranslation, label: "Medical Reports".needTranslation,
textColor: AppColors.blackColor, textColor: AppColors.blackColor,

@ -1,16 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hmg_patient_app_new/core/app_export.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/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/presentation/parking/parking_slot.dart'; import 'package:hmg_patient_app_new/presentation/parking/parking_slot.dart';
import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:provider/provider.dart';
import '../../features/qr_parking/qr_parking_view_model.dart';
import '../../theme/colors.dart'; import '../../theme/colors.dart';
import '../../widgets/appbar/app_bar_widget.dart'; import '../../widgets/appbar/app_bar_widget.dart';
import '../../widgets/routes/custom_page_route.dart'; import '../../widgets/routes/custom_page_route.dart';
class ParkingPage extends StatefulWidget { class ParkingPage extends StatefulWidget {
const ParkingPage({super.key}); const ParkingPage({super.key});
@ -19,10 +21,29 @@ class ParkingPage extends StatefulWidget {
} }
class _ParkingPageState extends State<ParkingPage> { class _ParkingPageState extends State<ParkingPage> {
String? scannedCode; Future<void> _readQR(BuildContext context) async {
final vm = context.read<QrParkingViewModel>();
final model = await vm.scanAndGetParking();
if (model == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(vm.error ?? "Invalid Qr Code")),
);
return;
}
Navigator.of(context).push(
CustomPageRoute(
page: ParkingSlot(model: model),
),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final vm = context.watch<QrParkingViewModel>(); // عشان loading
return Scaffold( return Scaffold(
backgroundColor: AppColors.scaffoldBgColor, backgroundColor: AppColors.scaffoldBgColor,
appBar: CustomAppBar( appBar: CustomAppBar(
@ -38,22 +59,24 @@ class _ParkingPageState extends State<ParkingPage> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text("Parking".needTranslation, Text(
style: TextStyle( "Parking".needTranslation,
color: AppColors.textColor, style: TextStyle(
fontSize: 27.f, color: AppColors.textColor,
fontWeight: FontWeight.w600)), fontSize: 27.f,
Container( fontWeight: FontWeight.w600,
decoration: RoundedRectangleBorder() ),
.toSmoothCornerDecoration( ),
color: AppColors.whiteColor, Container(
borderRadius: 24.r, decoration: RoundedRectangleBorder()
hasShadow: true, .toSmoothCornerDecoration(
), color: AppColors.whiteColor,
// margin: EdgeInsets.all(10), borderRadius: 24.r,
child: Padding( hasShadow: true,
padding: EdgeInsets.all(16.h), ),
child: Text( child: Padding(
padding: EdgeInsets.all(16.h),
child: Text(
"Dr. Sulaiman Al Habib hospital are conduction a test for the emerging corona" "Dr. Sulaiman Al Habib hospital are conduction a test for the emerging corona"
" virus and issuing travel certificates 24/7 in a short time and with high accuracy." " virus and issuing travel certificates 24/7 in a short time and with high accuracy."
" Those wishing to benefit from this service can visit one of Dr. Sulaiman Al Habib branches " " Those wishing to benefit from this service can visit one of Dr. Sulaiman Al Habib branches "
@ -62,19 +85,20 @@ class _ParkingPageState extends State<ParkingPage> {
"Those wishing to benefit from this service can visit one of Dr. Sulaiman Al Habib branches to conduct a corona test within few minutes.", "Those wishing to benefit from this service can visit one of Dr. Sulaiman Al Habib branches to conduct a corona test within few minutes.",
style: TextStyle( style: TextStyle(
color: AppColors.textColor, color: AppColors.textColor,
fontSize: 12, height: 1.4, fontWeight: FontWeight.w500), fontSize: 12,
), height: 1.4,
), fontWeight: FontWeight.w500,
).paddingOnly( top: 16, bottom: 16), ),
),
),
).paddingOnly(top: 16, bottom: 16),
], ],
), ),
), ),
), ),
/// Bottom button /// Bottom button
Container Container(
(
decoration: RoundedRectangleBorder() decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration( .toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
@ -93,13 +117,17 @@ class _ParkingPageState extends State<ParkingPage> {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
), ),
onPressed: () { onPressed: vm.isLoading ? null : () => _readQR(context),
Navigator.of(context).push( child: vm.isLoading
CustomPageRoute( ? const SizedBox(
page: ParkingSlot(), width: 22,
) ); height: 22,
}, child: CircularProgressIndicator(
child: Text( strokeWidth: 2,
color: Colors.white,
),
)
: const Text(
"Read Barcodes", "Read Barcodes",
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
@ -116,3 +144,4 @@ class _ParkingPageState extends State<ParkingPage> {
); );
} }
} }

@ -1,26 +1,106 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.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/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:hmg_patient_app_new/features/qr_parking/models/qr_parking_response_model.dart';
import '../../features/qr_parking/qr_parking_view_model.dart';
import '../../theme/colors.dart'; import '../../theme/colors.dart';
import '../../widgets/appbar/app_bar_widget.dart'; import '../../widgets/appbar/app_bar_widget.dart';
import '../../widgets/chip/app_custom_chip_widget.dart'; import '../../widgets/chip/app_custom_chip_widget.dart';
import 'package:maps_launcher/maps_launcher.dart';
import 'package:provider/provider.dart';
class ParkingSlot extends StatefulWidget { class ParkingSlot extends StatefulWidget {
const ParkingSlot({super.key}); final QrParkingResponseModel model;
const ParkingSlot({
super.key,
required this.model,
});
@override @override
State<ParkingSlot> createState() => _ParkingSlotState(); State<ParkingSlot> createState() => _ParkingSlotState();
} }
class _ParkingSlotState extends State<ParkingSlot> { class _ParkingSlotState extends State<ParkingSlot> {
String? scannedCode;
void _openDirection() {
final lat = widget.model.latitude;
final lng = widget.model.longitude;
final valid = lat != null &&
lng != null &&
!(lat == 0.0 && lng == 0.0) &&
lat >= -90 && lat <= 90 &&
lng >= -180 && lng <= 180;
if (!valid) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Parking location not available")),
);
return;
}
MapsLauncher.launchCoordinates(lat, lng);
}
Future<void> _resetDirection() async {
final vm = context.read<QrParkingViewModel>();
await vm.clearParking();
Navigator.of(context).popUntil((route) => route.isFirst);
}
DateTime? _parseDotNetDate(String? value) {
if (value == null || value.isEmpty) return null;
final regExp = RegExp(r'Date\((\d+)([+-]\d+)?\)');
final match = regExp.firstMatch(value);
if (match == null) return null;
final milliseconds = int.tryParse(match.group(1)!);
if (milliseconds == null) return null;
return DateTime.fromMillisecondsSinceEpoch(milliseconds, isUtc: true)
.toLocal();
}
String _formatPrettyDate(String? value) {
final date = _parseDotNetDate(value);
if (date == null) return '-';
const months = [
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
];
final day = date.day;
final month = months[date.month - 1];
final year = date.year;
return "$day $month $year";
}
String _formatPrettyTime(String? value) {
final date = _parseDotNetDate(value);
if (date == null) return '-';
int hour = date.hour;
final minute = date.minute.toString().padLeft(2, '0');
final isPM = hour >= 12;
final period = isPM ? 'PM' : 'AM';
hour = hour % 12;
if (hour == 0) hour = 12;
return "${hour.toString().padLeft(2, '0')}:$minute $period";
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -34,7 +114,7 @@ class _ParkingSlotState extends State<ParkingSlot> {
body: LayoutBuilder( body: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
final maxW = constraints.maxWidth; final maxW = constraints.maxWidth;
final contentW = maxW > 600 ? 600.0 : maxW; // حد أقصى للتابلت final contentW = maxW > 600 ? 600.0 : maxW;
return Align( return Align(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
@ -45,9 +125,11 @@ class _ParkingSlotState extends State<ParkingSlot> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
Container( Container(
width: double.infinity, width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: 24.r, borderRadius: 24.r,
hasShadow: true, hasShadow: true,
@ -58,7 +140,7 @@ class _ParkingSlotState extends State<ParkingSlot> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Parking Slot Details", "Parking Slot Details".needTranslation,
style: TextStyle( style: TextStyle(
fontSize: 16.f, fontSize: 16.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -70,10 +152,26 @@ class _ParkingSlotState extends State<ParkingSlot> {
spacing: 4, spacing: 4,
runSpacing: 4, runSpacing: 4,
children: [ children: [
AppCustomChipWidget(labelText: "Slot: B-24".needTranslation), AppCustomChipWidget(
AppCustomChipWidget(labelText: "Basement: Zone B".needTranslation), labelText:
AppCustomChipWidget(labelText: "Date: 16 Dec 2025".needTranslation), "Slot: ${widget.model.qRParkingCode ?? '-'}"
AppCustomChipWidget(labelText: "Parked Since: 10:32 AM".needTranslation), .needTranslation,
),
AppCustomChipWidget(
labelText:
"Basement: ${widget.model.floorDescription ?? '-'}"
.needTranslation,
),
AppCustomChipWidget(
labelText:
"Date: ${_formatPrettyDate(widget.model.createdOn)}"
.needTranslation,
),
AppCustomChipWidget(
labelText:
"Parked Since: ${_formatPrettyTime(widget.model.createdOn)}"
.needTranslation,
),
], ],
), ),
], ],
@ -93,42 +191,40 @@ class _ParkingSlotState extends State<ParkingSlot> {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
), ),
onPressed: () {}, onPressed: _openDirection,
child: Text( child: Text(
"Get Direction", "Get Direction".needTranslation,
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.white, color: AppColors.whiteColor,
), ),
), ),
), ),
), ),
const Spacer(), // const Spacer(),
SizedBox( // SizedBox(
width: double.infinity, // width: double.infinity,
height: 48.h, // height: 48.h,
child: OutlinedButton( // child: OutlinedButton(
style: OutlinedButton.styleFrom( // style: OutlinedButton.styleFrom(
side: BorderSide(color: AppColors.primaryRedColor), // side: BorderSide(color: AppColors.primaryRedColor),
shape: RoundedRectangleBorder( // shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), // borderRadius: BorderRadius.circular(10),
), // ),
), // ),
onPressed: () { // onPressed: _resetDirection,
// Reset direction logic // child: Text(
}, // "Reset Direction".needTranslation,
child: Text( // style: TextStyle(
"Reset Direction", // fontSize: 16,
style: TextStyle( // fontWeight: FontWeight.w600,
fontSize: 16, // color: AppColors.primaryRedColor,
fontWeight: FontWeight.w600, // ),
color: AppColors.primaryRedColor, // ),
), // ),
), // ),
),
),
], ],
), ),
), ),
@ -136,7 +232,8 @@ class _ParkingSlotState extends State<ParkingSlot> {
); );
}, },
), ),
); );
} }
} }

@ -24,12 +24,14 @@ import 'package:hmg_patient_app_new/presentation/symptoms_checker/user_info_sele
import 'package:hmg_patient_app_new/presentation/tele_consultation/zoom/call_screen.dart'; import 'package:hmg_patient_app_new/presentation/tele_consultation/zoom/call_screen.dart';
import 'package:hmg_patient_app_new/splashPage.dart'; import 'package:hmg_patient_app_new/splashPage.dart';
import '../features/qr_parking/qr_parking_view_model.dart';
import '../presentation/covid19test/covid19_landing_page.dart'; import '../presentation/covid19test/covid19_landing_page.dart';
import '../core/dependencies.dart'; import '../core/dependencies.dart';
import '../features/monthly_reports/monthly_reports_repo.dart'; import '../features/monthly_reports/monthly_reports_repo.dart';
import '../features/monthly_reports/monthly_reports_view_model.dart'; import '../features/monthly_reports/monthly_reports_view_model.dart';
import '../presentation/monthly_reports/monthly_reports_page.dart'; import '../presentation/monthly_reports/monthly_reports_page.dart';
import '../presentation/parking/paking_page.dart';
import '../services/error_handler_service.dart'; import '../services/error_handler_service.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -65,6 +67,7 @@ class AppRoutes {
static const String userInfoSelection = '/userInfoSelection'; static const String userInfoSelection = '/userInfoSelection';
static const String userInfoFlowManager = '/userInfoFlowManager'; static const String userInfoFlowManager = '/userInfoFlowManager';
static const String monthlyReports = '/monthlyReportsPage'; static const String monthlyReports = '/monthlyReportsPage';
static const String qrParking = '/qrParkingPage';
static Map<String, WidgetBuilder> get routes => { static Map<String, WidgetBuilder> get routes => {
initialRoute: (context) => SplashPage(), initialRoute: (context) => SplashPage(),
@ -92,7 +95,6 @@ class AppRoutes {
covid19Test: (context) => Covid19LandingPage(), covid19Test: (context) => Covid19LandingPage(),
// //
// healthCalculatorsPage: (context) => HealthCalculatorsPage(), // healthCalculatorsPage: (context) => HealthCalculatorsPage(),
// monthlyReports: (context) => MonthlyReportsPage()
monthlyReports: (context) => ChangeNotifierProvider( monthlyReports: (context) => ChangeNotifierProvider(
create: (_) => MonthlyReportsViewModel( create: (_) => MonthlyReportsViewModel(
monthlyReportsRepo: getIt<MonthlyReportsRepo>(), monthlyReportsRepo: getIt<MonthlyReportsRepo>(),
@ -102,6 +104,12 @@ class AppRoutes {
), ),
healthCalculatorsPage: (context) => HealthCalculatorsPage(type: HealthCalConEnum.calculator), healthCalculatorsPage: (context) => HealthCalculatorsPage(type: HealthCalConEnum.calculator),
healthConvertersPage: (context) => HealthCalculatorsPage(type: HealthCalConEnum.converter) healthConvertersPage: (context) => HealthCalculatorsPage(type: HealthCalConEnum.converter),
}; qrParking: (context) => ChangeNotifierProvider<QrParkingViewModel>(
create: (_) => getIt<QrParkingViewModel>(),
child: const ParkingPage(),
),
};
} }

Loading…
Cancel
Save