diff --git a/assets/dome.json b/assets/dome.json deleted file mode 100644 index 91186608..00000000 --- a/assets/dome.json +++ /dev/null @@ -1,96 +0,0 @@ -[ - { - "accessUser": "Ali Doe", - "accessType": "Admin", - "startTime": "2023-08-01", - "endTime": "2023-08-02", - "accessibleDevice": "Smart Door", - "authorizationSource": "System", - "authorizer": "Jane Smith", - "authorizationTime": "2023-08-01 10:00 AM", - "accessStatus": "Granted", - "actions": "View" - }, { - "accessUser": "oamr Doe", - "accessType": "Admin", - "startTime": "2023-08-01", - "endTime": "2023-08-05", - "accessibleDevice": "Smart Door", - "authorizationSource": "System", - "authorizer": "Jane Smith", - "authorizationTime": "2023-08-01 10:00 AM", - "accessStatus": "Granted", - "actions": "View" - }, { - "accessUser": "John Doe", - "accessType": "Admin", - "startTime": "2023-08-01", - "endTime": "2023-08-10", - "accessibleDevice": "Smart Door", - "authorizationSource": "System", - "authorizer": "Jane Smith", - "authorizationTime": "2023-08-01 10:00 AM", - "accessStatus": "Granted", - "actions": "View" - }, - - { - "accessUser": "John Doe", - "accessType": "Admin", - "startTime": "2023-08-01", - "endTime": "2023-10-10", - "accessibleDevice": "Smart Door", - "authorizationSource": "System", - "authorizer": "Jane Smith", - "authorizationTime": "2023-08-01 10:00 AM", - "accessStatus": "Granted", - "actions": "View" - }, - { - "accessUser": "John Doe", - "accessType": "Admin", - "startTime": "2023-03-01", - "endTime": "2023-05-10", - "accessibleDevice": "Smart Door", - "authorizationSource": "System", - "authorizer": "Jane Smith", - "authorizationTime": "2023-08-01 10:00 AM", - "accessStatus": "Granted", - "actions": "View" - }, - { - "accessUser": "John Doe", - "accessType": "Admin", - "startTime": "2023-07-01", - "endTime": "2023-08-10", - "accessibleDevice": "Smart Door", - "authorizationSource": "System", - "authorizer": "Jane Smith", - "authorizationTime": "2023-08-01 10:00 AM", - "accessStatus": "Granted", - "actions": "View" - }, { - "accessUser": "John Doe", - "accessType": "Admin", - "startTime": "2023-01-01", - "endTime": "2023-09-05", - "accessibleDevice": "Smart Door", - "authorizationSource": "System", - "authorizer": "Jane Smith", - "authorizationTime": "2023-08-01 10:00 AM", - "accessStatus": "Granted", - "actions": "View" - }, - { - "accessUser": "Alice Johnson", - "accessType": "User", - "startTime": "2023-08-01", - "endTime": "2023-08-10", - "accessibleDevice": "Smart Lock", - "authorizationSource": "Admin", - "authorizer": "John Doe", - "authorizationTime": "2023-08-02 11:00 AM", - "accessStatus": "Pending", - "actions": "Approve" - } -] diff --git a/lib/main.dart b/lib/main.dart index af74170d..01911d05 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,8 +5,7 @@ import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/auth/view/login_page.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/view/home_page.dart'; -import 'package:syncrow_web/pages/visitor_password/view/add_device_dialog.dart'; -import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart'; +import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart'; import 'package:syncrow_web/services/locator.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -30,6 +29,8 @@ class MyApp extends StatelessWidget { return MultiBlocProvider( providers: [ BlocProvider(create: (context) => HomeBloc()), + BlocProvider( + create: (context) => VisitorPasswordBloc(),) ], child: MaterialApp( debugShowCheckedModeBanner: false, // Hide debug banner diff --git a/lib/pages/access_management/bloc/access_bloc.dart b/lib/pages/access_management/bloc/access_bloc.dart index 5b406add..4ed61291 100644 --- a/lib/pages/access_management/bloc/access_bloc.dart +++ b/lib/pages/access_management/bloc/access_bloc.dart @@ -39,12 +39,9 @@ class AccessBloc extends Bloc { } } void updateTabsCount() { - // Count occurrences based on the type field int toBeEffectiveCount = data.where((item) => item.passwordStatus.value== 'To Be Effective').length; int effectiveCount = data.where((item) => item.passwordStatus.value == 'Effective').length; int expiredCount = data.where((item) => item.passwordStatus.value == 'Expired').length; - - // Update tab labels with counts tabs[1] = 'To Be Effective ($toBeEffectiveCount)'; tabs[2] = 'Effective ($effectiveCount)'; tabs[3] = 'Expired ($expiredCount)'; @@ -53,7 +50,6 @@ class AccessBloc extends Bloc { int selectedIndex = 0; - final List tabs = [ 'All', 'To Be Effective (0)', @@ -76,7 +72,6 @@ class AccessBloc extends Bloc { Future selectTime(SelectTime event, Emitter emit) async { - final DateTime? picked = await showDatePicker( context: event.context, initialDate: DateTime.now(), @@ -146,8 +141,6 @@ class AccessBloc extends Bloc { try { filteredData = data.where((item) { bool matchesCriteria = true; - - // Filter by password name if provided if (event.passwordName != null && event.passwordName!.isNotEmpty) { final bool matchesName = item.passwordName != null && item.passwordName.contains(event.passwordName); @@ -155,8 +148,6 @@ class AccessBloc extends Bloc { matchesCriteria = false; } } - - // Filter by date range if provided if (event.startTime != null && event.endTime != null) { final int? effectiveTime = int.tryParse(item.effectiveTime.toString()); final int? invalidTime = int.tryParse(item.invalidTime.toString()); @@ -170,8 +161,6 @@ class AccessBloc extends Bloc { } } } - - // Filter by tab selection if (event.selectedTabIndex == 1 && item.passwordStatus.value != 'To Be Effective') { matchesCriteria = false; } else if (event.selectedTabIndex == 2 && item.passwordStatus.value != 'Effective') { @@ -179,7 +168,6 @@ class AccessBloc extends Bloc { } else if (event.selectedTabIndex == 3 && item.passwordStatus.value != 'Expired') { matchesCriteria = false; } - return matchesCriteria; }).toList(); emit(TableLoaded(filteredData)); @@ -206,7 +194,6 @@ class AccessBloc extends Bloc { try { emit(AccessLoaded()); selectedIndex = event.selectedIndex; - // Apply filtering based on selected tab switch (selectedIndex) { case 0: // All filteredData = data; @@ -216,7 +203,6 @@ class AccessBloc extends Bloc { break; case 2: // Effective filteredData = data.where((item) => item.passwordStatus.value == "Effective").toList(); - break; case 3: // Expired filteredData = data.where((item) => item.passwordStatus.value == "Expired").toList(); @@ -225,7 +211,7 @@ class AccessBloc extends Bloc { filteredData = data; } add(FilterDataEvent( - selectedTabIndex: selectedIndex, // Pass the selected tab index + selectedTabIndex: selectedIndex, passwordName: passwordName.text.toLowerCase(), startTime: effectiveTimeTimeStamp, endTime: expirationTimeTimeStamp diff --git a/lib/pages/access_management/view/access_management.dart b/lib/pages/access_management/view/access_management.dart index 38fb545e..6245dcda 100644 --- a/lib/pages/access_management/view/access_management.dart +++ b/lib/pages/access_management/view/access_management.dart @@ -21,17 +21,14 @@ class AccessManagementPage extends StatelessWidget { enableMenuSideba: false, appBarTitle: Row( children: [ - Text( - 'Access Management', + Text('Access Management', style: Theme.of(context).textTheme.headlineLarge, ) ], ), appBarBody: [ - Text( - 'Physical Access', - style: Theme.of(context) - .textTheme + Text('Physical Access', + style: Theme.of(context).textTheme .headlineMedium! .copyWith(color: Colors.white), ), @@ -101,7 +98,9 @@ class AccessManagementPage extends StatelessWidget { height: 20, ), Row( - children: [ + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + textBaseline: TextBaseline.ideographic, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -111,7 +110,7 @@ class AccessManagementPage extends StatelessWidget { color: Colors.black,fontSize: 13),), const SizedBox(height: 5,), Container( - height:size.height * 0.053, + height:43, width: size.width * 0.15, decoration: containerDecoration, child: TextFormField( @@ -145,51 +144,44 @@ class AccessManagementPage extends StatelessWidget { ), SizedBox( + height:45, + width: size.width * 0.06, - child: Column( - children: [ - Text(''), - Container( - decoration: containerDecoration, - child: DefaultButton( - onPressed: () { - accessBloc.add(FilterDataEvent( - selectedTabIndex: BlocProvider.of(context).selectedIndex, // Pass the selected tab index - passwordName: accessBloc.passwordName.text.toLowerCase(), - startTime: accessBloc.effectiveTimeTimeStamp, - endTime: accessBloc.expirationTimeTimeStamp - )); - }, borderRadius: 9, - child: const Text('Search'))), - ], - ), + child:Container( + decoration: containerDecoration, + child: DefaultButton( + onPressed: () { + accessBloc.add(FilterDataEvent( + selectedTabIndex: BlocProvider.of(context).selectedIndex, // Pass the selected tab index + passwordName: accessBloc.passwordName.text.toLowerCase(), + startTime: accessBloc.effectiveTimeTimeStamp, + endTime: accessBloc.expirationTimeTimeStamp + )); + }, borderRadius: 9, + child: const Text('Search'))), ), const SizedBox( width: 10, ), SizedBox( + height:45, width: size.width * 0.06, - child: Column( - children: [ - Text(''), - Container( - decoration: containerDecoration, - child: DefaultButton( - onPressed: () { - accessBloc.add(ResetSearch()); - }, - backgroundColor: ColorsManager.whiteColors, - borderRadius: 9, - child: Text( - 'Reset', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: Colors.black), - ), - ), + child: Container( + decoration: containerDecoration, + child: DefaultButton( + onPressed: () { + accessBloc.add(ResetSearch()); + }, + backgroundColor: ColorsManager.whiteColors, + borderRadius: 9, + child: Text( + 'Reset', + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(color: Colors.black), ), - ], + ), ), ), ], diff --git a/lib/pages/auth/view/login_mobile_page.dart b/lib/pages/auth/view/login_mobile_page.dart index 98402f59..d6544a98 100644 --- a/lib/pages/auth/view/login_mobile_page.dart +++ b/lib/pages/auth/view/login_mobile_page.dart @@ -146,7 +146,6 @@ class LoginMobilePage extends StatelessWidget { textAlign: TextAlign.center, ), ), - isDense: true, style: const TextStyle(color: Colors.black), items:loginBloc.regionList!.map((RegionModel region) { @@ -156,7 +155,6 @@ class LoginMobilePage extends StatelessWidget { ); }).toList(), onChanged: (String? value) { - print(value); }, ), ) diff --git a/lib/pages/common/custom_dialog.dart b/lib/pages/common/custom_dialog.dart new file mode 100644 index 00000000..3b667811 --- /dev/null +++ b/lib/pages/common/custom_dialog.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +Future showCustomDialog({ + required BuildContext context, + required String message, + String? title, + String? iconPath, + double? dialogHeight, + double? iconHeight, + double? iconWidth, + VoidCallback? onOkPressed, + bool barrierDismissible = false, required actions, +}) { + return showDialog( + context: context, + barrierDismissible: barrierDismissible, + builder: (BuildContext context) { + final size = MediaQuery.of(context).size; + return AlertDialog( + alignment: Alignment.center, + content: SizedBox( + height: dialogHeight ?? size.height * 0.15, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (iconPath != null) + SvgPicture.asset( + iconPath, + height: iconHeight ?? 35, + width: iconWidth ?? 35, + ), + if (title != null) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + title, + style: Theme.of(context).textTheme.headlineLarge!.copyWith( + fontSize: 20, + fontWeight: FontWeight.w400, + color: Colors.black), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + message, + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.black), + textAlign: TextAlign.center, + ), + ), + ], + ), + ), + actionsAlignment: MainAxisAlignment.center, + actions: [ + TextButton( + onPressed: onOkPressed ?? () => Navigator.of(context).pop(), + child: Text( + 'OK', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.blackColor, + fontSize: 16), + ), + ), + ], + ); + }, + ); +} diff --git a/lib/pages/common/custom_table.dart b/lib/pages/common/custom_table.dart index b7aa71a5..a691b673 100644 --- a/lib/pages/common/custom_table.dart +++ b/lib/pages/common/custom_table.dart @@ -15,7 +15,7 @@ class DynamicTable extends StatefulWidget { final void Function(int, bool?)? onRowCheckboxChanged; const DynamicTable({ - Key? key, + super.key, required this.headers, required this.data, required this.size, @@ -25,7 +25,7 @@ class DynamicTable extends StatefulWidget { this.cellDecoration, this.selectAll, this.onRowCheckboxChanged, - }) : super(key: key); + }); @override _DynamicTableState createState() => _DynamicTableState(); @@ -66,7 +66,7 @@ class _DynamicTableState extends State { return Container( decoration: widget.cellDecoration, child: Padding( - padding: const EdgeInsets.all(10.0), + padding: const EdgeInsets.all(2.0), child: ListView( scrollDirection: Axis.horizontal, @@ -102,7 +102,7 @@ class _DynamicTableState extends State { SvgPicture.asset( Assets.emptyTable ), - SizedBox(height: 15,), + const SizedBox(height: 15,), Text('No Passwords',style: Theme.of(context).textTheme.bodySmall!.copyWith(color:ColorsManager.grayColor ),) ], ), @@ -122,9 +122,9 @@ class _DynamicTableState extends State { return Row( children: [ if (widget.withCheckBox) - _buildRowCheckbox(index), + _buildRowCheckbox(index,widget.size.height*0.10), ...row.map((cell) => - _buildTableCell(cell.toString())).toList(), + _buildTableCell(cell.toString(),widget.size.height*0.10)).toList(), ], ); }, @@ -141,8 +141,12 @@ class _DynamicTableState extends State { } Widget _buildSelectAllCheckbox() { - return SizedBox( - width: 50, + return Container( + padding: const EdgeInsets.all(8.0), + + decoration: const BoxDecoration( + border: Border.symmetric( + vertical: BorderSide(color: ColorsManager.boxDivider))), child: Checkbox( value: _selectAll, onChanged: _toggleSelectAll, @@ -150,16 +154,27 @@ class _DynamicTableState extends State { ); } - Widget _buildRowCheckbox(int index) { - return SizedBox( - width: 50, - child: Checkbox( + Widget _buildRowCheckbox(int index,size) { + return Container( + padding: const EdgeInsets.all(8.0), + + height:size , + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide( + color: ColorsManager.boxDivider, + width: 1.0, + ), + )), + alignment: Alignment.centerLeft, + child: Center(child: Checkbox( value: _selected[index], onChanged: (bool? value) { _toggleRowSelection(index, value); }, - ), + ),) ); + } Widget _buildTableHeaderCell(String title) { @@ -177,11 +192,11 @@ class _DynamicTableState extends State { ); } - Widget _buildTableCell(String content) { + Widget _buildTableCell(String content,size) { return Expanded( child: Container( - height: 80, - padding: const EdgeInsets.all(15.0), + height:size , + padding: const EdgeInsets.all(5.0), decoration: const BoxDecoration( border: Border( bottom: BorderSide( diff --git a/lib/pages/common/custom_web_textfield.dart b/lib/pages/common/custom_web_textfield.dart index daaa2c13..363a1994 100644 --- a/lib/pages/common/custom_web_textfield.dart +++ b/lib/pages/common/custom_web_textfield.dart @@ -33,8 +33,7 @@ class CustomWebTextField extends StatelessWidget { children: [ Text('* ', style: Theme.of(context) - .textTheme - .bodyMedium! + .textTheme.bodyMedium! .copyWith(color: Colors.red), ), Text(textFieldName, style: Theme.of(context).textTheme.bodySmall!.copyWith( @@ -46,10 +45,8 @@ class CustomWebTextField extends StatelessWidget { child: Text( description??'', style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith( - fontSize: 9, + .textTheme.bodySmall! + .copyWith(fontSize: 9, fontWeight: FontWeight.w400, color: ColorsManager.textGray), ), @@ -65,21 +62,19 @@ class CustomWebTextField extends StatelessWidget { color: Colors.grey.withOpacity(0.3), spreadRadius:2, blurRadius: 3, - offset: Offset(1, 1), // changes position of shadow + offset: const Offset(1, 1), // changes position of shadow ), ] ), - child: Container( - child: TextFormField( - validator: validator, - controller: controller, - style: const TextStyle(color: Colors.black), - decoration: textBoxDecoration()! - .copyWith( - errorStyle: const TextStyle(height: 0), // Hide the error text space + child: TextFormField( + validator: validator, + controller: controller, + style: const TextStyle(color: Colors.black), + decoration: textBoxDecoration()! + .copyWith( + errorStyle: const TextStyle(height: 0), // Hide the error text space - hintText: 'Please enter'), - ), + hintText: 'Please enter'), ), ), ], diff --git a/lib/pages/common/hour_picker_dialog.dart b/lib/pages/common/hour_picker_dialog.dart index 63e95e21..718f1ebf 100644 --- a/lib/pages/common/hour_picker_dialog.dart +++ b/lib/pages/common/hour_picker_dialog.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; class HourPickerDialog extends StatefulWidget { final TimeOfDay initialTime; - HourPickerDialog({required this.initialTime}); + const HourPickerDialog({super.key, required this.initialTime}); @override _HourPickerDialogState createState() => _HourPickerDialogState(); @@ -24,7 +24,7 @@ class _HourPickerDialogState extends State { @override Widget build(BuildContext context) { return AlertDialog( - title: Text('Select Hour'), + title: const Text('Select Hour'), content: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -46,7 +46,7 @@ class _HourPickerDialogState extends State { SizedBox(width: 16.0), DropdownButton( value: _isPm, - items: [ + items: const [ DropdownMenuItem( value: false, child: Text('AM'), @@ -67,14 +67,14 @@ class _HourPickerDialogState extends State { actions: [ TextButton( onPressed: () => Navigator.of(context).pop(null), - child: Text('Cancel'), + child: const Text('Cancel'), ), TextButton( onPressed: () { int hour = _isPm ? _selectedHour + 12 : _selectedHour; Navigator.of(context).pop(TimeOfDay(hour: hour, minute: 0)); }, - child: Text('OK'), + child: const Text('OK'), ), ], ); diff --git a/lib/pages/common/info_dialog.dart b/lib/pages/common/info_dialog.dart index 4465ced6..cfd2cbd4 100644 --- a/lib/pages/common/info_dialog.dart +++ b/lib/pages/common/info_dialog.dart @@ -66,7 +66,7 @@ class InfoDialog extends StatelessWidget { onPressed: () { Navigator.of(context).pop(); }, - child: Text('OK'), + child: const Text('OK'), ), ], ); diff --git a/lib/pages/home/view/home_page_web.dart b/lib/pages/home/view/home_page_web.dart index 0124acad..39a79a57 100644 --- a/lib/pages/home/view/home_page_web.dart +++ b/lib/pages/home/view/home_page_web.dart @@ -2,13 +2,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; +import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/pages/home/view/home_card.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; -import '../bloc/home_state.dart'; - class HomeWebPage extends StatelessWidget { HomeWebPage({super.key}); @override diff --git a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart index ff3b57e4..ee40de87 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart @@ -2,6 +2,7 @@ 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/custom_dialog.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'; @@ -9,9 +10,9 @@ import 'package:syncrow_web/pages/visitor_password/model/device_model.dart'; 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/constants/assets.dart'; import 'package:syncrow_web/utils/snack_bar.dart'; - class VisitorPasswordBloc extends Bloc { VisitorPasswordBloc() : super(VisitorPasswordInitial()) { @@ -23,10 +24,8 @@ class VisitorPasswordBloc on(toggleDaySelection); on(selectDevice); on(_onUpdateFilteredDevices); - on(postOnlineOneTimePassword); on(postOnlineMultipleTimePassword); - on(postOfflineMultipleTimePassword); on(postOfflineOneTimePassword); on(selectTimeOfLinePassword); @@ -58,16 +57,12 @@ class VisitorPasswordBloc int? effectiveTimeTimeStamp; int? expirationTimeTimeStamp; - int? repeatEffectiveTimeTimeStamp; - int? repeatExpirationTimeTimeStamp; - DateTime? startTime = DateTime.now(); DateTime? endTime; String startTimeAccess = 'Start Time'; String endTimeAccess = 'End Time'; - selectAccessType( SelectPasswordType event, Emitter emit) { accessTypeSelected = event.type; @@ -81,9 +76,9 @@ class VisitorPasswordBloc } Future selectTimeVisitorPassword( - SelectTimeVisitorPassword event, - Emitter emit, - ) async { + SelectTimeVisitorPassword event, + Emitter emit, + ) async { final DateTime? picked = await showDatePicker( context: event.context, initialDate: DateTime.now(), @@ -122,7 +117,8 @@ class VisitorPasswordBloc timePicked.minute, ); - final selectedTimestamp = selectedDateTime.millisecondsSinceEpoch ~/ 1000; + final selectedTimestamp = + selectedDateTime.millisecondsSinceEpoch ~/ 1000; if (event.isStart) { if (expirationTimeTimeStamp != null && @@ -133,9 +129,7 @@ class VisitorPasswordBloc return; } effectiveTimeTimeStamp = selectedTimestamp; - - startTimeAccess = selectedDateTime.toString().split('.').first; - + startTimeAccess = selectedDateTime.toString().split('.').first; } else { if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) { @@ -145,11 +139,8 @@ class VisitorPasswordBloc return; } expirationTimeTimeStamp = selectedTimestamp; - - endTimeAccess = selectedDateTime.toString().split('.').first; - + endTimeAccess = selectedDateTime.toString().split('.').first; } - emit(ChangeTimeState()); emit(VisitorPasswordInitial()); } @@ -201,10 +192,10 @@ class VisitorPasswordBloc } //online password - Future postOnlineOneTimePassword(OnlineOneTimePasswordEvent event, Emitter emit) async { try { + emit(LoadingInitialState()); generate7DigitNumber(); bool res = await AccessMangApi().postOnlineOneTime( email: event.email, @@ -213,27 +204,37 @@ class VisitorPasswordBloc passwordName: event.passwordName, effectiveTime: effectiveTimeTimeStamp.toString(), invalidTime: expirationTimeTimeStamp.toString()); - if (res = true) { - Navigator.pop(event.context!); + if (res == true) { + emit(SuccessState()); + } else { + throw Exception('Failed to create password'); } emit(TableLoaded(data)); } catch (e) { - emit(FailedState(e.toString())); + Navigator.pop(event.context!); + stateDialog( + context: event.context!, + message: e.toString(), + title: 'Something Wrong'); } } + Future postOnlineMultipleTimePassword( OnlineMultipleTimePasswordEvent event, Emitter emit) async { try { - generate7DigitNumber(); - bool res = await AccessMangApi().postOnlineMultipleTime( + emit(LoadingInitialState()); + + await generate7DigitNumber(); + bool res = await AccessMangApi().postOnlineMultipleTime( scheduleList: [ if (repeat) Schedule( effectiveTime: getTimeFromDateTimeString(expirationTime), - invalidTime: getTimeFromDateTimeString(effectiveTime).toString(), + invalidTime: + getTimeFromDateTimeString(effectiveTime).toString(), workingDay: selectedDays, ), ], @@ -243,9 +244,11 @@ class VisitorPasswordBloc email: event.email, devicesUuid: selectedDevices, passwordName: event.passwordName); - if(res==true){ - Navigator.pop(event.context!); + if (res == true) { + emit(SuccessState()); + emit(TableLoaded(data)); } + } catch (e) { emit(FailedState(e.toString())); } @@ -255,11 +258,16 @@ class VisitorPasswordBloc Future postOfflineOneTimePassword(OfflineOneTimePasswordEvent event, Emitter emit) async { try { - generate7DigitNumber(); - await AccessMangApi().postOffLineOneTime( + emit(LoadingInitialState()); + await generate7DigitNumber(); + bool res = await AccessMangApi().postOffLineOneTime( email: event.email, devicesUuid: selectedDevices, passwordName: event.passwordName); + if (res == true) { + emit(SuccessState()); + emit(TableLoaded(data)); + } } catch (e) { emit(FailedState(e.toString())); } @@ -269,15 +277,19 @@ class VisitorPasswordBloc OfflineMultipleTimePasswordEvent event, Emitter emit) async { try { - generate7DigitNumber(); - await AccessMangApi().postOffLineMultipleTime( - email: event.email, - devicesUuid: selectedDevices, - passwordName: event.passwordName, + emit(LoadingInitialState()); + await generate7DigitNumber(); + bool res = await AccessMangApi().postOffLineMultipleTime( + email: event.email, + devicesUuid: selectedDevices, + passwordName: event.passwordName, invalidTime: expirationTimeTimeStamp.toString(), effectiveTime: effectiveTimeTimeStamp.toString(), - ); + if (res == true) { + emit(SuccessState()); + emit(TableLoaded(data)); + } } catch (e) { emit(FailedState(e.toString())); } @@ -300,13 +312,11 @@ class VisitorPasswordBloc } Future generate7DigitNumber() async { - emit(LoadingInitialState()); passwordController = ''; Random random = Random(); int min = 1000000; int max = 9999999; passwordController = (min + random.nextInt(max - min + 1)).toString(); - emit(GeneratePasswordState()); return passwordController; } @@ -319,12 +329,10 @@ class VisitorPasswordBloc final deviceName = deviceNameController.text.toLowerCase(); final deviceId = deviceIdController.text.toLowerCase(); final unitName = unitNameController.text.toLowerCase(); - final filteredData = data.where((device) { final matchesDeviceName = device.name.toLowerCase().contains(deviceName); 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; }).toList(); add(UpdateFilteredDevicesEvent(filteredData)); @@ -334,7 +342,6 @@ class VisitorPasswordBloc Stream mapEventToState( VisitorPasswordEvent event) async* { if (event is FetchDevice) { - // Fetching logic... } else if (event is UpdateFilteredDevicesEvent) { yield TableLoaded(event.filteredData); } @@ -347,10 +354,11 @@ class VisitorPasswordBloc addDeviceToList(context) { selectedDevices = selectedDeviceIds; - Navigator.of(context).pop(selectedDevices); // Close the dialog + Navigator.of(context).pop(selectedDevices); } - Future selectTimeOfLinePassword(SelectTimeEvent event, Emitter emit) async { + Future selectTimeOfLinePassword( + SelectTimeEvent event, Emitter emit) async { emit(ChangeTimeState()); final DateTime? picked = await showDatePicker( context: event.context, @@ -416,11 +424,10 @@ class VisitorPasswordBloc 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) { @@ -432,8 +439,6 @@ class VisitorPasswordBloc 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); } @@ -444,4 +449,27 @@ class VisitorPasswordBloc return null; } + Future stateDialog({ + BuildContext? context, + String? message, + String? title, + dynamic actions, + }) { + return showCustomDialog( + context: context!, + message: message!, + iconPath: Assets.deviceNoteIcon, + title: title, + dialogHeight: 150, + actions: actions ?? + [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('OK'), + ), + ], + ); + } } diff --git a/lib/pages/visitor_password/bloc/visitor_password_event.dart b/lib/pages/visitor_password/bloc/visitor_password_event.dart index 723ac052..05ad73d1 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_event.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_event.dart @@ -77,11 +77,12 @@ class OnlineMultipleTimePasswordEvent extends VisitorPasswordEvent { //offline password class OfflineOneTimePasswordEvent extends VisitorPasswordEvent { + final BuildContext? context; final String? email; final String? passwordName; - const OfflineOneTimePasswordEvent({this.email,this.passwordName}); + const OfflineOneTimePasswordEvent({this.email,this.passwordName,this.context}); @override - List get props => [email!,passwordName!,]; + List get props => [email!,passwordName!,context!,]; } class OfflineMultipleTimePasswordEvent extends VisitorPasswordEvent { @@ -89,11 +90,12 @@ class OfflineMultipleTimePasswordEvent extends VisitorPasswordEvent { final String? passwordName; final String? invalidTime; final String? effectiveTime; + final BuildContext? context; - const OfflineMultipleTimePasswordEvent({this.email,this.passwordName,this.invalidTime,this.effectiveTime}); + const OfflineMultipleTimePasswordEvent({this.context,this.email,this.passwordName,this.invalidTime,this.effectiveTime}); @override - List get props => [email!,passwordName!,invalidTime!,effectiveTime!]; + List get props => [email!,passwordName!,invalidTime!,effectiveTime!,context!]; } diff --git a/lib/pages/visitor_password/bloc/visitor_password_state.dart b/lib/pages/visitor_password/bloc/visitor_password_state.dart index 27008a30..279c9809 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_state.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_state.dart @@ -43,7 +43,7 @@ class LoadingInitialState extends VisitorPasswordState {} class ChangeTimeState extends VisitorPasswordState {} class TimeSelectedState extends VisitorPasswordState {} class DeviceLoaded extends VisitorPasswordState {} -class GeneratePasswordState extends VisitorPasswordState {} +class SuccessState extends VisitorPasswordState {} class FailedState extends VisitorPasswordState { final String message; diff --git a/lib/pages/visitor_password/view/add_device_dialog.dart b/lib/pages/visitor_password/view/add_device_dialog.dart index 9d7be636..eccdfd78 100644 --- a/lib/pages/visitor_password/view/add_device_dialog.dart +++ b/lib/pages/visitor_password/view/add_device_dialog.dart @@ -12,7 +12,6 @@ import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/const.dart'; import 'package:syncrow_web/utils/style.dart'; - class AddDeviceDialog extends StatelessWidget { const AddDeviceDialog({super.key}); @override @@ -54,7 +53,7 @@ class AddDeviceDialog extends StatelessWidget { width: 15, ), ), - SizedBox(width: 10,), + const SizedBox(width: 10,), Text('Only online accessible devices can be added', style: Theme.of(context).textTheme.bodySmall!.copyWith( fontWeight: FontWeight.w400, @@ -63,13 +62,15 @@ class AddDeviceDialog extends StatelessWidget { ], ) ), - SizedBox(height: 20,), + const SizedBox(height: 20,), Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + textBaseline: TextBaseline.alphabetic, + children: [ Expanded( - flex: 2, + flex: 4, child: CustomWebTextField( controller: visitorBloc.deviceNameController, isRequired: true, @@ -79,7 +80,7 @@ class AddDeviceDialog extends StatelessWidget { ), const SizedBox(width: 10), Expanded( - flex: 2, + flex: 4, child: CustomWebTextField( controller: visitorBloc.deviceIdController, isRequired: true, @@ -89,7 +90,7 @@ class AddDeviceDialog extends StatelessWidget { ), const SizedBox(width: 10), Expanded( - flex: 2, + flex: 4, child: CustomWebTextField( controller: visitorBloc.unitNameController, isRequired: true, @@ -97,67 +98,49 @@ class AddDeviceDialog extends StatelessWidget { description: '', ), ), - const SizedBox(width: 10), - Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 25), - Center( - child: Container( - height: 43, - width: 100, - decoration: containerDecoration, - child: Center( - child: DefaultButton( - onPressed: () { - visitorBloc.filterDevices(); // Call filter function - }, - borderRadius: 9, - child: const Text('Search'), - ), + Expanded( + flex: 2, + child: Container( + child: SizedBox( + width: size.width * 0.06, + child: Center( + child: DefaultButton( + onPressed: () { + visitorBloc.filterDevices(); + }, + borderRadius: 9, + child: const Text('Search'), ), ), ), - - ], + ), ), const SizedBox(width: 10), - Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 25), - - Center( - child: Container( - height: 43, - width: 100, - decoration: containerDecoration, - child: Center( - child: DefaultButton( - backgroundColor: ColorsManager.whiteColors, - borderRadius: 9, - child: Text( - 'Reset', - style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), - ), - onPressed: () { - visitorBloc.deviceNameController.clear(); - visitorBloc.deviceIdController.clear(); - visitorBloc.unitNameController.clear(); - visitorBloc.add(FetchDevice()); // Reset to original list - }, - ), - ), + Expanded( + flex: 2, + child: Container( + width: size.width * 0.06, + child: DefaultButton( + backgroundColor: ColorsManager.whiteColors, + borderRadius: 9, + child: Text('Reset', + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), ), + onPressed: () { + visitorBloc.deviceNameController.clear(); + visitorBloc.deviceIdController.clear(); + visitorBloc.unitNameController.clear(); + visitorBloc.add(FetchDevice()); // Reset to original list + }, ), - ], - ), - + ), + ) ], ), const SizedBox(height: 20), Expanded( + flex: 3, child: state is TableLoaded ? DynamicTable( cellDecoration: containerDecoration, diff --git a/lib/pages/visitor_password/view/visitor_password_dialog.dart b/lib/pages/visitor_password/view/visitor_password_dialog.dart index 1f7e3a14..0aa63638 100644 --- a/lib/pages/visitor_password/view/visitor_password_dialog.dart +++ b/lib/pages/visitor_password/view/visitor_password_dialog.dart @@ -1,11 +1,10 @@ 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:flutter_svg/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'; -import 'package:syncrow_web/pages/common/info_dialog.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.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'; @@ -21,441 +20,517 @@ class VisitorPasswordDialog extends StatelessWidget { @override Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; + var text = Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13); return BlocProvider( create: (context) => VisitorPasswordBloc(), - child: BlocBuilder( - builder: (BuildContext context, VisitorPasswordState state) { + child: BlocListener( + listener: (context, state) { final visitorBloc = BlocProvider.of(context); - 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), - ), - content: SingleChildScrollView( - child: Form( - key: visitorBloc.forgetFormKey, - child: Padding( - padding: const EdgeInsets.all(5.0), - child: ListBody( - children: [ - Container( - child: Row( - children: [ - Expanded( - flex: 2, - child: CustomWebTextField( - validator: visitorBloc.validate, - controller: visitorBloc.userNameController, - isRequired: true, - textFieldName: 'Name', - description: '', - ), - ), - const Spacer(), - Expanded( - flex: 2, - child: CustomWebTextField( - validator: visitorBloc.validateEmail, - controller: visitorBloc.emailController, - isRequired: true, - textFieldName: 'Email Address', - description: - 'The password will be sent to the visitor’s email address.', - ), - ), - const Spacer(), - ], - ), - ), - const SizedBox( - height: 15, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( + if (state is SuccessState) { + visitorBloc.stateDialog( + context: context, + message: 'Password Created Successfully', + title: 'Send Success', + ); + } else if (state is FailedState) { + visitorBloc.stateDialog( + context: context, + message: state.message, + title: 'Something Wrong', + ); + } + }, + child: BlocBuilder( + builder: (BuildContext context, VisitorPasswordState state) { + final visitorBloc = BlocProvider.of(context); + 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), + ), + content: + state is LoadingInitialState ?const Center(child: CircularProgressIndicator()): + SingleChildScrollView( + child: Form( + key: visitorBloc.forgetFormKey, + child: Padding( + padding: const EdgeInsets.all(5.0), + child: ListBody( + children: [ + Container( + child: Row( children: [ - Text( - '* ', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Colors.red), + Expanded( + flex: 2, + child: CustomWebTextField( + validator: visitorBloc.validate, + controller: visitorBloc.userNameController, + isRequired: true, + textFieldName: 'Name', + description: '', + ), ), - Text('Access Type', - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: Colors.black,fontSize: 13),), + const Spacer(), + Expanded( + flex: 2, + child: CustomWebTextField( + validator: visitorBloc.validateEmail, + controller: visitorBloc.emailController, + isRequired: true, + textFieldName: 'Email Address', + description: + 'The password will be sent to the visitor’s email address.', + ), + ), + const Spacer(), ], ), - Row( - children: [ - SizedBox( - width: size.width * 0.15, - child: RadioListTile( - 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 - : visitorBloc.accessTypeSelected, - onChanged: (String? value) { - if (value != null) { - print(value); - context - .read() - .add(SelectPasswordType(value)); - } - }, - ), - ), - SizedBox( - width: size.width * 0.15, - child: RadioListTile( - 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 - : visitorBloc.accessTypeSelected, - onChanged: (String? value) { - if (value != null) { - context.read() - .add(SelectPasswordType(value)); - } - }, - ), - ), - SizedBox( - width: size.width * 0.15, - child: RadioListTile( - 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 = ''; - } - }, - ), - ), - ], - ), - 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() - : Column( - crossAxisAlignment: CrossAxisAlignment.start, + ), + const SizedBox( + height: 15, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( 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),), - ], + Text( + '* ', + style: Theme.of(context).textTheme + .bodyMedium!.copyWith(color: Colors.red), ), - 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) { - 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), - ) + Text('Access Type', + style:text ), ], ), - const SizedBox( - height: 20, - ), - if ((visitorBloc.usageFrequencySelected != 'One-Time' || - visitorBloc.accessTypeSelected != 'Offline Password') && - (visitorBloc.usageFrequencySelected != '')) - DateTimeWebWidget( - isTime: false, - 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(), + Row( + children: [ + SizedBox( + width: size.width * 0.15, + child: RadioListTile( + title: Text('Online Password', + style: text, + ), + value: 'Online Password', + groupValue: (state is PasswordTypeSelected) + ? state.selectedType + : visitorBloc.accessTypeSelected, + onChanged: (String? value) { + if (value != null) { + context.read() + .add(SelectPasswordType(value)); + } + }, + ), + ), + SizedBox( + width: size.width * 0.15, + child: RadioListTile( + title: Text('Offline Password', + style:text ), + value: 'Offline Password', + groupValue: (state is PasswordTypeSelected) + ? state.selectedType + : visitorBloc.accessTypeSelected, + onChanged: (String? value) { + if (value != null) { + context.read().add(SelectPasswordType(value)); + } + }, + ), + ), + SizedBox( + width: size.width * 0.15, + child: RadioListTile( + 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 = ''; + } + }, + ), + ), + ], + ), + 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, + ) + ], ), - const SizedBox( - height: 20, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - '* ', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Colors.red), - ), - Text('Access Devices', - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: Colors.black,fontSize: 13),), - ], + visitorBloc.accessTypeSelected == 'Dynamic Password' + ? const SizedBox() + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text('* ', + style: Theme.of(context).textTheme.bodyMedium! + .copyWith(color: Colors.red), + ), + Text('Usage Frequency',style:text ,), + ], + ), + Row( + children: [ + SizedBox( + width: 200, + child: RadioListTile( + title: Text('One-Time', + style:text ,), + value: 'One-Time', + groupValue: + (state is UsageFrequencySelected) + ? state.selectedFrequency + : visitorBloc.usageFrequencySelected, + onChanged: (String? value) { + if (value != null) { + context.read() + .add(SelectUsageFrequency(value)); + } + }, + ), + ), + SizedBox( + width: 200, + child: RadioListTile( + title: Text('Periodic', + style: text), + 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( + isTime: false, + 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(), ), - 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, - ), - if (visitorBloc.usageFrequencySelected == 'Periodic' && - visitorBloc.accessTypeSelected == 'Online Password') - SizedBox( - width: 100, - child: Column( - children: [ - Text('Repeat', - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: Colors.black,fontSize: 13),), - Transform.scale( - scale: .8, - child: CupertinoSwitch( - value: visitorBloc.repeat, - onChanged: (value) { - visitorBloc.add(ToggleRepeatEvent()); - }, - applyTheme: true, - ), - ), - ], + const SizedBox( + height: 20, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text('* ', + style: Theme.of(context).textTheme.bodyMedium! + .copyWith(color: Colors.red), + ), + Text('Access Devices', + style:text ,), + ], + ), + 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, + ), + if (visitorBloc.usageFrequencySelected == 'Periodic' && + visitorBloc.accessTypeSelected == 'Online Password') + SizedBox( + width: 100, + child: Column( + children: [ + Text('Repeat', + style:text), + Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: visitorBloc.repeat, + onChanged: (value) { + visitorBloc.add(ToggleRepeatEvent()); + }, + applyTheme: true, + ), + ), + ], + ), + ), + if (visitorBloc.usageFrequencySelected == 'Periodic' && + visitorBloc.accessTypeSelected == 'Online Password') + isRepeat ? const RepeatWidget() : const SizedBox(), + Container( + decoration: containerDecoration, + width: size.width * 0.08, + child: DefaultButton( + onPressed: () { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return const AddDeviceDialog(); + }, + ).then((listDevice) { + if(listDevice!=null){ + visitorBloc.selectedDevices = listDevice; + } + }); + }, + borderRadius: 8, + child: Text('+ Add Device',style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.whiteColors,fontSize: 12),), ), ), - if (visitorBloc.usageFrequencySelected == 'Periodic' && - visitorBloc.accessTypeSelected == 'Online Password') - isRepeat ? const RepeatWidget() : const SizedBox(), - Container( - decoration: containerDecoration, - width: size.width * 0.08, - child: DefaultButton( - onPressed: () { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return const AddDeviceDialog(); - }, - ).then((listDevice) { - if(listDevice!=null){ - print('selectedDevices==$listDevice'); - visitorBloc.selectedDevices = listDevice; + ], + ), + ], + ), + ), + ), + ), + actionsAlignment: MainAxisAlignment.center, + actions: [ + Container( + decoration: containerDecoration, + width: size.width * 0.2, + 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.2, + child: DefaultButton( + onPressed: () { + if (visitorBloc.forgetFormKey.currentState!.validate()) { + if(visitorBloc.selectedDevices.isNotEmpty){ + if(visitorBloc.effectiveTimeTimeStamp!=null&&visitorBloc.expirationTimeTimeStamp!=null) { + setPasswordFunction(context, size, visitorBloc); + } + else{ + visitorBloc.stateDialog(context: + context,message: 'Please select Access Period to continue',title: 'Access Period'); + } + }else{ + visitorBloc.stateDialog(context: + context,message: 'Please select devices to continue',title: 'Select Devices'); + } + } + }, + borderRadius: 8, + child: Text('Ok', style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.whiteColors,fontSize: 16),), + ), + ), + ], + ); }, + ), + ), - } - }); + ); + } - }, - borderRadius: 8, - child: Text('+ Add Device',style: Theme.of(context).textTheme.bodySmall!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.whiteColors,fontSize: 12),), + Future setPasswordFunction( + BuildContext context, + Size size, + VisitorPasswordBloc visitorBloc, + ) { + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is LoadingInitialState) { + // Show loading indicator while loading + return AlertDialog( + alignment: Alignment.center, + content: SizedBox( + height: size.height * 0.25, + child: Center( + child: CircularProgressIndicator(), // Display a loading spinner + ), + ), + ); + }else{ + return AlertDialog( + alignment: Alignment.center, + content: SizedBox( + height: size.height * 0.25, + child: Column( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + child: SvgPicture.asset( + Assets.deviceNoteIcon, + height: 35, + width: 35, + ), + ), + Text( + 'Set Password', + style: Theme.of(context).textTheme.headlineLarge!.copyWith( + fontSize: 30, + fontWeight: FontWeight.w400, + color: Colors.black, ), ), ], ), + const SizedBox(width: 15), + Text( + 'This action will update all of the selected\n door locks passwords in the property.\n\nAre you sure you want to continue?', + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: ColorsManager.grayColor, + fontWeight: FontWeight.w400, + fontSize: 18, + ), + ), ], ), ), - ), - ), - actionsAlignment: MainAxisAlignment.center, - actions: [ - Container( - decoration: containerDecoration, - width: size.width * 0.2, - 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), + actionsAlignment: MainAxisAlignment.center, + actions: [ + Container( + decoration: containerDecoration, + width: size.width * 0.1, + child: DefaultButton( + borderRadius: 8, + onPressed: () { + Navigator.of(context).pop(); + }, + 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.2, - 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?', - actions: [ - Container( - decoration: containerDecoration, - width: size.width * 0.1, - child: DefaultButton( - borderRadius: 8, - onPressed: () { - Navigator.of(context).pop(); - }, - 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: Text('Ok', - style: Theme.of(context).textTheme.bodySmall!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.whiteColors,fontSize: 16), - ), - ), - ), - ], - ); - }, - ); - } - }, - borderRadius: 8, - child: Text('Ok', style: Theme.of(context).textTheme.bodySmall!.copyWith( - fontWeight: FontWeight.w400, - color: ColorsManager.whiteColors,fontSize: 16),), - ), - ), - ], - ); - }, - ), + Container( + decoration: containerDecoration, + width: size.width * 0.1, + child: DefaultButton( + borderRadius: 8, + onPressed: () { + Navigator.pop(context); + 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( + 'Ok', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontWeight: FontWeight.w400, + color: ColorsManager.whiteColors, + fontSize: 16, + ), + ), + ), + ), + ], + ); + } + }, + ); + }, ); } } diff --git a/lib/services/access_mang_api.dart b/lib/services/access_mang_api.dart index 26d7eeea..6a2cf40d 100644 --- a/lib/services/access_mang_api.dart +++ b/lib/services/access_mang_api.dart @@ -2,12 +2,11 @@ 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/device_model.dart'; import 'package:syncrow_web/pages/visitor_password/model/schedule_model.dart'; import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; -import '../pages/visitor_password/model/device_model.dart'; - class AccessMangApi{ Future> fetchVisitorPassword() async { @@ -17,7 +16,6 @@ class AccessMangApi{ showServerMessage: true, expectedResponseModel: (json) { List jsonData = json; - print('Password List: $json'); List passwordList = jsonData.map((jsonItem) { return PasswordModel.fromJson(jsonItem); }).toList(); @@ -38,7 +36,6 @@ class AccessMangApi{ showServerMessage: true, expectedResponseModel: (json) { List jsonData = json; - print('fetchDevices List: $json'); List passwordList = jsonData.map((jsonItem) { return DeviceModel.fromJson(jsonItem); }).toList(); @@ -60,18 +57,6 @@ class AccessMangApi{ String? invalidTime, List? devicesUuid}) async { try { - - print('postOfflineOneTime List: ${ - jsonEncode({ - "email": email, - "passwordName": passwordName, - "password": password, - "devicesUuid": devicesUuid, - "effectiveTime":effectiveTime , - "invalidTime": invalidTime - }) - }'); - final response = await HTTPService().post( path: ApiEndpoints.sendOnlineOneTime, body: jsonEncode({ @@ -84,7 +69,6 @@ class AccessMangApi{ }), showServerMessage: true, expectedResponseModel: (json) { - print('postOfflineOneTime List: $json'); if(json['statusCode'].toString()=='201'){ return true; }else{ @@ -93,9 +77,8 @@ class AccessMangApi{ }, ); return response; - } on DioException catch (e) { + } on DioException catch (e) { debugPrint('Error: ${e.message}'); - debugPrint('Error fetching ${e.response!.statusMessage}'); return false; } @@ -121,15 +104,11 @@ class AccessMangApi{ if (scheduleList != null) { body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList(); } - print('createPassword =${jsonEncode(body)}'); - final response = await HTTPService().post( path: ApiEndpoints.sendOnlineMultipleTime, body: jsonEncode(body), showServerMessage: true, expectedResponseModel: (json) { - print('createPassword =${json}'); - if(json['data']['successOperations'][0]['success'].toString()=='true'){ return true; }else{ @@ -138,7 +117,7 @@ class AccessMangApi{ }, ); return response; - } on DioException catch (e){ + } on DioException catch (e){ debugPrint('Error fetching ${e.type.name}'); debugPrint('Error fetching ${e.response!.statusMessage}'); return false; @@ -149,15 +128,6 @@ class AccessMangApi{ Future postOffLineOneTime({String? email,String? passwordName,List? devicesUuid}) async { try { - - print('postOfflineOneTime List: ${ - { - "email": email, - "passwordName": passwordName, - "devicesUuid": devicesUuid - } - }'); - final response = await HTTPService().post( path: ApiEndpoints.sendOffLineOneTime, body: jsonEncode({ @@ -166,10 +136,14 @@ class AccessMangApi{ "devicesUuid": devicesUuid }), showServerMessage: true, - expectedResponseModel: (json) { - List jsonData = json; - print('postOfflineOneTime List: $json'); - }, + expectedResponseModel: (json) { + if (json['data']['successOperations'][0]['success'].toString() == + 'true') { + return true; + } else { + return false; + } + } ); return response; } catch (e) { @@ -187,15 +161,6 @@ class AccessMangApi{ }) async { try { - - print('postOfflineOneTime List: ${ - { - "email": email, - "passwordName": passwordName, - "devicesUuid": devicesUuid - } - }'); - final response = await HTTPService().post( path: ApiEndpoints.sendOffLineOneTime, body: jsonEncode({ @@ -206,10 +171,14 @@ class AccessMangApi{ "invalidTime": invalidTime }), showServerMessage: true, - expectedResponseModel: (json) { - List jsonData = json; - print('postOfflineOneTime List: $json'); - }, + expectedResponseModel: (json) { + if (json['data']['successOperations'][0]['success'].toString() == + 'true') { + return true; + } else { + return false; + } + } ); return response; } catch (e) { diff --git a/lib/services/auth_api.dart b/lib/services/auth_api.dart index 55584055..3bb8f7e7 100644 --- a/lib/services/auth_api.dart +++ b/lib/services/auth_api.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; @@ -8,9 +7,7 @@ import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; class AuthenticationAPI { - static Future loginWithEmail({required var model}) async { - print('model=$model'); final response = await HTTPService().post( path: ApiEndpoints.login, body: model.toJson(), @@ -46,19 +43,15 @@ class AuthenticationAPI { }, showServerMessage: true, expectedResponseModel: (json) { - print('object==$json'); return 30; } - ); return 30; } on DioException catch (e) { if (e.response != null) { if (e.response!.statusCode == 400) { - // Handle 400 Bad Request final errorData = e.response!.data; String errorMessage = errorData['message']; - debugPrint('Unexpected Error: $errorMessage'); if(errorMessage=='User not found'){ return 1; }else{ @@ -87,8 +80,6 @@ class AuthenticationAPI { body: {"email": email, "type": "PASSWORD", "otpCode": otpCode}, showServerMessage: true, expectedResponseModel: (json) { - print('json=$json'); - if (json['message'] == 'Otp Verified Successfully') { return true; } else { @@ -99,12 +90,9 @@ class AuthenticationAPI { }on DioException catch (e){ if (e.response != null) { if (e.response!.statusCode == 400) { - // Handle 400 Bad Request final errorData = e.response!.data; String errorMessage = errorData['message']; - debugPrint('Unexpected Error: $errorMessage'); return errorMessage; - } } else { debugPrint('Error: ${e.message}'); diff --git a/lib/web_layout/web_scaffold.dart b/lib/web_layout/web_scaffold.dart index 567710bd..31b8d958 100644 --- a/lib/web_layout/web_scaffold.dart +++ b/lib/web_layout/web_scaffold.dart @@ -3,14 +3,12 @@ import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/web_layout/web_app_bar.dart'; import 'menu_sidebar.dart'; - class WebScaffold extends StatelessWidget { final bool enableMenuSideba; final Widget? appBarTitle; final List? appBarBody; final Widget? scaffoldBody; const WebScaffold({super.key,this.appBarTitle,this.appBarBody,this.scaffoldBody,this.enableMenuSideba=true}); - @override Widget build(BuildContext context) { return Scaffold(