From 4b7567a6fe01e241ec923f382397160f76dee0d5 Mon Sep 17 00:00:00 2001 From: mohammad Date: Wed, 21 Aug 2024 16:02:08 +0300 Subject: [PATCH] padding and text them --- lib/main.dart | 2 +- lib/pages/common/custom_web_textfield.dart | 24 +- lib/pages/common/date_time_widget.dart | 10 +- lib/pages/common/hour_picker_dialog.dart | 92 +++ .../bloc/visitor_password_bloc.dart | 269 ++++++--- .../bloc/visitor_password_event.dart | 22 +- .../bloc/visitor_password_state.dart | 1 + .../view/add_device_dialog.dart | 1 + .../visitor_password/view/repeat_widget.dart | 27 +- .../view/visitor_password_dialog.dart | 534 +++++++++++------- lib/services/access_mang_api.dart | 56 +- 11 files changed, 720 insertions(+), 318 deletions(-) create mode 100644 lib/pages/common/hour_picker_dialog.dart diff --git a/lib/main.dart b/lib/main.dart index dcabeaa2..c00a8da7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -59,7 +59,7 @@ class MyApp extends StatelessWidget { colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), // Set up color scheme useMaterial3: true, // Enable Material 3 ), - // home: AddDeviceDialog() + // home: VisitorPasswordDialog() home:isLoggedIn == 'Success' ? const HomePage() : const LoginPage(), )); } diff --git a/lib/pages/common/custom_web_textfield.dart b/lib/pages/common/custom_web_textfield.dart index 4ec0aa81..82fb86c9 100644 --- a/lib/pages/common/custom_web_textfield.dart +++ b/lib/pages/common/custom_web_textfield.dart @@ -37,19 +37,23 @@ class CustomWebTextField extends StatelessWidget { .bodyMedium! .copyWith(color: Colors.red), ), - Text(textFieldName), + Text(textFieldName, + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13),), ], ), const SizedBox(width: 10,), - Text( - description??'', // ' The password will be sent to the visitor’s email address.', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith( - fontSize: 9, - fontWeight: FontWeight.w400, - color: ColorsManager.textGray), + Expanded( + child: Text( + description??'', + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + fontSize: 9, + fontWeight: FontWeight.w400, + color: ColorsManager.textGray), + ), ), ], ), diff --git a/lib/pages/common/date_time_widget.dart b/lib/pages/common/date_time_widget.dart index ace2cf8d..f5034342 100644 --- a/lib/pages/common/date_time_widget.dart +++ b/lib/pages/common/date_time_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/style.dart'; @@ -39,7 +40,8 @@ class DateTimeWebWidget extends StatelessWidget { .bodyMedium! .copyWith(color: Colors.red), ), - Text(title??''), + Text(title??'' , style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13),), ], ), SizedBox(height: 8,), @@ -55,12 +57,14 @@ class DateTimeWebWidget extends StatelessWidget { children: [ InkWell( onTap: startTime, - child: Text(firstString) + child: Text(firstString, style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor,fontSize: 12,fontWeight: FontWeight.w400),) ), const Icon(Icons.arrow_right_alt), InkWell( onTap:endTime, - child: Text(secondString)), + child: Text(secondString, style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor,fontSize: 12,fontWeight: FontWeight.w400),)), SvgPicture.asset( Assets.calendarIcon, ), diff --git a/lib/pages/common/hour_picker_dialog.dart b/lib/pages/common/hour_picker_dialog.dart new file mode 100644 index 00000000..63e95e21 --- /dev/null +++ b/lib/pages/common/hour_picker_dialog.dart @@ -0,0 +1,92 @@ + + +import 'package:flutter/material.dart'; + +class HourPickerDialog extends StatefulWidget { + final TimeOfDay initialTime; + HourPickerDialog({required this.initialTime}); + + @override + _HourPickerDialogState createState() => _HourPickerDialogState(); +} + +class _HourPickerDialogState extends State { + late int _selectedHour; + bool _isPm = false; + + @override + void initState() { + super.initState(); + _selectedHour = widget.initialTime.hour > 12 ? widget.initialTime.hour - 12 : widget.initialTime.hour; + _isPm = widget.initialTime.period == DayPeriod.pm; + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text('Select Hour'), + content: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + DropdownButton( + value: _selectedHour, + items: List.generate(12, (index) { + int displayHour = index + 1; + return DropdownMenuItem( + value: displayHour, + child: Text(displayHour.toString()), + ); + }), + onChanged: (value) { + setState(() { + _selectedHour = value!; + }); + }, + ), + SizedBox(width: 16.0), + DropdownButton( + value: _isPm, + items: [ + DropdownMenuItem( + value: false, + child: Text('AM'), + ), + DropdownMenuItem( + value: true, + child: Text('PM'), + ), + ], + onChanged: (value) { + setState(() { + _isPm = value!; + }); + }, + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(null), + child: Text('Cancel'), + ), + TextButton( + onPressed: () { + int hour = _isPm ? _selectedHour + 12 : _selectedHour; + Navigator.of(context).pop(TimeOfDay(hour: hour, minute: 0)); + }, + child: Text('OK'), + ), + ], + ); + } +} + +Future showHourPicker({ + required BuildContext context, + required TimeOfDay initialTime, +}) { + return showDialog( + context: context, + builder: (context) => HourPickerDialog(initialTime: initialTime), + ); +} diff --git a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart index d9f49e56..e620cc59 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart @@ -1,8 +1,8 @@ import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; +import 'package:syncrow_web/pages/common/hour_picker_dialog.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart'; import 'package:syncrow_web/pages/visitor_password/model/device_model.dart'; @@ -10,7 +10,8 @@ import 'package:syncrow_web/pages/visitor_password/model/schedule_model.dart'; import 'package:syncrow_web/services/access_mang_api.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/snack_bar.dart'; - List selectedDevices = []; + +List selectedDevices = []; // Define the BLoC class VisitorPasswordBloc @@ -30,7 +31,8 @@ class VisitorPasswordBloc on(postOfflineMultipleTimePassword); on(postOfflineOneTimePassword); - + on(selectTimeOfLinePassword); + on(changeTime); } final TextEditingController userNameController = TextEditingController(); final TextEditingController emailController = TextEditingController(); @@ -38,14 +40,14 @@ class VisitorPasswordBloc final TextEditingController deviceNameController = TextEditingController(); final TextEditingController deviceIdController = TextEditingController(); final TextEditingController unitNameController = TextEditingController(); - final TextEditingController virtualAddressController = TextEditingController(); - - + final TextEditingController virtualAddressController = + TextEditingController(); List data = []; List selectedDeviceIds = []; + String effectiveTime = 'Select Time'; - + String expirationTime = 'Select Time'; final forgetFormKey = GlobalKey(); @@ -61,12 +63,11 @@ class VisitorPasswordBloc int? repeatEffectiveTimeTimeStamp; int? repeatExpirationTimeTimeStamp; - String startTime = 'Start Time'; - String endTime = 'End Time'; + DateTime? startTime = DateTime.now(); + DateTime? endTime; - - String repeatStartTime = 'Start Time'; - String repeatEndTime = 'End Time'; + String startTimeAccess = 'Start Time'; + String endTimeAccess = 'End Time'; // DateTime? repeatStartTime=DateTime.now(); // DateTime? repeatEndTime; @@ -85,13 +86,15 @@ class VisitorPasswordBloc Future selectTimeVisitorPassword( SelectTimeVisitorPassword event, - Emitter emit) async { + Emitter emit, + ) async { final DateTime? picked = await showDatePicker( context: event.context, initialDate: DateTime.now(), firstDate: DateTime(2015, 8), lastDate: DateTime(3101), ); + if (picked != null) { final TimeOfDay? timePicked = await showTimePicker( context: event.context, @@ -113,6 +116,7 @@ class VisitorPasswordBloc ); }, ); + if (timePicked != null) { final selectedDateTime = DateTime( picked.year, @@ -121,49 +125,39 @@ class VisitorPasswordBloc timePicked.hour, timePicked.minute, ); - final selectedTimestamp = DateTime( - selectedDateTime.year, - selectedDateTime.month, - selectedDateTime.day, - selectedDateTime.hour, - selectedDateTime.minute, - ).millisecondsSinceEpoch ~/ - 1000; // Divide by 1000 to remove milliseconds - if (event.isStart) { - if (expirationTimeTimeStamp != null && selectedTimestamp >expirationTimeTimeStamp!) { - CustomSnackBar.displaySnackBar('Effective Time cannot be later than Expiration Time.'); - } else { - if(event.isRepeat==true) - {repeatStartTime = selectedDateTime.toString().split('.').first;} - else // Remove seconds and milliseconds - {startTime = selectedDateTime.toString().split('.').first;} - effectiveTimeTimeStamp = selectedTimestamp; - emit(ChangeTimeState()); + final selectedTimestamp = selectedDateTime.millisecondsSinceEpoch ~/ 1000; + + if (event.isStart) { + if (expirationTimeTimeStamp != null && + selectedTimestamp > expirationTimeTimeStamp!) { + CustomSnackBar.displaySnackBar( + 'Effective Time cannot be later than Expiration Time.', + ); + return; } - emit(ChangeTimeState()); + effectiveTimeTimeStamp = selectedTimestamp; + + startTimeAccess = selectedDateTime.toString().split('.').first; + } else { if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) { - CustomSnackBar.displaySnackBar('Expiration Time cannot be earlier than Effective Time.'); - } else { - if(event.isRepeat==true) - {repeatEndTime = selectedDateTime.toString().split('.').first;} - else - {endTime = selectedDateTime.toString().split('.').first;} - expirationTimeTimeStamp = selectedTimestamp; - emit(ChangeTimeState()); - + CustomSnackBar.displaySnackBar( + 'Expiration Time cannot be earlier than Effective Time.', + ); + return; } - emit(ChangeTimeState()); + expirationTimeTimeStamp = selectedTimestamp; + + endTimeAccess = selectedDateTime.toString().split('.').first; - emit(VisitorPasswordInitial()); } + + emit(ChangeTimeState()); + emit(VisitorPasswordInitial()); } } - - // emit(AccessInitial()); - // emit(TableLoaded(data)); } bool toggleRepeat( @@ -212,17 +206,24 @@ class VisitorPasswordBloc //online password - Future postOnlineOneTimePassword( - OnlineOneTimePasswordEvent event, + Future postOnlineOneTimePassword(OnlineOneTimePasswordEvent event, Emitter emit) async { try { + generate7DigitNumber(); + print('selectedDevices$selectedDevices'); // emit(DeviceLoaded()); - await AccessMangApi().postOnlineOneTime( + bool res = await AccessMangApi().postOnlineOneTime( email: event.email, + password: passwordController, devicesUuid: selectedDevices, - passwordName: event.passwordName); - // emit(TableLoaded(data)); + passwordName: event.passwordName, + effectiveTime: effectiveTimeTimeStamp.toString(), + invalidTime: expirationTimeTimeStamp.toString()); + if (res = true) { + Navigator.pop(event.context!); + } + emit(TableLoaded(data)); } catch (e) { emit(FailedState(e.toString())); } @@ -233,33 +234,31 @@ class VisitorPasswordBloc Emitter emit) async { try { generate7DigitNumber(); - // emit(DeviceLoaded()); - await AccessMangApi().postOnlineMultipleTime( - scheduleList:[ - // if (repeat) - // Schedule( - // effectiveTime: getTimeOnly(repeatStartTime), - // invalidTime: getTimeOnly(repeatEndTime).toString(), - // workingDay: selectedDays, - // ), - ] , + bool res = await AccessMangApi().postOnlineMultipleTime( + scheduleList: [ + if (repeat) + Schedule( + effectiveTime: getTimeFromDateTimeString(expirationTime), + invalidTime: getTimeFromDateTimeString(effectiveTime).toString(), + workingDay: selectedDays, + ), + ], password: passwordController, - invalidTime:event.invalidTime , - effectiveTime:event.effectiveTime , + invalidTime: expirationTimeTimeStamp.toString(), + effectiveTime: effectiveTimeTimeStamp.toString(), email: event.email, devicesUuid: selectedDevices, - passwordName: event.passwordName - ); - // emit(TableLoaded(data)); + passwordName: event.passwordName); + if(res==true){ + Navigator.pop(event.context!); + } } catch (e) { emit(FailedState(e.toString())); } } - //offline password - Future postOfflineOneTimePassword( - OfflineOneTimePasswordEvent event, + Future postOfflineOneTimePassword(OfflineOneTimePasswordEvent event, Emitter emit) async { try { generate7DigitNumber(); @@ -267,8 +266,7 @@ class VisitorPasswordBloc await AccessMangApi().postOffLineOneTime( email: event.email, devicesUuid: selectedDevices, - passwordName: event.passwordName - ); + passwordName: event.passwordName); // emit(TableLoaded(data)); } catch (e) { emit(FailedState(e.toString())); @@ -285,16 +283,14 @@ class VisitorPasswordBloc email: event.email, devicesUuid: selectedDevices, passwordName: event.passwordName, - invalidTime:event.invalidTime , - effectiveTime:event.effectiveTime - ); + invalidTime: event.invalidTime, + effectiveTime: event.effectiveTime); // emit(TableLoaded(data)); } catch (e) { emit(FailedState(e.toString())); } } - void selectDevice( SelectDeviceEvent event, Emitter emit) { if (selectedDeviceIds.contains(event.deviceId)) { @@ -302,8 +298,6 @@ class VisitorPasswordBloc } else { selectedDeviceIds.add(event.deviceId); } - selectedDevices=selectedDeviceIds; - print(selectedDevices); } String? validate(String? value) { @@ -313,10 +307,9 @@ class VisitorPasswordBloc return null; } - Future generate7DigitNumber() async { emit(LoadingInitialState()); - passwordController=''; + passwordController = ''; Random random = Random(); int min = 1000000; int max = 9999999; @@ -324,12 +317,12 @@ class VisitorPasswordBloc emit(GeneratePasswordState()); return passwordController; } + String getTimeOnly(DateTime? dateTime) { if (dateTime == null) return ''; return DateFormat('HH:mm').format(dateTime); } - void filterDevices() { final deviceName = deviceNameController.text.toLowerCase(); final deviceId = deviceIdController.text.toLowerCase(); @@ -340,14 +333,16 @@ class VisitorPasswordBloc final matchesDeviceId = device.uuid.toLowerCase().contains(deviceId); // final matchesUnitName = device.unitName.toLowerCase().contains(unitName); // Assuming unitName is a property of the device - return matchesDeviceName && matchesDeviceId ; + return matchesDeviceName && matchesDeviceId; }).toList(); // emit(TableLoaded(filteredData)); - add(UpdateFilteredDevicesEvent(filteredData)); + add(UpdateFilteredDevicesEvent(filteredData)); } + @override - Stream mapEventToState(VisitorPasswordEvent event) async* { + Stream mapEventToState( + VisitorPasswordEvent event) async* { if (event is FetchDevice) { // Fetching logic... } else if (event is UpdateFilteredDevicesEvent) { @@ -355,7 +350,113 @@ class VisitorPasswordBloc } } - void _onUpdateFilteredDevices(UpdateFilteredDevicesEvent event, Emitter emit) { + void _onUpdateFilteredDevices( + UpdateFilteredDevicesEvent event, Emitter emit) { emit(TableLoaded(event.filteredData)); } + + addDeviceToList() { + selectedDevices = selectedDeviceIds; + print(selectedDevices); + } + + Future selectTimeOfLinePassword(SelectTimeEvent event, Emitter emit) async { + emit(ChangeTimeState()); + final DateTime? picked = await showDatePicker( + context: event.context, + initialDate: DateTime.now(), + firstDate: DateTime.now(), + lastDate: DateTime(3101), + ); + if (picked != null) { + final TimeOfDay? timePicked = await showHourPicker( + context: event.context, + initialTime: TimeOfDay.now(), + ); + if (timePicked != null) { + final selectedDateTime = DateTime( + picked.year, + picked.month, + picked.day, + timePicked.hour, + 0, + ); + final selectedTimestamp = DateTime( + selectedDateTime.year, + selectedDateTime.month, + selectedDateTime.day, + selectedDateTime.hour, + selectedDateTime.minute, + ).millisecondsSinceEpoch ~/ + 1000; // Divide by 1000 to remove milliseconds + if (event.isEffective) { + if (expirationTimeTimeStamp != null && + selectedTimestamp > expirationTimeTimeStamp!) { + CustomSnackBar.displaySnackBar( + 'Effective Time cannot be later than Expiration Time.'); + } else { + effectiveTime = selectedDateTime + .toString() + .split('.') + .first; // Remove seconds and milliseconds + effectiveTimeTimeStamp = selectedTimestamp; + } + } else { + if (effectiveTimeTimeStamp != null && + selectedTimestamp < effectiveTimeTimeStamp!) { + CustomSnackBar.displaySnackBar( + 'Expiration Time cannot be earlier than Effective Time.'); + } else { + expirationTime = selectedDateTime + .toString() + .split('.') + .first; // Remove seconds and milliseconds + expirationTimeTimeStamp = selectedTimestamp; + } + } + print('effectiveTime=$effectiveTime'); + print('expirationTime=$expirationTime'); + + print('expirationTimeTimeStamp=$expirationTimeTimeStamp'); + print('effectiveTimeTimeStamp=$effectiveTimeTimeStamp'); + emit(TimeSelectedState()); + } + } + } + + changeTime(ChangeTimeEvent event, Emitter emit) { + if (event.isStartEndTime == true) { + startTime = event.val; + } else { + endTime = event.val; + } + } + DateTime? convertStringToDateTime(String dateTimeString) { + try { + // Define the input format. Adjust this pattern based on your input string format. + final DateFormat inputFormat = DateFormat('yyyy-MM-dd HH:mm:ss'); + // Convert the string to a DateTime object. + DateTime dateTime = inputFormat.parse(dateTimeString); + return dateTime; + } catch (e) { + print("Error parsing date: $e"); + return null; + } + } + + String getTimeFromDateTimeString(String dateTimeString) { + DateTime? dateTime = convertStringToDateTime(dateTimeString); + if (dateTime == null) return ''; + + // Extract the time component from the DateTime object. + return DateFormat('HH:mm').format(dateTime); + } + + String? validateEmail(String? value) { + if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value!)) { + return ''; + } + return null; + } + } diff --git a/lib/pages/visitor_password/bloc/visitor_password_event.dart b/lib/pages/visitor_password/bloc/visitor_password_event.dart index 9990ad9a..723ac052 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_event.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_event.dart @@ -57,8 +57,9 @@ class FetchDevice extends VisitorPasswordEvent {} class OnlineOneTimePasswordEvent extends VisitorPasswordEvent { final String? email; final String? passwordName; + final BuildContext? context; - const OnlineOneTimePasswordEvent({this.email,this.passwordName}); + const OnlineOneTimePasswordEvent({this.email,this.passwordName,this.context}); @override List get props => [email!,passwordName!,]; @@ -68,9 +69,10 @@ class OnlineMultipleTimePasswordEvent extends VisitorPasswordEvent { final String? passwordName; final String? invalidTime; final String? effectiveTime; - const OnlineMultipleTimePasswordEvent({this.email,this.passwordName,this.invalidTime,this.effectiveTime}); + final BuildContext? context; + const OnlineMultipleTimePasswordEvent({this.email,this.passwordName,this.invalidTime,this.effectiveTime,this.context}); @override - List get props => [email!,passwordName!,invalidTime!,effectiveTime!]; + List get props => [email!,passwordName!,invalidTime!,effectiveTime!,context!]; } //offline password @@ -115,4 +117,18 @@ class UpdateFilteredDevicesEvent extends VisitorPasswordEvent { final List filteredData; UpdateFilteredDevicesEvent(this.filteredData); +}class SelectTimeEvent extends VisitorPasswordEvent { + final BuildContext context; + final bool isEffective; + const SelectTimeEvent({required this.context,required this.isEffective}); + @override + List get props => [context,isEffective]; +} +class ChangeTimeEvent extends VisitorPasswordEvent { + final dynamic val; + final bool isStartEndTime; + + const ChangeTimeEvent({required this.val,required this.isStartEndTime}); + @override + List get props => [val,isStartEndTime]; } \ No newline at end of file diff --git a/lib/pages/visitor_password/bloc/visitor_password_state.dart b/lib/pages/visitor_password/bloc/visitor_password_state.dart index 374433af..27008a30 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_state.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_state.dart @@ -41,6 +41,7 @@ class IsRepeatState extends VisitorPasswordState { class LoadingInitialState extends VisitorPasswordState {} class ChangeTimeState extends VisitorPasswordState {} +class TimeSelectedState extends VisitorPasswordState {} class DeviceLoaded extends VisitorPasswordState {} class GeneratePasswordState extends VisitorPasswordState {} diff --git a/lib/pages/visitor_password/view/add_device_dialog.dart b/lib/pages/visitor_password/view/add_device_dialog.dart index 7dc5407d..844e0ca9 100644 --- a/lib/pages/visitor_password/view/add_device_dialog.dart +++ b/lib/pages/visitor_password/view/add_device_dialog.dart @@ -207,6 +207,7 @@ class AddDeviceDialog extends StatelessWidget { width: size.width * 0.2, child: DefaultButton( onPressed: () { + visitorBloc.addDeviceToList(); Navigator.of(context).pop(); // Close the dialog }, borderRadius: 8, diff --git a/lib/pages/visitor_password/view/repeat_widget.dart b/lib/pages/visitor_password/view/repeat_widget.dart index b04614fe..73770100 100644 --- a/lib/pages/visitor_password/view/repeat_widget.dart +++ b/lib/pages/visitor_password/view/repeat_widget.dart @@ -56,19 +56,26 @@ class RepeatWidget extends StatelessWidget { title: '', size: size, endTime: () { - visitorBloc.add(SelectTimeVisitorPassword( - isRepeat: true, - context: context, isStart: false - )); + print('sadasd'); + visitorBloc.add(SelectTimeEvent( + context: context, + isEffective: false)); + new Future.delayed(const Duration(milliseconds: 500), () { + visitorBloc.add(ChangeTimeEvent(val: visitorBloc.endTime, isStartEndTime: true)); + + }); + + }, startTime: () { - visitorBloc.add(SelectTimeVisitorPassword( - isRepeat: true, - context: context, isStart: true - )); + new Future.delayed(const Duration(milliseconds: 500), () { + visitorBloc.add(ChangeTimeEvent(val: visitorBloc.endTime, isStartEndTime: true)); + }); + visitorBloc.add(SelectTimeEvent(context: context, isEffective: true)); + }, - firstString: visitorBloc.repeatStartTime.toString(), - secondString: visitorBloc.repeatEndTime.toString(), + firstString:visitorBloc.effectiveTime , + secondString: visitorBloc.expirationTime , ), ), diff --git a/lib/pages/visitor_password/view/visitor_password_dialog.dart b/lib/pages/visitor_password/view/visitor_password_dialog.dart index e91ab834..2299877f 100644 --- a/lib/pages/visitor_password/view/visitor_password_dialog.dart +++ b/lib/pages/visitor_password/view/visitor_password_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/pages/common/custom_web_textfield.dart'; import 'package:syncrow_web/pages/common/date_time_widget.dart'; import 'package:syncrow_web/pages/common/default_button.dart'; @@ -10,6 +11,8 @@ import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.d import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart'; import 'package:syncrow_web/pages/visitor_password/view/add_device_dialog.dart'; import 'package:syncrow_web/pages/visitor_password/view/repeat_widget.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/style.dart'; class VisitorPasswordDialog extends StatelessWidget { @@ -23,19 +26,22 @@ class VisitorPasswordDialog extends StatelessWidget { child: BlocBuilder( builder: (BuildContext context, VisitorPasswordState state) { final visitorBloc = BlocProvider.of(context); - bool isRepeat = state is IsRepeatState ? state.repeat : visitorBloc.repeat; + bool isRepeat = + state is IsRepeatState ? state.repeat : visitorBloc.repeat; return AlertDialog( backgroundColor: Colors.white, - title: Text('Create visitor password', - style: Theme.of(context).textTheme.headlineLarge!.copyWith( - fontWeight: FontWeight.w400, - fontSize: 24, - color: Colors.black),), + title: Text( + 'Create visitor password', + style: Theme.of(context).textTheme.headlineLarge!.copyWith( + fontWeight: FontWeight.w400, + fontSize: 24, + color: Colors.black), + ), content: SingleChildScrollView( child: Form( key: visitorBloc.forgetFormKey, child: Padding( - padding: const EdgeInsets.all(10.0), + padding: const EdgeInsets.all(5.0), child: ListBody( children: [ Container( @@ -55,18 +61,21 @@ class VisitorPasswordDialog extends StatelessWidget { Expanded( flex: 2, child: CustomWebTextField( - validator: visitorBloc.validate, + validator: visitorBloc.validateEmail, controller: visitorBloc.emailController, isRequired: true, textFieldName: 'Email Address', - description: 'The password will be sent to the visitor’s email address.', + description: + 'The password will be sent to the visitor’s email address.', ), ), const Spacer(), ], ), ), - SizedBox(height: 20,), + const SizedBox( + height: 15, + ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -79,7 +88,9 @@ class VisitorPasswordDialog extends StatelessWidget { .bodyMedium! .copyWith(color: Colors.red), ), - const Text('Access Type'), + Text('Access Type', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13),), ], ), Row( @@ -87,7 +98,10 @@ class VisitorPasswordDialog extends StatelessWidget { SizedBox( width: size.width * 0.15, child: RadioListTile( - title: const Text('Online Password'), + title: Text('Online Password', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13), + ), value: 'Online Password', groupValue: (state is PasswordTypeSelected) ? state.selectedType @@ -95,16 +109,19 @@ class VisitorPasswordDialog extends StatelessWidget { onChanged: (String? value) { if (value != null) { print(value); - context.read().add(SelectPasswordType(value)); + context + .read() + .add(SelectPasswordType(value)); } }, ), ), - SizedBox( width: size.width * 0.15, child: RadioListTile( - title: const Text('Offline Password'), + title: Text('Offline Password', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13),), value: 'Offline Password', groupValue: (state is PasswordTypeSelected) ? state.selectedType @@ -113,36 +130,146 @@ class VisitorPasswordDialog extends StatelessWidget { if (value != null) { print(value); - context.read().add(SelectPasswordType(value)); + context + .read() + .add(SelectPasswordType(value)); } }, ), ), - SizedBox( width: size.width * 0.15, child: RadioListTile( - title: const Text('Dynamic Password'), + title: Text('Dynamic Password', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13),), value: 'Dynamic Password', groupValue: (state is PasswordTypeSelected) ? state.selectedType : visitorBloc.accessTypeSelected, onChanged: (String? value) { if (value != null) { - context.read().add(SelectPasswordType(value)); - visitorBloc.usageFrequencySelected=''; + context + .read() + .add(SelectPasswordType(value)); + visitorBloc.usageFrequencySelected = ''; } }, ), ), ], ), - const Text('Only currently online devices can be selected. It is recommended to use when the device network is stable, and the system randomly generates a digital password'), - const SizedBox(height: 20,) + Text( + 'Only currently online devices can be selected. It is recommended to use when the device network is stable, and the system randomly generates a digital password', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.grayColor,fontSize: 9),), + const SizedBox( + height: 20, + ) ], ), - visitorBloc.accessTypeSelected=='Dynamic Password' ? - SizedBox(): + visitorBloc.accessTypeSelected == 'Dynamic Password' + ? SizedBox() + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + Text('Usage Frequency',style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13),), + ], + ), + Row( + children: [ + SizedBox( + width: 200, + child: RadioListTile( + title: Text('One-Time',style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13),), + value: 'One-Time', + groupValue: + (state is UsageFrequencySelected) + ? state.selectedFrequency + : visitorBloc + .usageFrequencySelected, + onChanged: (String? value) { + if (value != null) { + print(value); + + context + .read() + .add(SelectUsageFrequency( + value)); + } + }, + ), + ), + SizedBox( + width: 200, + child: RadioListTile( + title: Text('Periodic', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13),), + value: 'Periodic', + groupValue: + (state is UsageFrequencySelected) + ? state.selectedFrequency + : visitorBloc + .usageFrequencySelected, + onChanged: (String? value) { + if (value != null) { + context + .read() + .add(SelectUsageFrequency( + value)); + } + }, + ), + ), + ], + ), + Text('Within the validity period, each device can be unlocked only once.', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor,fontSize: 9),) + ], + ), + const SizedBox( + height: 20, + ), + if ((visitorBloc.usageFrequencySelected != 'One-Time' || + visitorBloc.accessTypeSelected != + 'Offline Password') && + (visitorBloc.usageFrequencySelected != '')) + DateTimeWebWidget( + isRequired: true, + title: 'Access Period', + size: size, + endTime: () { + visitorBloc.add(SelectTimeVisitorPassword( + context: context, + isStart: false, + isRepeat: false)); + }, + startTime: () { + visitorBloc.add(SelectTimeVisitorPassword( + context: context, + isStart: true, + isRepeat: false)); + }, + firstString: visitorBloc.startTimeAccess.toString(), + secondString: visitorBloc.endTimeAccess.toString(), + ), + const SizedBox( + height: 20, + ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -155,111 +282,47 @@ class VisitorPasswordDialog extends StatelessWidget { .bodyMedium! .copyWith(color: Colors.red), ), - const Text('Usage Frequency'), + Text('Access Devices', style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13),), ], ), - Row( - children: [ - SizedBox( - width: 200, - child: RadioListTile( - title: const Text('One-Time'), - value: 'One-Time', - groupValue: (state is UsageFrequencySelected) - ? state.selectedFrequency - : visitorBloc.usageFrequencySelected, - onChanged: (String? value) { - if (value != null) { - print(value); - - context.read().add(SelectUsageFrequency(value)); - } - }, - ), - ), - SizedBox( - width: 200, - child: RadioListTile( - title: const Text('Periodic'), - value: 'Periodic', - groupValue: (state is UsageFrequencySelected) - ? state.selectedFrequency - : visitorBloc.usageFrequencySelected, - onChanged: (String? value) { - if (value != null) { - context.read().add(SelectUsageFrequency(value)); - } - }, - ), - ), - ], + Text( + 'Within the validity period, each device can be unlocked only once.',style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.grayColor,fontSize: 9),), + const SizedBox( + height: 20, ), - - const Text('Within the validity period, each device can be unlocked only once.') - ], - ), - const SizedBox(height: 20,), - if((visitorBloc.usageFrequencySelected!='One-Time'||visitorBloc.accessTypeSelected!='Offline Password')&&(visitorBloc.usageFrequencySelected!='')) - DateTimeWebWidget( - - isRequired: true, - title: 'Access Period', - size: size, - endTime: () { - visitorBloc.add(SelectTimeVisitorPassword( - context: context, isStart: false,isRepeat:false)); - }, - startTime: () { - visitorBloc.add(SelectTimeVisitorPassword( - context: context, isStart: true,isRepeat:false)); - }, - firstString: visitorBloc.startTime, - secondString: visitorBloc.endTime, - ), - - const SizedBox(height: 20,), - - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - '* ', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Colors.red), - ), - const Text('Access Devices'), - ], - ), - const Text('Within the validity period, each device can be unlocked only once.'), - const SizedBox(height: 20,), - if(visitorBloc.usageFrequencySelected=='Periodic'&&visitorBloc.accessTypeSelected=='Online Password') - SizedBox( - width: 100, - child: ListTile( - contentPadding: EdgeInsets.zero, - leading: const Text('Repeat'), - trailing: Transform.scale( - scale: .8, - child: CupertinoSwitch( - value: visitorBloc.repeat, - onChanged: (value) { - visitorBloc.add(ToggleRepeatEvent()); - }, - applyTheme: true, + if (visitorBloc.usageFrequencySelected == + 'Periodic' && + visitorBloc.accessTypeSelected == + 'Online Password') + SizedBox( + width: 100, + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: const Text('Repeat'), + trailing: Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: visitorBloc.repeat, + onChanged: (value) { + visitorBloc.add(ToggleRepeatEvent()); + }, + applyTheme: true, + ), ), ), ), - ), - if(visitorBloc.usageFrequencySelected=='Periodic'&&visitorBloc.accessTypeSelected=='Online Password') + if (visitorBloc.usageFrequencySelected == + 'Periodic' && + visitorBloc.accessTypeSelected == + 'Online Password') isRepeat ? const RepeatWidget() : const SizedBox(), Container( decoration: containerDecoration, - width: size.width * 0.1, - child: DefaultButton( + width: size.width * 0.08, + child: DefaultButton( onPressed: () { showDialog( context: context, @@ -270,12 +333,13 @@ class VisitorPasswordDialog extends StatelessWidget { ); }, borderRadius: 8, - child: Text('+ Add Device'), + child: Text('+ Add Device',style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.whiteColors,fontSize: 12),), ), ), ], ), - ], ), ), @@ -294,88 +358,176 @@ class VisitorPasswordDialog extends StatelessWidget { backgroundColor: Colors.white, child: Text( 'Cancel', - style: Theme.of(context).textTheme.bodyMedium!, + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor,fontSize: 16), ), ), ), Container( decoration: containerDecoration, width: size.width * 0.2, - child: DefaultButton( + child: DefaultButton( onPressed: () { - if(visitorBloc.forgetFormKey.currentState!.validate()){ - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return InfoDialog( - size: size, - title: 'Set Password', - content: 'This action will update all of the selected\n door locks passwords in the property.\n\nAre you sure you want to continue?', + print(selectedDevices); + if (visitorBloc.forgetFormKey.currentState!.validate()) { + if (selectedDevices.toString() != '[]') { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return InfoDialog( + size: size, + title: 'Set Password', + content: + 'This action will update all of the selected\n door locks passwords in the property.\n\nAre you sure you want to continue?', + actions: [ + Container( + decoration: containerDecoration, + width: size.width * 0.1, + child: DefaultButton( + borderRadius: 8, + onPressed: () { + Navigator.of(context) + .pop(); // Close the dialog + }, + backgroundColor: Colors.white, + child: Text( + 'Cancel', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor,fontSize: 16), + ), + ), + ), + Container( + decoration: containerDecoration, + width: size.width * 0.1, + child: DefaultButton( + borderRadius: 8, + onPressed: () { + if (visitorBloc.usageFrequencySelected == + 'One-Time' && + visitorBloc.accessTypeSelected == + 'Online Password') { + visitorBloc.add( + OnlineOneTimePasswordEvent( + context: context, + passwordName: visitorBloc + .userNameController.text, + email: visitorBloc + .emailController.text)); + } else if (visitorBloc + .usageFrequencySelected == + 'Periodic' && + visitorBloc.accessTypeSelected == + 'Online Password') { + visitorBloc.add( + OnlineMultipleTimePasswordEvent( + passwordName: + visitorBloc + .userNameController + .text, + email: + visitorBloc + .emailController.text, + effectiveTime: visitorBloc + .effectiveTimeTimeStamp + .toString(), + invalidTime: visitorBloc + .expirationTimeTimeStamp + .toString())); + } else if (visitorBloc + .usageFrequencySelected == + 'One-Time' && + visitorBloc.accessTypeSelected == + 'Offline Password') { + visitorBloc + .add(OfflineOneTimePasswordEvent( + passwordName: visitorBloc + .userNameController.text, + email: + visitorBloc.emailController.text, + )); + } else if (visitorBloc + .usageFrequencySelected == + 'Periodic' && + visitorBloc.accessTypeSelected == + 'Offline Password') { + visitorBloc.add( + OfflineMultipleTimePasswordEvent( + passwordName: visitorBloc + .userNameController.text, + email: visitorBloc + .emailController.text, + effectiveTime: visitorBloc + .effectiveTimeTimeStamp + .toString(), + invalidTime: visitorBloc + .expirationTimeTimeStamp + .toString())); + } + }, + child: const Text( + 'Ok', + ), + ), + ), + ], + ); + }, + ); + } else { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return AlertDialog( + alignment: Alignment.center, + content: SizedBox( + height: size!.height * 0.15, + child: Column( + children: [ + SizedBox( + child: SvgPicture.asset( + Assets.deviceNoteIcon, + height: 35, + width: 35, + ), + ), - actions: [ - Container( - decoration: containerDecoration, - width: size.width * 0.1, - child: DefaultButton( - borderRadius: 8, - onPressed: () { - Navigator.of(context).pop(); // Close the dialog - }, - backgroundColor: Colors.white, - child: Text( - 'Cancel', - style: Theme.of(context).textTheme.bodyMedium!, - ), + const SizedBox( + width: 15, + ), + Text( + 'Please select devices to continue', + style: Theme.of(context) + .textTheme + .headlineLarge! + .copyWith( + fontSize: 20, + fontWeight: FontWeight.w400, + color: Colors.black), + ), + ], ), ), - Container( - decoration: containerDecoration, - width: size.width * 0.1, - child: DefaultButton( - borderRadius: 8, + actionsAlignment: MainAxisAlignment.center, + actions: [ + TextButton( onPressed: () { - if(visitorBloc.usageFrequencySelected=='One-Time'&&visitorBloc.accessTypeSelected=='Online Password'){ - visitorBloc.add(OnlineOneTimePasswordEvent( - passwordName:visitorBloc.userNameController.text , - email: visitorBloc.emailController.text - ) - ); - } - else if(visitorBloc.usageFrequencySelected=='Periodic'&&visitorBloc.accessTypeSelected=='Online Password') { - visitorBloc.add(OnlineMultipleTimePasswordEvent( - passwordName:visitorBloc.userNameController.text , - email: visitorBloc.emailController.text, - effectiveTime:visitorBloc.effectiveTimeTimeStamp.toString() , - invalidTime:visitorBloc.expirationTimeTimeStamp.toString() - ) - ); - } - else if(visitorBloc.usageFrequencySelected=='One-Time'&&visitorBloc.accessTypeSelected=='Offline Password') { - visitorBloc.add(OfflineOneTimePasswordEvent( - passwordName:visitorBloc.userNameController.text , - email: visitorBloc.emailController.text, - ) - ); - } - else if(visitorBloc.usageFrequencySelected=='Periodic'&&visitorBloc.accessTypeSelected=='Offline Password') { - visitorBloc.add(OfflineMultipleTimePasswordEvent( - passwordName:visitorBloc.userNameController.text , - email: visitorBloc.emailController.text, - effectiveTime:visitorBloc.effectiveTimeTimeStamp.toString() , - invalidTime:visitorBloc.expirationTimeTimeStamp.toString() - ) - ); - } + Navigator.of(context).pop(); }, - child: const Text( - 'Ok', - ), + child: Text('OK', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.whiteColors,fontSize: 16),), ), - ), - ],); - }, - ); + ], + ); + }, + ); + } } }, borderRadius: 8, diff --git a/lib/services/access_mang_api.dart b/lib/services/access_mang_api.dart index 38730553..26d7eeea 100644 --- a/lib/services/access_mang_api.dart +++ b/lib/services/access_mang_api.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:dio/dio.dart'; import 'package:flutter/cupertino.dart'; import 'package:syncrow_web/pages/access_management/model/password_model.dart'; import 'package:syncrow_web/pages/visitor_password/model/schedule_model.dart'; @@ -51,15 +52,24 @@ class AccessMangApi{ } } - Future postOnlineOneTime({String? email,String? passwordName,List? devicesUuid}) async { + Future postOnlineOneTime({ + String? email, + String? passwordName, + String? password, + String? effectiveTime, + String? invalidTime, + List? devicesUuid}) async { try { print('postOfflineOneTime List: ${ - { + jsonEncode({ "email": email, "passwordName": passwordName, - "devicesUuid": devicesUuid - } + "password": password, + "devicesUuid": devicesUuid, + "effectiveTime":effectiveTime , + "invalidTime": invalidTime + }) }'); final response = await HTTPService().post( @@ -67,18 +77,27 @@ class AccessMangApi{ body: jsonEncode({ "email": email, "passwordName": passwordName, - "devicesUuid": devicesUuid + "password": password, + "devicesUuid": devicesUuid, + "effectiveTime":effectiveTime , + "invalidTime": invalidTime }), showServerMessage: true, expectedResponseModel: (json) { - List jsonData = json; print('postOfflineOneTime List: $json'); + if(json['statusCode'].toString()=='201'){ + return true; + }else{ + return false; + } }, ); return response; - } catch (e) { - debugPrint('Error fetching $e'); - return []; + } on DioException catch (e) { + debugPrint('Error: ${e.message}'); + + debugPrint('Error fetching ${e.response!.statusMessage}'); + return false; } } @@ -99,25 +118,30 @@ class AccessMangApi{ "effectiveTime": effectiveTime, "invalidTime": invalidTime, }; - print('createPassword =${scheduleList![0].workingDay}'); if (scheduleList != null) { body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList(); } - print('createPassword =$body'); + print('createPassword =${jsonEncode(body)}'); final response = await HTTPService().post( path: ApiEndpoints.sendOnlineMultipleTime, body: jsonEncode(body), showServerMessage: true, expectedResponseModel: (json) { - List jsonData = json; - print('postOfflineOneTime List: $json'); + print('createPassword =${json}'); + + if(json['data']['successOperations'][0]['success'].toString()=='true'){ + return true; + }else{ + return false; + } }, ); return response; - } catch (e) { - debugPrint('Error fetching $e'); - return []; + } on DioException catch (e){ + debugPrint('Error fetching ${e.type.name}'); + debugPrint('Error fetching ${e.response!.statusMessage}'); + return false; } }