You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
HMG_Patient_App_New/lib/presentation/parking/parking_slot.dart

237 lines
7.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/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/buttons/custom_button.dart';
import '../../widgets/chip/app_custom_chip_widget.dart';
import 'package:maps_launcher/maps_launcher.dart';
import 'package:provider/provider.dart';
import '../../widgets/routes/custom_page_route.dart';
class ParkingSlot extends StatefulWidget {
final QrParkingResponseModel model;
const ParkingSlot({
super.key,
required this.model,
});
@override
State<ParkingSlot> createState() => _ParkingSlotState();
}
class _ParkingSlotState extends State<ParkingSlot> {
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();
final model = await vm.scanAndGetParking();
if (model == null) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(vm.error ?? "Scan cancelled")),
);
Navigator.of(context).pop();
return;
}
if (!mounted) return;
Navigator.of(context).pushReplacement(
CustomPageRoute(
page: ChangeNotifierProvider.value(
value: vm,
child: ParkingSlot(model: model),
),
),
);
}
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.toString().padLeft(2, '0');
final month = months[date.month - 1];
final year = date.year;
return "$day $month $year"; // ✅ 15 Dec 2025
}
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"; // ✅ 03:05 PM
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.scaffoldBgColor,
appBar: CustomAppBar(
onBackPressed: () => Navigator.of(context).pop(),
onLanguageChanged: (_) {},
hideLogoAndLang: true,
),
body: LayoutBuilder(
builder: (context, constraints) {
final maxW = constraints.maxWidth;
final contentW = maxW > 600 ? 600.0 : maxW;
return Align(
alignment: Alignment.topCenter,
child: SizedBox(
width: contentW,
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
width: double.infinity,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Parking Slot Details".needTranslation,
style: TextStyle(
fontSize: 16.f,
fontWeight: FontWeight.w600,
color: AppColors.textColor,
),
),
SizedBox(height: 16.h),
Wrap(
spacing: 4,
runSpacing: 4,
children: [
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,
),
],
),
],
),
),
),
SizedBox(height: 24.h),
SizedBox(
width: double.infinity,
height: 48.h,
child: CustomButton(
text: "Get Direction".needTranslation,
onPressed: _openDirection,
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 18,
fontWeight: FontWeight.bold,
borderRadius: 10,
),
),
const Spacer(),
SizedBox(
width: double.infinity,
height: 48.h,
child: CustomButton(
text: "Reset Direction".needTranslation,
onPressed: _resetDirection,
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
fontSize: 18,
fontWeight: FontWeight.bold,
borderRadius: 10,
),
),
],
),
),
),
);
},
),
);
}
}