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_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
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 {
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
@ -828,6 +828,7 @@ class ApiConsts {
static final String getTermsConditions = 'Services/Patients.svc/Rest/GetUserTermsAndConditions';
static final String getMonthlyReports = 'Services/Patients.svc/Rest/UpdatePateintHealthSummaryReport';
static final String updatePatientEmail = 'Services/Patients.svc/Rest/UpdatePateintEmail';
static final String getQrParkingDetails = 'Services/SWP.svc/REST/GetQRParkingByID';
// Ancillary Order Apis
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_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_view_model.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: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 '../features/active_prescriptions/active_prescriptions_repo.dart';
@ -140,6 +142,15 @@ class AppDependencies {
getIt.registerFactory<TermsConditionsViewModel>(() => TermsConditionsViewModel(termsConditionsRepo: getIt<TermsConditionsRepo>(), errorHandlerService: getIt<ErrorHandlerService>(),
),);
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
// Global/shared VMs LazySingleton
@ -245,6 +256,14 @@ class AppDependencies {
activePrescriptionsRepo: getIt()
),
);
getIt.registerFactory<QrParkingViewModel>(
() => QrParkingViewModel(
qrParkingRepo: getIt<QrParkingRepo>(),
errorHandlerService: getIt<ErrorHandlerService>(),
cacheService: getIt<CacheService>(),
),
);
// Screen-specific VMs Factory
// 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/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/parking/paking_page.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/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/routes/custom_page_route.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 '../../features/qr_parking/qr_parking_view_model.dart';
class ServicesPage extends StatelessWidget {
ServicesPage({super.key});
@ -89,30 +88,18 @@ class ServicesPage extends StatelessWidget {
route: null, onTap: () async {
LoaderBottomSheet.showLoader(loadingText: "Fetching Data...");
await bloodDonationViewModel.getRegionSelectedClinics(onSuccess: (val) async {
// await bloodDonationViewModel.getPatientBloodGroupDetails(onSuccess: (val) {
await bloodDonationViewModel.getPatientBloodGroupDetails(onSuccess: (val) {
LoaderBottomSheet.hideLoader();
Navigator.of(GetIt.instance<NavigationService>().navigatorKey.currentContext!).push(
CustomPageRoute(
page: BloodDonationPage(),
),
);
// }, onError: (err) {
// LoaderBottomSheet.hideLoader();
// });
});
}, onError: (err) {
LoaderBottomSheet.hideLoader();
});
}),
HmgServicesComponentModel(
11,
"Covid 19 Test".needTranslation,
"".needTranslation,
AppAssets.covid19icon,
bgColor: AppColors.covid29Color,
true,
route: AppRoutes.covid19Test,
)
// HmgServicesComponentModel(
// 3,
// "Home Health Care".needTranslation,
@ -176,30 +163,8 @@ class ServicesPage extends StatelessWidget {
route: AppRoutes.smartWatches,
// 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
Widget build(BuildContext context) {
bloodDonationViewModel = Provider.of<BloodDonationViewModel>(context);
@ -467,43 +432,37 @@ class ServicesPage extends StatelessWidget {
),
SizedBox(width: 16.w),
Expanded(
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ParkingPage(),
),
);
},
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Row(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.car_parking_icon,
width: 32.w,
height: 32.h,
fit: BoxFit.contain,
),
SizedBox(width: 8.w),
"Car Parking".needTranslation.toText12(fontWeight: FontWeight.w500)
],
).onPress(() {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ParkingPage(),
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 12.h,
hasShadow: false,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Row(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.car_parking_icon,
width: 32.w,
height: 32.h,
fit: BoxFit.contain,
),
SizedBox(width: 8.w),
"Car Parking".needTranslation.toText12(fontWeight: FontWeight.w500)
],
).onPress(() {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ChangeNotifierProvider(
create: (_) => getIt<QrParkingViewModel>(),
child: const ParkingPage(),
),
);
}),
),
),
);
}),
),
),
),

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

