From ddcdd4891a2590c76431bbbafe3d2262a36e5cfd Mon Sep 17 00:00:00 2001 From: mohammad Date: Mon, 2 Sep 2024 10:16:28 +0300 Subject: [PATCH 1/8] forget password changes & bugs number 10 & 15-18 --- lib/main.dart | 4 +- .../view/access_management.dart | 31 ++++-- lib/pages/auth/bloc/auth_bloc.dart | 45 ++++---- .../auth/view/forget_password_web_page.dart | 17 +-- lib/pages/common/custom_table.dart | 16 ++- .../text_field/custom_web_textfield.dart | 20 ++-- .../widgets/device_managment_body.dart | 105 +++++++++--------- lib/pages/home/bloc/home_bloc.dart | 1 + .../view/add_device_dialog.dart | 78 ++++++------- .../view/visitor_password_dialog.dart | 5 +- lib/services/auth_api.dart | 17 +-- lib/services/home_api.dart | 1 + 12 files changed, 180 insertions(+), 160 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index b4fcb72d..6e2531a7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -38,6 +38,7 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { + HomeBloc.fetchUserInfo(); return MultiBlocProvider( providers: [ BlocProvider(create: (context) => HomeBloc()), @@ -57,6 +58,7 @@ class MyApp extends StatelessWidget { ), theme: myTheme, routerConfig: _router, - )); + ) + ); } } diff --git a/lib/pages/access_management/view/access_management.dart b/lib/pages/access_management/view/access_management.dart index 26a1dcc2..19c29643 100644 --- a/lib/pages/access_management/view/access_management.dart +++ b/lib/pages/access_management/view/access_management.dart @@ -38,12 +38,22 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout { style: Theme.of(context).textTheme.headlineLarge, ), ), - centerBody: Text( - 'Physical Access', - style: Theme.of(context) - .textTheme - .headlineMedium! - .copyWith(color: Colors.white), + centerBody: Wrap( + children: [ + Padding( + padding: EdgeInsets.only(left: MediaQuery.of(context).size.width*0.09), + child: Align( + alignment: Alignment.bottomLeft, + child: Text( + 'Physical Access', + style: Theme.of(context) + .textTheme + .headlineMedium! + .copyWith(color: Colors.white), + ), + ), + ), + ], ), rightBody: const NavigateHomeGridView(), scaffoldBody: BlocProvider( @@ -82,6 +92,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout { const SizedBox(height: 20), Expanded( child: DynamicTable( + withSelectAll: false, isEmpty: filteredData.isEmpty, withCheckBox: false, size: MediaQuery.of(context).size, @@ -113,8 +124,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout { }))); } - Wrap _buildVisitorAdminPasswords( - BuildContext context, AccessBloc accessBloc) { + Wrap _buildVisitorAdminPasswords(BuildContext context, AccessBloc accessBloc) { return Wrap( spacing: 10, runSpacing: 10, @@ -171,15 +181,14 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout { width: 250, child: CustomWebTextField( controller: accessBloc.passwordName, - height: 38, - isRequired: true, + height: 43, + isRequired: false, textFieldName: 'Name', description: '', ), ), const SizedBox(width: 15), SizedBox( - height: 70, child: DateTimeWebWidget( icon: Assets.calendarIcon, isRequired: false, diff --git a/lib/pages/auth/bloc/auth_bloc.dart b/lib/pages/auth/bloc/auth_bloc.dart index a08cd0d3..455fc324 100644 --- a/lib/pages/auth/bloc/auth_bloc.dart +++ b/lib/pages/auth/bloc/auth_bloc.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; @@ -81,21 +82,25 @@ class AuthBloc extends Bloc { _timer?.cancel(); emit(const TimerState(isButtonEnabled: true, remainingTime: 0)); emit(SuccessForgetState()); - } else if (response == "You entered wrong otp") { - forgetValidate = 'Wrong one time password.'; - emit(AuthInitialState()); - } else if (response == "OTP expired") { - forgetValidate = 'One time password has been expired.'; - emit(AuthInitialState()); } - } catch (failure) { - // forgetValidate='Invalid Credentials!'; - emit(AuthInitialState()); - // emit(FailureForgetState(error: failure.toString())); } + on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + if(errorMessage=='this email is not registered'){ + validate='Invalid Credentials!'; + emit(AuthInitialState()); + }else if (errorMessage == "You entered wrong otp") { + forgetValidate = 'Wrong one time password.'; + emit(AuthInitialState()); + } else if (errorMessage == "OTP expired") { + forgetValidate = 'One time password has been expired.'; + emit(AuthInitialState()); + } + + } } -//925207 String? validateCode(String? value) { if (value == null || value.isEmpty) { return 'Code is required'; @@ -177,15 +182,15 @@ class AuthBloc extends Bloc { emit(LoginInitial()); } - checkOtpCode( - ChangePasswordEvent event, - Emitter emit, - ) async { - emit(LoadingForgetState()); - await AuthenticationAPI.verifyOtp( - email: forgetEmailController.text, otpCode: forgetOtp.text); - emit(SuccessForgetState()); - } + // checkOtpCode( + // ChangePasswordEvent event, + // Emitter emit, + // ) async { + // emit(LoadingForgetState()); + // await AuthenticationAPI.verifyOtp( + // email: forgetEmailController.text, otpCode: forgetOtp.text); + // emit(SuccessForgetState()); + // } void _passwordVisible(PasswordVisibleEvent event, Emitter emit) { emit(AuthLoading()); diff --git a/lib/pages/auth/view/forget_password_web_page.dart b/lib/pages/auth/view/forget_password_web_page.dart index 907569ee..dd930779 100644 --- a/lib/pages/auth/view/forget_password_web_page.dart +++ b/lib/pages/auth/view/forget_password_web_page.dart @@ -346,8 +346,7 @@ class ForgetPasswordWebPage extends StatelessWidget { if (forgetBloc .forgetFormKey.currentState! .validate()) { - forgetBloc - .add(ChangePasswordEvent()); + forgetBloc.add(ChangePasswordEvent()); } }, ), @@ -355,12 +354,14 @@ class ForgetPasswordWebPage extends StatelessWidget { ], ), const SizedBox(height: 10.0), - SizedBox( - child: Text( - forgetBloc.validate, - style: const TextStyle( - fontWeight: FontWeight.w700, - color: ColorsManager.red), + Center( + child: SizedBox( + child: Text( + forgetBloc.validate, + style: const TextStyle( + fontWeight: FontWeight.w700, + color: ColorsManager.red), + ), ), ), SizedBox( diff --git a/lib/pages/common/custom_table.dart b/lib/pages/common/custom_table.dart index 5308e450..281a3bbb 100644 --- a/lib/pages/common/custom_table.dart +++ b/lib/pages/common/custom_table.dart @@ -10,6 +10,7 @@ class DynamicTable extends StatefulWidget { final BoxDecoration? cellDecoration; final Size size; final bool withCheckBox; + final bool withSelectAll; final bool isEmpty; final void Function(bool?)? selectAll; final void Function(int, bool, dynamic)? onRowSelected; @@ -21,6 +22,7 @@ class DynamicTable extends StatefulWidget { required this.size, required this.isEmpty, required this.withCheckBox, + required this.withSelectAll, this.headerDecoration, this.cellDecoration, this.selectAll, @@ -34,6 +36,7 @@ class DynamicTable extends StatefulWidget { class _DynamicTableState extends State { late List _selected; + bool _selectAll = false; @override void initState() { @@ -54,6 +57,17 @@ class _DynamicTableState extends State { }); } + + void _toggleSelectAll(bool? value) { + setState(() { + _selectAll = value ?? false; + _selected = List.filled(widget.data.length, _selectAll); + if (widget.selectAll != null) { + widget.selectAll!(_selectAll); + } + }); + } + @override Widget build(BuildContext context) { return Container( @@ -148,7 +162,7 @@ class _DynamicTableState extends State { ), child: Checkbox( value: _selected.every((element) => element == true), - onChanged: null, + onChanged:widget.withSelectAll?_toggleSelectAll:null, ), ); } diff --git a/lib/pages/common/text_field/custom_web_textfield.dart b/lib/pages/common/text_field/custom_web_textfield.dart index 926a20f5..756463e2 100644 --- a/lib/pages/common/text_field/custom_web_textfield.dart +++ b/lib/pages/common/text_field/custom_web_textfield.dart @@ -32,21 +32,19 @@ class CustomWebTextField extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - if (isRequired) + Row( children: [ - Text( - '* ', + if (isRequired) + Text('* ', style: Theme.of(context) - .textTheme - .bodyMedium! + .textTheme.bodyMedium! .copyWith(color: Colors.red), ), Text( textFieldName, style: Theme.of(context) - .textTheme - .bodySmall! + .textTheme.bodySmall! .copyWith(color: Colors.black, fontSize: 13), ), ], @@ -70,15 +68,17 @@ class CustomWebTextField extends StatelessWidget { ), Container( height: height ?? 35, - decoration: containerDecoration - .copyWith(color: const Color(0xFFF5F6F7), boxShadow: [ + decoration: containerDecoration.copyWith( + color: const Color(0xFFF5F6F7), + boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.3), spreadRadius: 2, blurRadius: 3, offset: const Offset(1, 1), // changes position of shadow ), - ]), + ] + ), child: TextFormField( validator: validator, controller: controller, diff --git a/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart b/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart index 2e9d916a..e4eb7966 100644 --- a/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart +++ b/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart @@ -51,71 +51,68 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { 'Low Battery ($lowBatteryCount)', ]; - return CustomScrollView( - slivers: [ - SliverToBoxAdapter( - child: Container( - padding: isLargeScreenSize(context) - ? const EdgeInsets.all(30) - : const EdgeInsets.all(15), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FilterWidget( - size: MediaQuery.of(context).size, - tabs: tabs, - selectedIndex: selectedIndex, - onTabChanged: (index) { - context - .read() - .add(SelectedFilterChanged(index)); - }, - ), - const SizedBox(height: 20), - const DeviceSearchFilters(), - const SizedBox(height: 12), - Container( - height: 45, - width: 100, - decoration: containerDecoration, - child: Center( - child: DefaultButton( - onPressed: isControlButtonEnabled - ? () { - final selectedDevice = context - .read() - .selectedDevices - .first; - showDialog( - context: context, - builder: (context) => DeviceControlDialog( - device: selectedDevice), - ); - } - : null, - borderRadius: 9, - child: Text( - 'Control', - style: TextStyle( - fontSize: 12, - color: isControlButtonEnabled - ? Colors.white - : Colors.grey, - ), + return Column( + children: [ + Container( + padding: isLargeScreenSize(context) + ? const EdgeInsets.all(30) + : const EdgeInsets.all(15), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FilterWidget( + size: MediaQuery.of(context).size, + tabs: tabs, + selectedIndex: selectedIndex, + onTabChanged: (index) { + context.read() + .add(SelectedFilterChanged(index)); + }, + ), + const SizedBox(height: 20), + const DeviceSearchFilters(), + const SizedBox(height: 12), + Container( + height: 45, + width: 100, + decoration: containerDecoration, + child: Center( + child: DefaultButton( + onPressed: isControlButtonEnabled + ? () { + final selectedDevice = context + .read() + .selectedDevices.first; + showDialog( + context: context, + builder: (context) => DeviceControlDialog( + device: selectedDevice), + ); + } + : null, + borderRadius: 9, + child: Text( + 'Control', + style: TextStyle( + fontSize: 12, + color: isControlButtonEnabled + ? Colors.white + : Colors.grey, ), ), ), ), - ], - ), + ), + ], ), ), - SliverFillRemaining( + Expanded( child: Padding( padding: isLargeScreenSize(context) ? const EdgeInsets.all(30) : const EdgeInsets.all(15), child: DynamicTable( + withSelectAll: false, cellDecoration: containerDecoration, onRowSelected: (index, isSelected, row) { final selectedDevice = devicesToShow[index]; diff --git a/lib/pages/home/bloc/home_bloc.dart b/lib/pages/home/bloc/home_bloc.dart index 79579efc..1de54eff 100644 --- a/lib/pages/home/bloc/home_bloc.dart +++ b/lib/pages/home/bloc/home_bloc.dart @@ -43,6 +43,7 @@ class HomeBloc extends Bloc { try { var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); user = await HomeApi().fetchUserInfo(uuid); + } catch (e) { return; } diff --git a/lib/pages/visitor_password/view/add_device_dialog.dart b/lib/pages/visitor_password/view/add_device_dialog.dart index 85262ff1..3bedbd2f 100644 --- a/lib/pages/visitor_password/view/add_device_dialog.dart +++ b/lib/pages/visitor_password/view/add_device_dialog.dart @@ -40,7 +40,7 @@ class AddDeviceDialog extends StatelessWidget { fontSize: 24, color: Colors.black), ), - content: Container( + content: SizedBox( height: MediaQuery.of(context).size.height / 1.7, width: MediaQuery.of(context).size.width / 2, child: Padding( @@ -78,7 +78,7 @@ class AddDeviceDialog extends StatelessWidget { ), ], )), - SizedBox( + const SizedBox( height: 20, ), const SizedBox( @@ -93,7 +93,7 @@ class AddDeviceDialog extends StatelessWidget { flex: 4, child: CustomWebTextField( controller: visitorBloc.deviceNameController, - isRequired: true, + isRequired: false, textFieldName: 'Device Name', description: '', ), @@ -103,7 +103,7 @@ class AddDeviceDialog extends StatelessWidget { flex: 4, child: CustomWebTextField( controller: visitorBloc.deviceIdController, - isRequired: true, + isRequired: false, textFieldName: 'Device ID', description: '', ), @@ -113,7 +113,7 @@ class AddDeviceDialog extends StatelessWidget { flex: 4, child: CustomWebTextField( controller: visitorBloc.unitNameController, - isRequired: true, + isRequired: false, textFieldName: 'Unit Name', description: '', ), @@ -168,40 +168,40 @@ class AddDeviceDialog extends StatelessWidget { flex: 3, child: state is TableLoaded ? DynamicTable( - initialSelectedIds: selectedDeviceIds, - cellDecoration: containerDecoration, - isEmpty: visitorBloc.data.isEmpty, - selectAll: (p0) { - visitorBloc.selectedDeviceIds.clear(); - for (var item in state.data) { - visitorBloc - .add(SelectDeviceEvent(item.uuid)); - } - }, - onRowSelected: (index, isSelected, row) { - final deviceId = state.data[index].uuid; - visitorBloc.add(SelectDeviceEvent(deviceId)); - }, - withCheckBox: true, - size: size * 0.5, - headers: const [ - 'Device Name', - 'Device ID', - 'Access Type', - 'Unit Name', - 'Status' - ], - data: state.data.map((item) { - return [ - item.name.toString(), - item.uuid.toString(), - item.productType.toString(), - '', - item.online.value.toString(), - ]; - }).toList(), - ) - : const Center(child: CircularProgressIndicator())) + withSelectAll: true, + + initialSelectedIds: selectedDeviceIds, + cellDecoration: containerDecoration, + isEmpty: visitorBloc.data.isEmpty, + selectAll: (p0) { + visitorBloc.selectedDeviceIds.clear(); + for (var item in state.data) { + visitorBloc.add(SelectDeviceEvent(item.uuid)); + } + }, + onRowSelected: (index, isSelected, row) { + final deviceId = state.data[index].uuid; + visitorBloc.add(SelectDeviceEvent(deviceId)); + }, + withCheckBox: true, + size: size * 0.5, + headers: const [ + 'Device Name', + 'Device ID', + 'Access Type', + 'Unit Name', + 'Status' + ], + data: state.data.map((item) { + return [ + item.name.toString(), + item.uuid.toString(), + item.productType.toString(), + '', + item.online.value.toString(), + ]; + }).toList(), + ) : const Center(child: CircularProgressIndicator())) ], ), ), diff --git a/lib/pages/visitor_password/view/visitor_password_dialog.dart b/lib/pages/visitor_password/view/visitor_password_dialog.dart index 591b6ff6..420fd3de 100644 --- a/lib/pages/visitor_password/view/visitor_password_dialog.dart +++ b/lib/pages/visitor_password/view/visitor_password_dialog.dart @@ -323,8 +323,7 @@ class VisitorPasswordDialog extends StatelessWidget { : visitorBloc.usageFrequencySelected, onChanged: (String? value) { if (value != null) { - context - .read() + context.read() .add(SelectUsageFrequency(value)); } }, @@ -344,7 +343,7 @@ class VisitorPasswordDialog extends StatelessWidget { if (visitorBloc.usageFrequencySelected == 'One-Time' && visitorBloc.accessTypeSelected == 'Offline Password') Text( - 'Within the validity period, there is no limit to the number of times each device can be unlocked.', + 'Within the validity period, each device can be unlocked only once, and the maximum validity period is 6 hours', style: Theme.of(context).textTheme.bodySmall!.copyWith( color: ColorsManager.grayColor, fontSize: 9), ), diff --git a/lib/services/auth_api.dart b/lib/services/auth_api.dart index 6573f3ad..844689df 100644 --- a/lib/services/auth_api.dart +++ b/lib/services/auth_api.dart @@ -25,7 +25,9 @@ class AuthenticationAPI { path: ApiEndpoints.forgetPassword, body: {"email": email, "password": password}, showServerMessage: true, - expectedResponseModel: (json) {}); + expectedResponseModel: (json) { + print('json=$json'); + }); return response; } @@ -66,7 +68,6 @@ class AuthenticationAPI { } static Future verifyOtp({required String email, required String otpCode}) async { - try { final response = await HTTPService().post( path: ApiEndpoints.verifyOtp, body: {"email": email, "type": "PASSWORD", "otpCode": otpCode}, @@ -79,17 +80,7 @@ class AuthenticationAPI { } }); return response; - } on DioException catch (e) { - if (e.response != null) { - if (e.response!.statusCode == 400) { - final errorData = e.response!.data; - String errorMessage = errorData['message']; - return errorMessage; - } - } else { - debugPrint('Error: ${e.message}'); - } - } + } static Future> fetchRegion() async { diff --git a/lib/services/home_api.dart b/lib/services/home_api.dart index dfbaf4bf..000d4ff2 100644 --- a/lib/services/home_api.dart +++ b/lib/services/home_api.dart @@ -8,6 +8,7 @@ class HomeApi { path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!), showServerMessage: true, expectedResponseModel: (json) { + print('fetchUserInfo$json'); return UserModel.fromJson(json); }); return response; From 00bce2d0ab707a2c872eb0d45c1265e3e4e70bf1 Mon Sep 17 00:00:00 2001 From: mohammad Date: Mon, 2 Sep 2024 14:52:06 +0300 Subject: [PATCH 2/8] fix pugs --- .../access_management/bloc/access_bloc.dart | 34 +++++++++++-------- .../model/password_model.dart | 2 +- .../view/access_management.dart | 3 +- lib/pages/auth/bloc/auth_event.dart | 8 +++-- lib/pages/common/custom_table.dart | 5 ++- .../bloc/visitor_password_bloc.dart | 5 +-- .../view/visitor_password_dialog.dart | 22 ++++++------ lib/services/access_mang_api.dart | 7 ++++ 8 files changed, 54 insertions(+), 32 deletions(-) diff --git a/lib/pages/access_management/bloc/access_bloc.dart b/lib/pages/access_management/bloc/access_bloc.dart index fb66408a..94b0992f 100644 --- a/lib/pages/access_management/bloc/access_bloc.dart +++ b/lib/pages/access_management/bloc/access_bloc.dart @@ -107,26 +107,28 @@ class AccessBloc extends Bloc { Future _filterData(FilterDataEvent event, Emitter emit) async { emit(AccessLoaded()); try { + // Convert search text to lower case for case-insensitive search + final searchText = event.passwordName?.toLowerCase() ?? ''; + filteredData = data.where((item) { bool matchesCriteria = true; // Convert timestamp to DateTime and extract date component DateTime effectiveDate = - DateTime.fromMillisecondsSinceEpoch(int.parse(item.effectiveTime.toString()) * 1000) - .toUtc() - .toLocal(); + DateTime.fromMillisecondsSinceEpoch(int.parse(item.effectiveTime.toString()) * 1000) + .toUtc() + .toLocal(); DateTime invalidDate = - DateTime.fromMillisecondsSinceEpoch(int.parse(item.invalidTime.toString()) * 1000) - .toUtc() - .toLocal(); + DateTime.fromMillisecondsSinceEpoch(int.parse(item.invalidTime.toString()) * 1000) + .toUtc() + .toLocal(); DateTime effectiveDateOnly = - DateTime(effectiveDate.year, effectiveDate.month, effectiveDate.day); + DateTime(effectiveDate.year, effectiveDate.month, effectiveDate.day); DateTime invalidDateOnly = DateTime(invalidDate.year, invalidDate.month, invalidDate.day); - // Filter by password name - if (event.passwordName != null && event.passwordName!.isNotEmpty) { - final bool matchesName = - item.passwordName != null && item.passwordName.contains(event.passwordName); + // Filter by password name, making the search case-insensitive + if (searchText.isNotEmpty) { + final bool matchesName = item.passwordName.toString().toLowerCase().contains(searchText); if (!matchesName) { matchesCriteria = false; } @@ -135,7 +137,7 @@ class AccessBloc extends Bloc { // Filter by start date only if (event.startTime != null && event.endTime == null) { DateTime startDateOnly = - DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000).toUtc().toLocal(); + DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000).toUtc().toLocal(); startDateOnly = DateTime(startDateOnly.year, startDateOnly.month, startDateOnly.day); if (effectiveDateOnly.isBefore(startDateOnly)) { matchesCriteria = false; @@ -145,7 +147,7 @@ class AccessBloc extends Bloc { // Filter by end date only if (event.endTime != null && event.startTime == null) { DateTime endDateOnly = - DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000).toUtc().toLocal(); + DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000).toUtc().toLocal(); endDateOnly = DateTime(endDateOnly.year, endDateOnly.month, endDateOnly.day); if (invalidDateOnly.isAfter(endDateOnly)) { matchesCriteria = false; @@ -155,9 +157,9 @@ class AccessBloc extends Bloc { // Filter by both start date and end date if (event.startTime != null && event.endTime != null) { DateTime startDateOnly = - DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000).toUtc().toLocal(); + DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000).toUtc().toLocal(); DateTime endDateOnly = - DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000).toUtc().toLocal(); + DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000).toUtc().toLocal(); startDateOnly = DateTime(startDateOnly.year, startDateOnly.month, startDateOnly.day); endDateOnly = DateTime(endDateOnly.year, endDateOnly.month, endDateOnly.day); if (effectiveDateOnly.isBefore(startDateOnly) || invalidDateOnly.isAfter(endDateOnly)) { @@ -183,6 +185,8 @@ class AccessBloc extends Bloc { } } + + resetSearch(ResetSearch event, Emitter emit) async { emit(AccessLoaded()); startTime = 'Start Time'; diff --git a/lib/pages/access_management/model/password_model.dart b/lib/pages/access_management/model/password_model.dart index 8436ef56..50c03090 100644 --- a/lib/pages/access_management/model/password_model.dart +++ b/lib/pages/access_management/model/password_model.dart @@ -30,7 +30,7 @@ class PasswordModel { effectiveTime: json['effectiveTime'], passwordCreated: json['passwordCreated'], createdTime: json['createdTime'], - passwordName: json['passwordName'] ?? 'No name', // New field + passwordName: json['passwordName']??'No Name', passwordStatus: AccessStatusExtension.fromString(json['passwordStatus']), passwordType: AccessTypeExtension.fromString(json['passwordType']), deviceUuid: json['deviceUuid'], diff --git a/lib/pages/access_management/view/access_management.dart b/lib/pages/access_management/view/access_management.dart index 19c29643..48811d32 100644 --- a/lib/pages/access_management/view/access_management.dart +++ b/lib/pages/access_management/view/access_management.dart @@ -92,6 +92,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout { const SizedBox(height: 20), Expanded( child: DynamicTable( + tableName:'AccessManagement', withSelectAll: false, isEmpty: filteredData.isEmpty, withCheckBox: false, @@ -108,7 +109,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout { ], data: filteredData.map((item) { return [ - item.passwordName.toString(), + item.passwordName, item.passwordType.value, ('${accessBloc.timestampToDate(item.effectiveTime)} - ${accessBloc.timestampToDate(item.invalidTime)}'), item.deviceUuid.toString(), diff --git a/lib/pages/auth/bloc/auth_event.dart b/lib/pages/auth/bloc/auth_event.dart index 0026554c..fa7e86b3 100644 --- a/lib/pages/auth/bloc/auth_event.dart +++ b/lib/pages/auth/bloc/auth_event.dart @@ -49,9 +49,13 @@ class UpdateTimerEvent extends AuthEvent { const UpdateTimerEvent({required this.remainingTime, required this.isButtonEnabled}); } -class ChangePasswordEvent extends AuthEvent {} +class ChangePasswordEvent extends AuthEvent { -class SendOtpEvent extends AuthEvent {} +} + +class SendOtpEvent extends AuthEvent { + +} class PasswordVisibleEvent extends AuthEvent { final bool? newValue; diff --git a/lib/pages/common/custom_table.dart b/lib/pages/common/custom_table.dart index 281a3bbb..82228da6 100644 --- a/lib/pages/common/custom_table.dart +++ b/lib/pages/common/custom_table.dart @@ -5,6 +5,7 @@ import 'package:syncrow_web/utils/constants/assets.dart'; class DynamicTable extends StatefulWidget { final List headers; + final String? tableName; final List> data; final BoxDecoration? headerDecoration; final BoxDecoration? cellDecoration; @@ -20,6 +21,7 @@ class DynamicTable extends StatefulWidget { required this.headers, required this.data, required this.size, + this.tableName, required this.isEmpty, required this.withCheckBox, required this.withSelectAll, @@ -106,7 +108,8 @@ class _DynamicTableState extends State { height: 15, ), Text( - 'No Devices', + // no password + widget.tableName=='AccessManagement'? 'No Password ' : 'No Devices', style: Theme.of(context) .textTheme .bodySmall! diff --git a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart index f1dbe47c..02f5ef0a 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart @@ -219,8 +219,8 @@ class VisitorPasswordBloc extends Bloc[ TextButton( onPressed: () { + Navigator.of(context).pop(true); }, child: const Text('OK'), diff --git a/lib/pages/visitor_password/view/visitor_password_dialog.dart b/lib/pages/visitor_password/view/visitor_password_dialog.dart index 420fd3de..5fc82dbf 100644 --- a/lib/pages/visitor_password/view/visitor_password_dialog.dart +++ b/lib/pages/visitor_password/view/visitor_password_dialog.dart @@ -87,7 +87,8 @@ class VisitorPasswordDialog extends StatelessWidget { ], )) .then((v) { - Navigator.of(context).pop(); + Navigator.of(context).pop(true); + }); } else if (state is FailedState) { visitorBloc.stateDialog( @@ -379,11 +380,9 @@ class VisitorPasswordDialog extends StatelessWidget { endTime: () { if (visitorBloc.usageFrequencySelected == 'Periodic' && visitorBloc.accessTypeSelected == 'Offline Password') { - visitorBloc.add( - SelectTimeEvent(context: context, isEffective: false)); + visitorBloc.add(SelectTimeEvent(context: context, isEffective: false)); } else { - visitorBloc.add(SelectTimeVisitorPassword( - context: context, isStart: false, isRepeat: false)); + visitorBloc.add(SelectTimeVisitorPassword(context: context, isStart: false, isRepeat: false)); } }, startTime: () { @@ -397,13 +396,11 @@ class VisitorPasswordDialog extends StatelessWidget { } }, firstString: (visitorBloc.usageFrequencySelected == - 'Periodic' && - visitorBloc.accessTypeSelected == 'Offline Password') + 'Periodic' && visitorBloc.accessTypeSelected == 'Offline Password') ? visitorBloc.effectiveTime : visitorBloc.startTimeAccess.toString(), secondString: (visitorBloc.usageFrequencySelected == - 'Periodic' && - visitorBloc.accessTypeSelected == 'Offline Password') + 'Periodic' && visitorBloc.accessTypeSelected == 'Offline Password') ? visitorBloc.expirationTime : visitorBloc.endTimeAccess.toString(), icon: Assets.calendarIcon), @@ -529,7 +526,7 @@ class VisitorPasswordDialog extends StatelessWidget { setPasswordFunction(context, size, visitorBloc); } else if (visitorBloc.accessTypeSelected == 'Dynamic Password') { setPasswordFunction(context, size, visitorBloc); - } else { + } else if(visitorBloc.endTimeAccess.toString()!='End Time'&&visitorBloc.startTimeAccess.toString()!='Start Time') { if (visitorBloc.effectiveTimeTimeStamp != null && visitorBloc.expirationTimeTimeStamp != null) { if (isRepeat == true) { @@ -553,6 +550,11 @@ class VisitorPasswordDialog extends StatelessWidget { message: 'Please select Access Period to continue', title: 'Access Period'); } + }else{ + visitorBloc.stateDialog( + context: context, + message: 'Please select Access Period to continue', + title: 'Access Period'); } } else { visitorBloc.stateDialog( diff --git a/lib/services/access_mang_api.dart b/lib/services/access_mang_api.dart index 3309e253..68ea4f8d 100644 --- a/lib/services/access_mang_api.dart +++ b/lib/services/access_mang_api.dart @@ -123,6 +123,13 @@ class AccessMangApi { String? effectiveTime, String? invalidTime, List? devicesUuid}) async { + print('object=== ${ jsonEncode({ + "email": email, + "devicesUuid": devicesUuid, + "passwordName": passwordName, + "effectiveTime": effectiveTime, + "invalidTime": invalidTime, + })}'); final response = await HTTPService().post( path: ApiEndpoints.sendOffLineMultipleTime, body: jsonEncode({ From 418f5d5406d727e0017edc6ae97c8510877c9a37 Mon Sep 17 00:00:00 2001 From: mohammad Date: Mon, 2 Sep 2024 16:59:25 +0300 Subject: [PATCH 3/8] login Enhancements --- lib/pages/auth/view/login_web_page.dart | 290 +++++++++++++++++------- pubspec.lock | 16 ++ pubspec.yaml | 2 + 3 files changed, 221 insertions(+), 87 deletions(-) diff --git a/lib/pages/auth/view/login_web_page.dart b/lib/pages/auth/view/login_web_page.dart index 1c638287..13492c45 100644 --- a/lib/pages/auth/view/login_web_page.dart +++ b/lib/pages/auth/view/login_web_page.dart @@ -1,3 +1,4 @@ +import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -23,7 +24,8 @@ class LoginWebPage extends StatefulWidget { State createState() => _LoginWebPageState(); } -class _LoginWebPageState extends State with HelperResponsiveLayout { +class _LoginWebPageState extends State + with HelperResponsiveLayout { @override Widget build(BuildContext context) { return Scaffold( @@ -58,7 +60,8 @@ class _LoginWebPageState extends State with HelperResponsiveLayout _scrollController = ScrollController(); void _scrollToCenter() { - final double middlePosition = _scrollController.position.maxScrollExtent / 2; + final double middlePosition = + _scrollController.position.maxScrollExtent / 2; _scrollController.animateTo( middlePosition, duration: const Duration(seconds: 1), @@ -120,7 +123,8 @@ class _LoginWebPageState extends State with HelperResponsiveLayout const Spacer(), Expanded( flex: 2, - child: _buildLoginFormFields(context, loginBloc, size), + child: _buildLoginFormFields( + context, loginBloc, size), ), const Spacer(), ], @@ -131,12 +135,14 @@ class _LoginWebPageState extends State with HelperResponsiveLayout ), ), ), - if (state is AuthLoading) const Center(child: CircularProgressIndicator()) + if (state is AuthLoading) + const Center(child: CircularProgressIndicator()) ], ); } - Widget _buildLoginFormFields(BuildContext context, AuthBloc loginBloc, Size size) { + Widget _buildLoginFormFields( + BuildContext context, AuthBloc loginBloc, Size size) { return Container( decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), @@ -146,8 +152,8 @@ class _LoginWebPageState extends State with HelperResponsiveLayout child: Form( key: loginBloc.loginFormKey, child: Padding( - padding: - EdgeInsets.symmetric(horizontal: size.width * 0.02, vertical: size.width * 0.003), + padding: EdgeInsets.symmetric( + horizontal: size.width * 0.02, vertical: size.width * 0.003), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, @@ -175,82 +181,190 @@ class _LoginWebPageState extends State with HelperResponsiveLayout ); } - Widget _buildDropdownField(BuildContext context, AuthBloc loginBloc, Size size) { + // Widget _buildDropdownField(BuildContext context, AuthBloc loginBloc, Size size) { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Text( + // "Country/Region", + // style: Theme.of(context) + // .textTheme + // .bodySmall! + // .copyWith(fontSize: 14, fontWeight: FontWeight.w400), + // ), + // const SizedBox(height: 10), + // SizedBox( + // width: size.width * 0.8, + // child: LayoutBuilder( + // builder: (context, constraints) { + // return DropdownButtonFormField( + // value: loginBloc.regionList!.any((region) => region.id == loginBloc.regionUuid) + // ? loginBloc.regionUuid + // : null, + // validator: loginBloc.validateRegion, + // icon: const Icon( + // Icons.keyboard_arrow_down_outlined, + // size: 20, + // ), + // decoration: textBoxDecoration()!.copyWith( + // errorStyle: const TextStyle(height: 0), + // contentPadding: const EdgeInsets.symmetric( + // vertical: 12, + // horizontal: 10, + // ), + // ), + // hint: Text( + // 'Select your region/country', + // style: Theme.of(context) + // .textTheme + // .bodySmall! + // .copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400), + // overflow: TextOverflow.ellipsis, + // ), + // isDense: true, + // style: const TextStyle(color: Colors.black), + // items: loginBloc.regionList!.map((RegionModel region) { + // return DropdownMenuItem( + // value: region.id, + // child: Container( + // constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40), + // child: Text( + // region.name, + // overflow: TextOverflow.ellipsis, + // maxLines: 1, + // ), + // ), + // ); + // }).toList(), + // onChanged: (String? value) { + // loginBloc.add(CheckEnableEvent()); + // loginBloc.add(SelectRegionEvent(val: value!)); + // }, + // dropdownColor: Colors.white, + // menuMaxHeight: size.height * 0.45, + // selectedItemBuilder: (context) { + // return loginBloc.regionList!.map((region) { + // return Container( + // constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40), + // child: Text( + // region.name, + // overflow: TextOverflow.ellipsis, + // maxLines: 1, + // ), + // ); + // }).toList(); + // }, + // ); + // }, + // ), + // ), + // ], + // ); + // } + + Widget _buildDropdownField( + BuildContext context, AuthBloc loginBloc, Size size) { + final TextEditingController textEditingController = TextEditingController(); return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text( "Country/Region", - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(fontSize: 14, fontWeight: FontWeight.w400), + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontSize: 14, + fontWeight: FontWeight.w400, + ), ), const SizedBox(height: 10), - SizedBox( - width: size.width * 0.8, - child: LayoutBuilder( - builder: (context, constraints) { - return DropdownButtonFormField( - value: loginBloc.regionList!.any((region) => region.id == loginBloc.regionUuid) - ? loginBloc.regionUuid - : null, - validator: loginBloc.validateRegion, - icon: const Icon( - Icons.keyboard_arrow_down_outlined, - size: 20, - ), - decoration: textBoxDecoration()!.copyWith( - errorStyle: const TextStyle(height: 0), - contentPadding: const EdgeInsets.symmetric( - vertical: 12, - horizontal: 10, + Container( + height: size.height * 0.05, + decoration:const BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.all(Radius.circular(8))), + width: size.width * 0.9, + child: DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + hint: Text( + 'Select your region/country', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor, + fontWeight: FontWeight.w400, + ), + overflow: TextOverflow.ellipsis, + ), + items: loginBloc.regionList!.map((RegionModel region) { + return DropdownMenuItem( + value: region.id, + child: Text( + region.name, + overflow: TextOverflow.ellipsis, + maxLines: 1, ), - ), - hint: Text( - 'Select your region/country', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400), - overflow: TextOverflow.ellipsis, - ), - isDense: true, - style: const TextStyle(color: Colors.black), - items: loginBloc.regionList!.map((RegionModel region) { - return DropdownMenuItem( - value: region.id, - child: Container( - constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40), - child: Text( - region.name, - overflow: TextOverflow.ellipsis, - maxLines: 1, + ); + }).toList(), + value: loginBloc.regionList!.any( + (region) => region.id == loginBloc.regionUuid, + ) + ? loginBloc.regionUuid + : null, + onChanged: (String? value) { + if (value != null) { + loginBloc.add(CheckEnableEvent()); + loginBloc.add(SelectRegionEvent(val: value)); + } + }, + buttonStyleData: const ButtonStyleData( + padding: EdgeInsets.symmetric(horizontal: 16), + height: 40, + width: double.infinity, + ), + dropdownStyleData: DropdownStyleData( + maxHeight: size.height * 0.45, + ), + menuItemStyleData: const MenuItemStyleData( + height: 40, + ), + dropdownSearchData: DropdownSearchData( + searchController: textEditingController, + searchInnerWidgetHeight: 50, + searchInnerWidget: Container( + height: 50, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: TextFormField( + style: TextStyle(color: Colors.black), + controller: textEditingController, + decoration: textBoxDecoration()!.copyWith( + errorStyle: const TextStyle(height: 0), + contentPadding: const EdgeInsets.symmetric( + vertical: 12, + horizontal: 10, ), ), - ); - }).toList(), - onChanged: (String? value) { - loginBloc.add(CheckEnableEvent()); - loginBloc.add(SelectRegionEvent(val: value!)); + ), + ), + + searchMatchFn: (item, searchValue) { + // Ensure both item value and search value are compared in lowercase for a case-insensitive match. + final itemValue = item.value?.toLowerCase() ?? ''; + final search = searchValue.toLowerCase().trim(); + + // Debugging print statement to ensure values are captured correctly. + print('searchValue == $search'); + + // Return true if the item value contains the search term. + return itemValue.contains(search); }, - dropdownColor: Colors.white, - menuMaxHeight: size.height * 0.45, - selectedItemBuilder: (context) { - return loginBloc.regionList!.map((region) { - return Container( - constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40), - child: Text( - region.name, - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), - ); - }).toList(); - }, - ); - }, + + ), + onMenuStateChange: (isOpen) { + if (!isOpen) { + textEditingController.clear(); + } + }, + ), ), ), ], @@ -280,10 +394,9 @@ class _LoginWebPageState extends State with HelperResponsiveLayout decoration: textBoxDecoration()!.copyWith( errorStyle: const TextStyle(height: 0), hintText: 'Enter your email address', - hintStyle: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400)), + hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor, + fontWeight: FontWeight.w400)), style: const TextStyle(color: Colors.black), ), ), @@ -315,17 +428,18 @@ class _LoginWebPageState extends State with HelperResponsiveLayout controller: loginBloc.loginPasswordController, decoration: textBoxDecoration()!.copyWith( hintText: 'At least 8 characters', - hintStyle: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400), + hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor, fontWeight: FontWeight.w400), suffixIcon: IconButton( onPressed: () { - loginBloc.add(PasswordVisibleEvent(newValue: loginBloc.obscureText)); + loginBloc.add( + PasswordVisibleEvent(newValue: loginBloc.obscureText)); }, icon: SizedBox( child: SvgPicture.asset( - loginBloc.obscureText ? Assets.visiblePassword : Assets.invisiblePassword, + loginBloc.obscureText + ? Assets.visiblePassword + : Assets.invisiblePassword, height: 15, width: 15, ), @@ -353,10 +467,10 @@ class _LoginWebPageState extends State with HelperResponsiveLayout }, child: Text( "Forgot Password?", - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400), + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black, + fontSize: 14, + fontWeight: FontWeight.w400), ), ), ], @@ -419,7 +533,8 @@ class _LoginWebPageState extends State with HelperResponsiveLayout ); } - Widget _buildSignInButton(BuildContext context, AuthBloc loginBloc, Size size) { + Widget _buildSignInButton( + BuildContext context, AuthBloc loginBloc, Size size) { return Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, @@ -460,7 +575,8 @@ class _LoginWebPageState extends State with HelperResponsiveLayout SizedBox( child: Text( loginBloc.validate, - style: const TextStyle(fontWeight: FontWeight.w700, color: ColorsManager.red), + style: const TextStyle( + fontWeight: FontWeight.w700, color: ColorsManager.red), ), ) ], diff --git a/pubspec.lock b/pubspec.lock index 9a9cd6a8..3da608f2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -89,6 +89,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + dropdown_button2: + dependency: "direct main" + description: + name: dropdown_button2 + sha256: b0fe8d49a030315e9eef6c7ac84ca964250155a6224d491c1365061bc974a9e1 + url: "https://pub.dev" + source: hosted + version: "2.3.9" + dropdown_search: + dependency: "direct main" + description: + name: dropdown_search + sha256: "55106e8290acaa97ed15bea1fdad82c3cf0c248dd410e651f5a8ac6870f783ab" + url: "https://pub.dev" + source: hosted + version: "5.0.6" equatable: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index a8e96a9b..ea0138ea 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,9 +43,11 @@ dependencies: get_it: ^7.6.7 flutter_secure_storage: ^9.2.2 shared_preferences: ^2.3.0 + dropdown_button2: ^2.3.9 data_table_2: ^2.5.15 go_router: intl: ^0.19.0 + dropdown_search: ^5.0.6 dev_dependencies: flutter_test: From 8130acc392a7e5483656e74f58fdc1b1c2ae52ae Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 3 Sep 2024 08:58:48 +0300 Subject: [PATCH 4/8] login Enhancements and add search to forget password --- .../auth/view/forget_password_web_page.dart | 145 +++++++++++++----- lib/pages/auth/view/login_web_page.dart | 126 +++------------ 2 files changed, 128 insertions(+), 143 deletions(-) diff --git a/lib/pages/auth/view/forget_password_web_page.dart b/lib/pages/auth/view/forget_password_web_page.dart index dd930779..5208a6b3 100644 --- a/lib/pages/auth/view/forget_password_web_page.dart +++ b/lib/pages/auth/view/forget_password_web_page.dart @@ -1,3 +1,4 @@ +import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -148,44 +149,7 @@ class ForgetPasswordWebPage extends StatelessWidget { ), const SizedBox(height: 10), SizedBox( - child: DropdownButtonFormField( - validator: forgetBloc.validateRegion, - icon: const Icon( - Icons.keyboard_arrow_down_outlined, - ), - decoration: - textBoxDecoration()!.copyWith( - hintText: null, - ), - hint: SizedBox( - width: size.width * 0.12, - child: const Align( - alignment: Alignment.centerLeft, - child: Text( - 'Select your region/country', - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - ), - ), - ), - isDense: true, - style: const TextStyle( - color: Colors.black), - items: forgetBloc.regionList! - .map((RegionModel region) { - return DropdownMenuItem( - value: region.id, - child: SizedBox( - width: size.width * 0.06, - child: Text(region.name)), - ); - }).toList(), - onChanged: (String? value) { - forgetBloc.add(SelectRegionEvent( - val: value!, - )); - }, - ), + child: _buildDropdownField(context, forgetBloc, size) ) ], ), @@ -411,4 +375,109 @@ class ForgetPasswordWebPage extends StatelessWidget { ), )); } + Widget _buildDropdownField( + BuildContext context, AuthBloc loginBloc, Size size) { + final TextEditingController textEditingController = TextEditingController(); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + "Country/Region", + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontSize: 14, + fontWeight: FontWeight.w400, + ), + ), + const SizedBox(height: 10), + Container( + height: 50, + decoration: const BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + width: size.width * 0.9, + child: DropdownButtonHideUnderline( + child: DropdownButton2( + style: TextStyle(color: Colors.black), + isExpanded: true, + hint: Text( + 'Select your region/country', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor, + fontWeight: FontWeight.w400, + ), + overflow: TextOverflow.ellipsis, + ), + items: loginBloc.regionList!.map((RegionModel region) { + return DropdownMenuItem( + value: region.id, // Use region.id as the value + child: Text( + region.name, + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ); + }).toList(), + value: loginBloc.regionList!.any( + (region) => region.id == loginBloc.regionUuid,) + ? loginBloc.regionUuid + : null, + onChanged: (String? value) { + if (value != null) { + + loginBloc.add(SelectRegionEvent( + val: value, + )); + } + }, + buttonStyleData: const ButtonStyleData( + padding: EdgeInsets.symmetric(horizontal: 16), + height: 40, + width: double.infinity, + ), + dropdownStyleData: DropdownStyleData( + maxHeight: size.height * 0.70, + ), + menuItemStyleData: const MenuItemStyleData( + height: 40, + ), + dropdownSearchData: DropdownSearchData( + searchController: textEditingController, + searchInnerWidgetHeight: 50, + searchInnerWidget: Container( + height: 50, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: TextFormField( + style: const TextStyle(color: Colors.black), + controller: textEditingController, + decoration: textBoxDecoration()!.copyWith( + errorStyle: const TextStyle(height: 0), + contentPadding: const EdgeInsets.symmetric( + vertical: 12, + horizontal: 10, + ), + ), + ), + ), + searchMatchFn: (item, searchValue) { + // Use the item's child text (region name) for searching. + final regionName = (item.child as Text).data?.toLowerCase() ?? ''; + final search = searchValue.toLowerCase().trim(); + // Debugging print statement to ensure values are captured correctly. + // Return true if the region name contains the search term. + return regionName.contains(search); + }, + ), + onMenuStateChange: (isOpen) { + if (!isOpen) { + textEditingController.clear(); + } + }, + ), + ), + ), + ], + ); + } } diff --git a/lib/pages/auth/view/login_web_page.dart b/lib/pages/auth/view/login_web_page.dart index 13492c45..af211308 100644 --- a/lib/pages/auth/view/login_web_page.dart +++ b/lib/pages/auth/view/login_web_page.dart @@ -181,87 +181,6 @@ class _LoginWebPageState extends State ); } - // Widget _buildDropdownField(BuildContext context, AuthBloc loginBloc, Size size) { - // return Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisAlignment: MainAxisAlignment.start, - // children: [ - // Text( - // "Country/Region", - // style: Theme.of(context) - // .textTheme - // .bodySmall! - // .copyWith(fontSize: 14, fontWeight: FontWeight.w400), - // ), - // const SizedBox(height: 10), - // SizedBox( - // width: size.width * 0.8, - // child: LayoutBuilder( - // builder: (context, constraints) { - // return DropdownButtonFormField( - // value: loginBloc.regionList!.any((region) => region.id == loginBloc.regionUuid) - // ? loginBloc.regionUuid - // : null, - // validator: loginBloc.validateRegion, - // icon: const Icon( - // Icons.keyboard_arrow_down_outlined, - // size: 20, - // ), - // decoration: textBoxDecoration()!.copyWith( - // errorStyle: const TextStyle(height: 0), - // contentPadding: const EdgeInsets.symmetric( - // vertical: 12, - // horizontal: 10, - // ), - // ), - // hint: Text( - // 'Select your region/country', - // style: Theme.of(context) - // .textTheme - // .bodySmall! - // .copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400), - // overflow: TextOverflow.ellipsis, - // ), - // isDense: true, - // style: const TextStyle(color: Colors.black), - // items: loginBloc.regionList!.map((RegionModel region) { - // return DropdownMenuItem( - // value: region.id, - // child: Container( - // constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40), - // child: Text( - // region.name, - // overflow: TextOverflow.ellipsis, - // maxLines: 1, - // ), - // ), - // ); - // }).toList(), - // onChanged: (String? value) { - // loginBloc.add(CheckEnableEvent()); - // loginBloc.add(SelectRegionEvent(val: value!)); - // }, - // dropdownColor: Colors.white, - // menuMaxHeight: size.height * 0.45, - // selectedItemBuilder: (context) { - // return loginBloc.regionList!.map((region) { - // return Container( - // constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40), - // child: Text( - // region.name, - // overflow: TextOverflow.ellipsis, - // maxLines: 1, - // ), - // ); - // }).toList(); - // }, - // ); - // }, - // ), - // ), - // ], - // ); - // } Widget _buildDropdownField( BuildContext context, AuthBloc loginBloc, Size size) { @@ -273,31 +192,33 @@ class _LoginWebPageState extends State Text( "Country/Region", style: Theme.of(context).textTheme.bodySmall!.copyWith( - fontSize: 14, - fontWeight: FontWeight.w400, - ), + fontSize: 14, + fontWeight: FontWeight.w400, + ), ), const SizedBox(height: 10), Container( - height: size.height * 0.05, - decoration:const BoxDecoration( - color: ColorsManager.boxColor, - borderRadius: BorderRadius.all(Radius.circular(8))), + height: 50, + decoration: const BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.all(Radius.circular(8)), + ), width: size.width * 0.9, child: DropdownButtonHideUnderline( child: DropdownButton2( + style: TextStyle(color: Colors.black), isExpanded: true, hint: Text( 'Select your region/country', style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: ColorsManager.grayColor, - fontWeight: FontWeight.w400, - ), + color: ColorsManager.grayColor, + fontWeight: FontWeight.w400, + ), overflow: TextOverflow.ellipsis, ), items: loginBloc.regionList!.map((RegionModel region) { return DropdownMenuItem( - value: region.id, + value: region.id, // Use region.id as the value child: Text( region.name, overflow: TextOverflow.ellipsis, @@ -306,8 +227,7 @@ class _LoginWebPageState extends State ); }).toList(), value: loginBloc.regionList!.any( - (region) => region.id == loginBloc.regionUuid, - ) + (region) => region.id == loginBloc.regionUuid,) ? loginBloc.regionUuid : null, onChanged: (String? value) { @@ -322,7 +242,7 @@ class _LoginWebPageState extends State width: double.infinity, ), dropdownStyleData: DropdownStyleData( - maxHeight: size.height * 0.45, + maxHeight: size.height * 0.70, ), menuItemStyleData: const MenuItemStyleData( height: 40, @@ -334,7 +254,7 @@ class _LoginWebPageState extends State height: 50, padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: TextFormField( - style: TextStyle(color: Colors.black), + style: const TextStyle(color: Colors.black), controller: textEditingController, decoration: textBoxDecoration()!.copyWith( errorStyle: const TextStyle(height: 0), @@ -345,19 +265,14 @@ class _LoginWebPageState extends State ), ), ), - searchMatchFn: (item, searchValue) { - // Ensure both item value and search value are compared in lowercase for a case-insensitive match. - final itemValue = item.value?.toLowerCase() ?? ''; + // Use the item's child text (region name) for searching. + final regionName = (item.child as Text).data?.toLowerCase() ?? ''; final search = searchValue.toLowerCase().trim(); - // Debugging print statement to ensure values are captured correctly. - print('searchValue == $search'); - - // Return true if the item value contains the search term. - return itemValue.contains(search); + // Return true if the region name contains the search term. + return regionName.contains(search); }, - ), onMenuStateChange: (isOpen) { if (!isOpen) { @@ -371,6 +286,7 @@ class _LoginWebPageState extends State ); } + Widget _buildEmailField(BuildContext context, AuthBloc loginBloc) { return Column( crossAxisAlignment: CrossAxisAlignment.start, From 6ba0a70289c47121a367e9dba9a8321ffde6a087 Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 3 Sep 2024 09:03:01 +0300 Subject: [PATCH 5/8] login Enhancements and add search to forget password --- .../auth/view/forget_password_web_page.dart | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/lib/pages/auth/view/forget_password_web_page.dart b/lib/pages/auth/view/forget_password_web_page.dart index 5208a6b3..efc580b3 100644 --- a/lib/pages/auth/view/forget_password_web_page.dart +++ b/lib/pages/auth/view/forget_password_web_page.dart @@ -172,11 +172,12 @@ class ForgetPasswordWebPage extends StatelessWidget { SizedBox( child: TextFormField( validator: forgetBloc.validateEmail, - controller: - forgetBloc.forgetEmailController, decoration: textBoxDecoration()! .copyWith( - hintText: 'Enter your email'), + hintText: 'Enter your email', + hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor, + fontWeight: FontWeight.w400)), style: const TextStyle( color: Colors.black), ), @@ -208,13 +209,15 @@ class ForgetPasswordWebPage extends StatelessWidget { decoration: textBoxDecoration()!.copyWith( hintText: 'Enter Code', + hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor, + fontWeight: FontWeight.w400), suffixIcon: SizedBox( width: 100, child: Center( child: InkWell( onTap: state is TimerState && - !state - .isButtonEnabled && + !state.isButtonEnabled && state.remainingTime != 1 ? null @@ -225,10 +228,8 @@ class ForgetPasswordWebPage extends StatelessWidget { child: Text( 'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}', style: TextStyle( - color: state - is TimerState && - !state - .isButtonEnabled + color: state is TimerState && + !state.isButtonEnabled ? Colors.grey : ColorsManager .btnColor, @@ -242,8 +243,7 @@ class ForgetPasswordWebPage extends StatelessWidget { color: Colors.black), ), ), - if (forgetBloc.forgetValidate != - '') // Check if there is a validation message + if (forgetBloc.forgetValidate != '') // Check if there is a validation message Padding( padding: const EdgeInsets.only(top: 8.0), @@ -275,18 +275,16 @@ class ForgetPasswordWebPage extends StatelessWidget { const SizedBox(height: 10), SizedBox( child: TextFormField( - validator: - forgetBloc.passwordValidator, - keyboardType: - TextInputType.visiblePassword, - controller: forgetBloc - .forgetPasswordController, - decoration: - textBoxDecoration()!.copyWith( + validator: forgetBloc.passwordValidator, + keyboardType: TextInputType.visiblePassword, + controller: forgetBloc.forgetPasswordController, + decoration: textBoxDecoration()!.copyWith( hintText: 'At least 8 characters', + hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor, + fontWeight: FontWeight.w400), ), - style: const TextStyle( - color: Colors.black), + style: const TextStyle(color: Colors.black), ), ), ], From a100b5c40ed4af12ecb64ad0a6da41b619cf7179 Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 3 Sep 2024 09:12:01 +0300 Subject: [PATCH 6/8] login Enhancements and add search to forget password --- lib/pages/home/bloc/home_bloc.dart | 1 - lib/services/access_mang_api.dart | 9 --------- lib/services/auth_api.dart | 4 ---- lib/services/home_api.dart | 1 - 4 files changed, 15 deletions(-) diff --git a/lib/pages/home/bloc/home_bloc.dart b/lib/pages/home/bloc/home_bloc.dart index 1de54eff..79579efc 100644 --- a/lib/pages/home/bloc/home_bloc.dart +++ b/lib/pages/home/bloc/home_bloc.dart @@ -43,7 +43,6 @@ class HomeBloc extends Bloc { try { var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); user = await HomeApi().fetchUserInfo(uuid); - } catch (e) { return; } diff --git a/lib/services/access_mang_api.dart b/lib/services/access_mang_api.dart index 68ea4f8d..b99b75a9 100644 --- a/lib/services/access_mang_api.dart +++ b/lib/services/access_mang_api.dart @@ -22,7 +22,6 @@ class AccessMangApi { ); return response; } catch (e) { - debugPrint('Error fetching visitor passwords: $e'); return []; } } @@ -42,7 +41,6 @@ class AccessMangApi { ); return response; } catch (e) { - debugPrint('Error fetching $e'); return []; } } @@ -123,13 +121,6 @@ class AccessMangApi { String? effectiveTime, String? invalidTime, List? devicesUuid}) async { - print('object=== ${ jsonEncode({ - "email": email, - "devicesUuid": devicesUuid, - "passwordName": passwordName, - "effectiveTime": effectiveTime, - "invalidTime": invalidTime, - })}'); final response = await HTTPService().post( path: ApiEndpoints.sendOffLineMultipleTime, body: jsonEncode({ diff --git a/lib/services/auth_api.dart b/lib/services/auth_api.dart index 844689df..a09fa7ba 100644 --- a/lib/services/auth_api.dart +++ b/lib/services/auth_api.dart @@ -26,7 +26,6 @@ class AuthenticationAPI { body: {"email": email, "password": password}, showServerMessage: true, expectedResponseModel: (json) { - print('json=$json'); }); return response; } @@ -54,15 +53,12 @@ class AuthenticationAPI { return cooldown; } } else { - debugPrint('Error: ${e.response!.statusCode} - ${e.response!.statusMessage}'); return 1; } } else { - debugPrint('Error: ${e.message}'); return 1; } } catch (e) { - debugPrint('Unexpected Error: $e'); return 1; } } diff --git a/lib/services/home_api.dart b/lib/services/home_api.dart index 000d4ff2..dfbaf4bf 100644 --- a/lib/services/home_api.dart +++ b/lib/services/home_api.dart @@ -8,7 +8,6 @@ class HomeApi { path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!), showServerMessage: true, expectedResponseModel: (json) { - print('fetchUserInfo$json'); return UserModel.fromJson(json); }); return response; From 439ce85117231c0d1c8ffcda992ddb08ec60d39c Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 3 Sep 2024 09:50:36 +0300 Subject: [PATCH 7/8] delete dynamic password --- .../view/visitor_password_dialog.dart | 151 ++++++++++-------- 1 file changed, 81 insertions(+), 70 deletions(-) diff --git a/lib/pages/visitor_password/view/visitor_password_dialog.dart b/lib/pages/visitor_password/view/visitor_password_dialog.dart index 5fc82dbf..cb93cb40 100644 --- a/lib/pages/visitor_password/view/visitor_password_dialog.dart +++ b/lib/pages/visitor_password/view/visitor_password_dialog.dart @@ -212,28 +212,28 @@ class VisitorPasswordDialog extends StatelessWidget { }, ), ), - SizedBox( - width: size.width * 0.12, - child: RadioListTile( - contentPadding: EdgeInsets.zero, - title: Text( - 'Dynamic Password', - style: text, - ), - value: 'Dynamic Password', - groupValue: (state is PasswordTypeSelected) - ? state.selectedType - : visitorBloc.accessTypeSelected, - onChanged: (String? value) { - if (value != null) { - context - .read() - .add(SelectPasswordType(value)); - visitorBloc.usageFrequencySelected = ''; - } - }, - ), - ), + // SizedBox( + // width: size.width * 0.12, + // child: RadioListTile( + // contentPadding: EdgeInsets.zero, + // title: Text( + // 'Dynamic Password', + // style: text, + // ), + // value: 'Dynamic Password', + // groupValue: (state is PasswordTypeSelected) + // ? state.selectedType + // : visitorBloc.accessTypeSelected, + // onChanged: (String? value) { + // if (value != null) { + // context + // .read() + // .add(SelectPasswordType(value)); + // visitorBloc.usageFrequencySelected = ''; + // } + // }, + // ), + // ), ], )), const Spacer( @@ -257,14 +257,14 @@ class VisitorPasswordDialog extends StatelessWidget { color: ColorsManager.grayColor, fontSize: 9), ), - if (visitorBloc.accessTypeSelected == 'Dynamic Password') - Text( - 'Quick and short-acting password, only valid within 5 minutes after creation, the system randomly generates a digital password.', - style: Theme.of(context).textTheme.bodySmall!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.grayColor, - fontSize: 9), - ), + // if (visitorBloc.accessTypeSelected == 'Dynamic Password') + // Text( + // 'Quick and short-acting password, only valid within 5 minutes after creation, 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, ) @@ -524,9 +524,20 @@ class VisitorPasswordDialog extends StatelessWidget { if (visitorBloc.usageFrequencySelected == 'One-Time' && visitorBloc.accessTypeSelected == 'Offline Password') { setPasswordFunction(context, size, visitorBloc); - } else if (visitorBloc.accessTypeSelected == 'Dynamic Password') { - setPasswordFunction(context, size, visitorBloc); - } else if(visitorBloc.endTimeAccess.toString()!='End Time'&&visitorBloc.startTimeAccess.toString()!='Start Time') { + } else if (visitorBloc.usageFrequencySelected == 'Periodic' && + visitorBloc.accessTypeSelected == 'Offline Password') { + if (visitorBloc.expirationTime != 'End Time' && + visitorBloc.effectiveTime != 'Start Time' ) { + setPasswordFunction(context, size, visitorBloc); + }else{ + visitorBloc.stateDialog( + context: context, + message: 'Please select Access Period to continue', + title: 'Access Period'); + } + } else if( + visitorBloc.endTimeAccess.toString()!='End Time' + &&visitorBloc.startTimeAccess.toString()!='Start Time') { if (visitorBloc.effectiveTimeTimeStamp != null && visitorBloc.expirationTimeTimeStamp != null) { if (isRepeat == true) { @@ -551,10 +562,10 @@ class VisitorPasswordDialog extends StatelessWidget { title: 'Access Period'); } }else{ - visitorBloc.stateDialog( - context: context, - message: 'Please select Access Period to continue', - title: 'Access Period'); + visitorBloc.stateDialog( + context: context, + message: 'Please select Access Period to continue', + title: 'Access Period'); } } else { visitorBloc.stateDialog( @@ -724,39 +735,39 @@ class VisitorPasswordDialog extends StatelessWidget { borderRadius: 8, onPressed: () { Navigator.pop(context); - if (visitorBloc.accessTypeSelected == 'Dynamic Password') { - } else { - 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( - context: context, - 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(), - )); - } + 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( + context: context, + 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: Text( From 3a254bce4d46f840fb89a035fec512d9b9871d00 Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 3 Sep 2024 09:51:55 +0300 Subject: [PATCH 8/8] delete dynamic password --- .../view/visitor_password_dialog.dart | 51 ------------------- 1 file changed, 51 deletions(-) diff --git a/lib/pages/visitor_password/view/visitor_password_dialog.dart b/lib/pages/visitor_password/view/visitor_password_dialog.dart index cb93cb40..ad11b30f 100644 --- a/lib/pages/visitor_password/view/visitor_password_dialog.dart +++ b/lib/pages/visitor_password/view/visitor_password_dialog.dart @@ -575,57 +575,6 @@ class VisitorPasswordDialog extends StatelessWidget { } } }, - - // onPressed: () { - // if (visitorBloc.forgetFormKey.currentState!.validate()) { - // if (visitorBloc.selectedDevices.isNotEmpty) { - // switch (visitorBloc.usageFrequencySelected) { - // case 'One-Time': - // if (visitorBloc.accessTypeSelected == 'Offline Password') { - // setPasswordFunction(context, size, visitorBloc); - // } else { - // visitorBloc.stateDialog( - // context: context, - // message: 'Invalid combination of Access Type and Usage Frequency.', - // title: 'Error', - // ); - // } - // break; - // default: - // if (visitorBloc.effectiveTimeTimeStamp != null && visitorBloc.expirationTimeTimeStamp != null) { - // if (isRepeat) { - // if (visitorBloc.expirationTime != 'End Time' && - // visitorBloc.effectiveTime != 'Start Time' && - // visitorBloc.selectedDays.isNotEmpty) { - // setPasswordFunction(context, size, visitorBloc); - // } else { - // visitorBloc.stateDialog( - // context: context, - // message: 'Please select days and fill start time and end time to continue', - // title: 'Access Period', - // ); - // } - // } else { - // setPasswordFunction(context, size, visitorBloc); - // } - // } else { - // visitorBloc.stateDialog( - // context: context, - // message: 'Please select Access Period to continue', - // title: 'Access Period', - // ); - // } - // break; - // } - // } else { - // visitorBloc.stateDialog( - // context: context, - // message: 'Please select devices to continue', - // title: 'Select Devices', - // ); - // } - // } - // }, borderRadius: 8, child: Text( 'Ok',