@ -1,16 +1,18 @@
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/extensions/string_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:mobile_scanner/mobile_scanner.dart';
import 'package:provider/provider.dart';
import '../../features/qr_parking/qr_parking_view_model.dart';
import '../../theme/colors.dart';
import '../../widgets/appbar/app_bar_widget.dart';
import '../../widgets/routes/custom_page_route.dart';
class ParkingPage extends StatefulWidget {
const ParkingPage({super.key});
@ -19,10 +21,29 @@ class ParkingPage extends StatefulWidget {
}
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
Widget build(BuildContext context) {
final vm = context.watch<QrParkingViewModel>(); // عشان loading
return Scaffold(
backgroundColor: AppColors.scaffoldBgColor,
appBar: CustomAppBar(
@ -38,22 +59,24 @@ class _ParkingPageState extends State<ParkingPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Parking".needTranslation,
style: TextStyle(
color: AppColors.textColor,
fontSize: 27.f,
fontWeight: FontWeight.w600)),
Container(
decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
// margin: EdgeInsets.all(10),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Text(
Text(
"Parking".needTranslation,
style: TextStyle(
color: AppColors.textColor,
fontSize: 27.f,
fontWeight: FontWeight.w600,
),
),
Container(
decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Text(
"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."
" 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.",
style: TextStyle(
color: AppColors.textColor,
fontSize: 12, height: 1.4, fontWeight: FontWeight.w500),
),
),
).paddingOnly( top: 16, bottom: 16),
fontSize: 12,
height: 1.4,
fontWeight: FontWeight.w500,
),
),
),
).paddingOnly(top: 16, bottom: 16),
],
),
),
),
/// Bottom button
Container
(
Container(
decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(
color: AppColors.whiteColor,
@ -93,13 +117,17 @@ class _ParkingPageState extends State<ParkingPage> {
borderRadius: BorderRadius.circular(10),
),
),
onPressed: () {
Navigator.of(context).push(
CustomPageRoute(
page: ParkingSlot(),
) );
},
child: Text(
onPressed: vm.isLoading ? null : () => _readQR(context),
child: vm.isLoading
? const SizedBox(
width: 22,
height: 22,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Text(
"Read Barcodes",
style: TextStyle(
fontSize: 18,
@ -116,3 +144,4 @@ class _ParkingPageState extends State<ParkingPage> {
);
}
}

@ -1,26 +1,106 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package: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 '../../widgets/appbar/app_bar_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 {
const ParkingSlot({super.key});
final QrParkingResponseModel model;
const ParkingSlot({
super.key,
required this.model,
});
@override
State<ParkingSlot> createState() => _ParkingSlotState();
}
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
Widget build(BuildContext context) {
@ -34,7 +114,7 @@ class _ParkingSlotState extends State<ParkingSlot> {
body: LayoutBuilder(
builder: (context, constraints) {
final maxW = constraints.maxWidth;
final contentW = maxW > 600 ? 600.0 : maxW; // حد أقصى للتابلت
final contentW = maxW > 600 ? 600.0 : maxW;
return Align(
alignment: Alignment.topCenter,
@ -45,9 +125,11 @@ class _ParkingSlotState extends State<ParkingSlot> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
@ -58,7 +140,7 @@ class _ParkingSlotState extends State<ParkingSlot> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Parking Slot Details",
"Parking Slot Details".needTranslation,
style: TextStyle(
fontSize: 16.f,
fontWeight: FontWeight.w600,
@ -70,10 +152,26 @@ class _ParkingSlotState extends State<ParkingSlot> {
spacing: 4,
runSpacing: 4,
children: [
AppCustomChipWidget(labelText: "Slot: B-24".needTranslation),
AppCustomChipWidget(labelText: "Basement: Zone B".needTranslation),
AppCustomChipWidget(labelText: "Date: 16 Dec 2025".needTranslation),
AppCustomChipWidget(labelText: "Parked Since: 10:32 AM".needTranslation),
AppCustomChipWidget(
labelText:
"Slot: ${widget.model.qRParkingCode ?? '-'}"
.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),
),
),
onPressed: () {},
onPressed: _openDirection,
child: Text(
"Get Direction",
"Get Direction".needTranslation,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
color: AppColors.whiteColor,
),
),
),
),
const Spacer(),
SizedBox(
width: double.infinity,
height: 48.h,
child: OutlinedButton(
style: OutlinedButton.styleFrom(
side: BorderSide(color: AppColors.primaryRedColor),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
onPressed: () {
// Reset direction logic
},
child: Text(
"Reset Direction",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.primaryRedColor,
),
),
),
),
// const Spacer(),
// SizedBox(
// width: double.infinity,
// height: 48.h,
// child: OutlinedButton(
// style: OutlinedButton.styleFrom(
// side: BorderSide(color: AppColors.primaryRedColor),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(10),
// ),
// ),
// onPressed: _resetDirection,
// child: Text(
// "Reset Direction".needTranslation,
// style: TextStyle(
// fontSize: 16,
// 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/splashPage.dart';
import '../features/qr_parking/qr_parking_view_model.dart';
import '../presentation/covid19test/covid19_landing_page.dart';
import '../core/dependencies.dart';
import '../features/monthly_reports/monthly_reports_repo.dart';
import '../features/monthly_reports/monthly_reports_view_model.dart';
import '../presentation/monthly_reports/monthly_reports_page.dart';
import '../presentation/parking/paking_page.dart';
import '../services/error_handler_service.dart';
import 'package:provider/provider.dart';
@ -65,6 +67,7 @@ class AppRoutes {
static const String userInfoSelection = '/userInfoSelection';
static const String userInfoFlowManager = '/userInfoFlowManager';
static const String monthlyReports = '/monthlyReportsPage';
static const String qrParking = '/qrParkingPage';
static Map<String, WidgetBuilder> get routes => {
initialRoute: (context) => SplashPage(),
@ -92,7 +95,6 @@ class AppRoutes {
covid19Test: (context) => Covid19LandingPage(),
//
// healthCalculatorsPage: (context) => HealthCalculatorsPage(),
// monthlyReports: (context) => MonthlyReportsPage()
monthlyReports: (context) => ChangeNotifierProvider(
create: (_) => MonthlyReportsViewModel(
monthlyReportsRepo: getIt<MonthlyReportsRepo>(),
@ -102,6 +104,12 @@ class AppRoutes {
),
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