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/assets/images/empty_table.svg b/assets/images/empty_table.svg new file mode 100644 index 00000000..24ac359d --- /dev/null +++ b/assets/images/empty_table.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/time_icon.svg b/assets/images/time_icon.svg new file mode 100644 index 00000000..a8f06677 --- /dev/null +++ b/assets/images/time_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/main.dart b/lib/main.dart index bf63e062..464406cc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +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/bloc/visitor_password_bloc.dart'; import 'package:syncrow_web/services/locator.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -28,6 +29,9 @@ class MyApp extends StatelessWidget { return MultiBlocProvider( providers: [ BlocProvider(create: (context) => HomeBloc()), + BlocProvider( + create: (context) => VisitorPasswordBloc(), + ) ], child: MaterialApp( debugShowCheckedModeBanner: false, // Hide debug banner @@ -39,6 +43,7 @@ class MyApp extends StatelessWidget { PointerDeviceKind.unknown, }, ), + theme: ThemeData( textTheme: const TextTheme( bodySmall: TextStyle( @@ -55,12 +60,10 @@ class MyApp extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - colorScheme: ColorScheme.fromSeed( seedColor: Colors.deepPurple), // Set up color scheme useMaterial3: true, // Enable Material 3 ), - // home: AddDeviceDialog() home: isLoggedIn == 'Success' ? const HomePage() : const LoginPage(), )); } diff --git a/lib/pages/access_management/bloc/access_bloc.dart b/lib/pages/access_management/bloc/access_bloc.dart index 7c57bfa4..47f865ef 100644 --- a/lib/pages/access_management/bloc/access_bloc.dart +++ b/lib/pages/access_management/bloc/access_bloc.dart @@ -5,23 +5,25 @@ import 'package:syncrow_web/pages/access_management/bloc/access_state.dart'; import 'package:syncrow_web/pages/access_management/model/password_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/const.dart'; import 'package:syncrow_web/utils/snack_bar.dart'; class AccessBloc extends Bloc { AccessBloc() : super((AccessInitial())) { on(_onFetchTableData); - on(selectFilterTap); + // on(selectFilterTap); on(selectTime); on(_filterData); on(resetSearch); + on(onTabChanged); } - String startTime = 'Start Time'; - String endTime = 'End Time'; + String startTime = 'Start Date'; + String endTime = 'End Date'; int? effectiveTimeTimeStamp; int? expirationTimeTimeStamp; TextEditingController passwordName= TextEditingController(); - List filteredData = []; // To store filtered data + List filteredData = []; List data=[]; Future _onFetchTableData( @@ -29,14 +31,25 @@ class AccessBloc extends Bloc { try { emit(AccessLoaded()); data = await AccessMangApi().fetchVisitorPassword(); - emit(TableLoaded(data)); + filteredData= data; + updateTabsCount(); + emit(TableLoaded(data)); } catch (e) { emit(FailedState(e.toString())); } } + void updateTabsCount() { + 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; + tabs[1] = 'To Be Effective ($toBeEffectiveCount)'; + tabs[2] = 'Effective ($effectiveCount)'; + tabs[3] = 'Expired ($expiredCount)'; + } + + int selectedIndex = 0; - final List tabs = [ 'All', 'To Be Effective (0)', @@ -49,9 +62,8 @@ class AccessBloc extends Bloc { try { emit(AccessLoaded()); selectedIndex= event.selectedIndex; - emit(AccessInitial()); + emit(AccessInitial()); emit(TableLoaded(data)); - } catch (e) { emit(FailedState( e.toString())); return; @@ -60,6 +72,8 @@ class AccessBloc extends Bloc { Future selectTime(SelectTime event, Emitter emit) async { + emit(AccessLoaded()); + final DateTime? picked = await showDatePicker( context: event.context, initialDate: DateTime.now(), @@ -120,27 +134,23 @@ class AccessBloc extends Bloc { } } } - emit(AccessInitial()); - emit(TableLoaded(data)); + emit(ChangeTimeState()); } + Future _filterData(FilterDataEvent event, Emitter emit) async { emit(AccessLoaded()); try { - // Filter the data based on the provided criteria filteredData = data.where((item) { bool matchesCriteria = true; - // Check if the password name should be used for filtering if (event.passwordName != null && event.passwordName!.isNotEmpty) { - final bool matchesName = item.passwodName != null && - item.passwodName.contains(event.passwordName!); + final bool matchesName = item.passwordName != null && + item.passwordName.contains(event.passwordName); if (!matchesName) { matchesCriteria = false; } } - // Check if the time range should be used for filtering if (event.startTime != null && event.endTime != null) { - // Ensure effectiveTime and invalidTime are treated as integers final int? effectiveTime = int.tryParse(item.effectiveTime.toString()); final int? invalidTime = int.tryParse(item.invalidTime.toString()); if (effectiveTime == null || invalidTime == null) { @@ -153,22 +163,30 @@ class AccessBloc extends Bloc { } } } + if (event.selectedTabIndex == 1 && item.passwordStatus.value != 'To Be Effective') { + matchesCriteria = false; + } else if (event.selectedTabIndex == 2 && item.passwordStatus.value != 'Effective') { + matchesCriteria = false; + } else if (event.selectedTabIndex == 3 && item.passwordStatus.value != 'Expired') { + matchesCriteria = false; + } return matchesCriteria; }).toList(); - print('Filtered data: $filteredData'); emit(TableLoaded(filteredData)); } catch (e) { - print('Error occurred during filtering: $e'); + emit(FailedState(e.toString())); } } - // ResetSearch + resetSearch(ResetSearch event, Emitter emit) async{ emit(AccessLoaded()); startTime = 'Start Time'; endTime = 'End Time'; passwordName.clear(); + selectedIndex=0; + effectiveTimeTimeStamp=null; + expirationTimeTimeStamp=null; add(FetchTableData()); - } String timestampToDate(dynamic timestamp) { @@ -176,5 +194,36 @@ class AccessBloc extends Bloc { return "${dateTime.year}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.day.toString().padLeft(2, '0')}"; } + Future onTabChanged(TabChangedEvent event, Emitter emit) async { + try { + emit(AccessLoaded()); + selectedIndex = event.selectedIndex; + switch (selectedIndex) { + case 0: // All + filteredData = data; + break; + case 1: // To Be Effective + filteredData = data.where((item) => item.passwordStatus.value == "To Be Effective").toList(); + 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(); + break; + default: + filteredData = data; + } + add(FilterDataEvent( + selectedTabIndex: selectedIndex, + passwordName: passwordName.text.toLowerCase(), + startTime: effectiveTimeTimeStamp, + endTime: expirationTimeTimeStamp + )); + emit(TableLoaded(filteredData)); + } catch (e) { + emit(FailedState(e.toString())); + } + } } diff --git a/lib/pages/access_management/bloc/access_event.dart b/lib/pages/access_management/bloc/access_event.dart index 3a64a11a..f2f631b4 100644 --- a/lib/pages/access_management/bloc/access_event.dart +++ b/lib/pages/access_management/bloc/access_event.dart @@ -31,10 +31,16 @@ class FilterDataEvent extends AccessEvent { final String? passwordName; final int? startTime; final int? endTime; + final int selectedTabIndex; // Add this field const FilterDataEvent({ this.passwordName, this.startTime, this.endTime, + required this.selectedTabIndex, // Initialize this field + }); } + + + diff --git a/lib/pages/access_management/model/password_model.dart b/lib/pages/access_management/model/password_model.dart index acd3e13a..584e9b7e 100644 --- a/lib/pages/access_management/model/password_model.dart +++ b/lib/pages/access_management/model/password_model.dart @@ -6,8 +6,8 @@ class PasswordModel { final dynamic effectiveTime; final dynamic passwordCreated; final dynamic createdTime; - final dynamic passwodName; // New field - final dynamic passwordStatus; + final dynamic passwordName; // New field + final AccessStatus passwordStatus; final AccessType passwordType; final dynamic deviceUuid; @@ -17,8 +17,8 @@ class PasswordModel { this.effectiveTime, this.passwordCreated, this.createdTime, - this.passwodName, // New field - this.passwordStatus, + this.passwordName, // New field + required this.passwordStatus, required this.passwordType, this.deviceUuid, }); @@ -30,9 +30,9 @@ class PasswordModel { effectiveTime: json['effectiveTime'], passwordCreated: json['passwordCreated'], createdTime: json['createdTime'], - passwodName: json['passwodName']??'No name', // New field - passwordStatus: json['passwordStatus'], - passwordType:AccessTypeExtension.fromString(json['passwordType']) , + passwordName: json['passwordName']??'No name', // New field + passwordStatus:AccessStatusExtension.fromString(json['passwordStatus']), + passwordType:AccessTypeExtension.fromString(json['passwordType']), deviceUuid: json['deviceUuid'], ); } @@ -44,13 +44,11 @@ class PasswordModel { 'effectiveTime': effectiveTime, 'passwordCreated': passwordCreated, 'createdTime': createdTime, - 'passwodName': passwodName, // New field + 'passwodName': passwordName, // New field 'passwordStatus': passwordStatus, 'passwordType': passwordType, 'deviceUuid': deviceUuid, }; } - List parsePasswordList(List jsonList) { - return jsonList.map((json) => PasswordModel.fromJson(json)).toList(); - } + } diff --git a/lib/pages/access_management/view/access_management.dart b/lib/pages/access_management/view/access_management.dart index de8dc102..4c7023bb 100644 --- a/lib/pages/access_management/view/access_management.dart +++ b/lib/pages/access_management/view/access_management.dart @@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/filter/filter_widget.dart'; import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/const.dart'; import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; @@ -41,270 +42,278 @@ class AccessManagementPage extends StatelessWidget { create: (BuildContext context) => AccessBloc()..add(FetchTableData()), child: BlocConsumer( - listener: (context, state) { - if (state is FailedState) { - // CustomSnackBar.displaySnackBar( - // state.errorMessage - // ); - } - }, builder: (context, state) { - final accessBloc = BlocProvider.of(context); - return Container( - padding: EdgeInsets.all(30), - height: size.height, - width: size.width, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FilterWidget( - size: size, - tabs: accessBloc.tabs, - selectedIndex: accessBloc.selectedIndex, - onTabChanged: (index) { - accessBloc.add(TabChangedEvent(index)); - }, - ), - const SizedBox( - height: 20, - ), - Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Text('Name'), - Container( - width: size.width * 0.15, - decoration: containerDecoration, - child: TextFormField( - controller: accessBloc.passwordName, - style: const TextStyle(color: Colors.black), - decoration: textBoxDecoration()! - .copyWith(hintText: 'Please enter'), - )), - ], - ), - const SizedBox( - width: 15, - ), - DateTimeWebWidget( - isRequired: false, - title: 'Access Time', - size: size, - endTime: () { - accessBloc.add( - SelectTime(context: context, isStart: false)); - }, - startTime: () { - accessBloc.add( - SelectTime(context: context, isStart: true)); - }, - firstString: - BlocProvider.of(context).startTime, - secondString: - BlocProvider.of(context).endTime, - ), - const SizedBox( - width: 15, - ), - SizedBox( - width: size.width * 0.06, + listener: (context, state) {}, + builder: (context, state) { + final accessBloc = BlocProvider.of(context); + final filteredData = accessBloc.filteredData; + return state is AccessLoaded + ? const Center(child: CircularProgressIndicator()) + : Container( + padding: EdgeInsets.all(30), + height: size.height, + width: size.width, child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(''), - Container( - decoration: containerDecoration, - child: DefaultButton( - onPressed: () { - accessBloc.add(FilterDataEvent( - passwordName: - accessBloc.passwordName.text, - startTime: accessBloc - .effectiveTimeTimeStamp, - endTime: accessBloc - .expirationTimeTimeStamp)); - }, - borderRadius: 9, - child: const Text('Search'))), - ], - ), - ), - const SizedBox( - width: 10, - ), - SizedBox( - 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), + height: size.height * 0.05, + child: Flexible( + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: + BlocProvider.of(context) + .tabs + .length, + shrinkWrap: true, + itemBuilder: (context, index) { + final isSelected = index == + BlocProvider.of(context) + .selectedIndex; + return InkWell( + onTap: () { + BlocProvider.of(context) + .add(TabChangedEvent(index)); + }, + child: Container( + decoration: BoxDecoration( + color: ColorsManager.boxColor, + border: Border.all( + color: isSelected + ? Colors.blue + : Colors.transparent, + width: 2.0, + ), + borderRadius: index == 0 + ? const BorderRadius.only( + topLeft: + Radius.circular(10), + bottomLeft: + Radius.circular(10)) + : index == 3 + ? const BorderRadius.only( + topRight: + Radius.circular(10), + bottomRight: + Radius.circular(10)) + : null, + ), + padding: const EdgeInsets.only( + left: 10, right: 10), + child: Center( + child: Text( + BlocProvider.of( + context) + .tabs[index], + style: TextStyle( + color: isSelected + ? Colors.blue + : Colors.black, + ), + ), + ), + ), + ); + }, ), ), ), + const SizedBox( + height: 20, + ), + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + textBaseline: TextBaseline.ideographic, + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Name', + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: Colors.black, + fontSize: 13), + ), + const SizedBox( + height: 5, + ), + Container( + height: 43, + width: size.width * 0.15, + decoration: containerDecoration, + child: TextFormField( + controller: accessBloc.passwordName, + style: const TextStyle( + color: Colors.black), + decoration: textBoxDecoration()! + .copyWith( + hintText: 'Please enter'), + )), + ], + ), + const SizedBox( + width: 15, + ), + DateTimeWebWidget( + icon: Assets.calendarIcon, + isRequired: false, + title: 'Access Time', + size: size, + endTime: () { + accessBloc.add(SelectTime( + context: context, isStart: false)); + }, + startTime: () { + accessBloc.add(SelectTime( + context: context, isStart: true)); + }, + firstString: + BlocProvider.of(context) + .startTime, + secondString: + BlocProvider.of(context) + .endTime, + ), + const SizedBox( + width: 15, + ), + SizedBox( + height: 45, + width: size.width * 0.06, + 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: 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), + ), + ), + ), + ), + ], + ), + const SizedBox( + height: 20, + ), + Wrap( + children: [ + Container( + width: size.width * 0.15, + decoration: containerDecoration, + child: DefaultButton( + onPressed: () { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return const VisitorPasswordDialog(); + }, + ).then((v) { + if (v != null) { + accessBloc.add(FetchTableData()); + } + }); + }, + borderRadius: 8, + child: const Text( + '+ Create Visitor Password ')), + ), + const SizedBox( + width: 10, + ), + Container( + width: size.width * 0.12, + decoration: containerDecoration, + child: DefaultButton( + borderRadius: 8, + backgroundColor: + ColorsManager.whiteColors, + child: Text( + 'Admin Password', + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(color: Colors.black), + ))) + ], + ), + const SizedBox( + height: 20, + ), + Expanded( + child: DynamicTable( + isEmpty: filteredData.isEmpty, + withCheckBox: false, + size: size, + cellDecoration: containerDecoration, + headers: const [ + 'Name', + 'Access Type', + 'Access Period', + 'Accessible Device', + 'Authorizer', + 'Authorization Date & Time', + 'Access Status' + ], + data: filteredData.map((item) { + return [ + item.passwordName.toString(), + item.passwordType.value, + ('${accessBloc.timestampToDate(item.effectiveTime)} - ${accessBloc.timestampToDate(item.invalidTime)}'), + item.deviceUuid.toString(), + '', + '', + item.passwordStatus.value + ]; + }).toList(), + ) + // : const Center(child: CircularProgressIndicator()), + ) ], ), - ), - ], - ), - const SizedBox( - height: 20, - ), - Wrap( - children: [ - Container( - width: size.width * 0.15, - decoration: containerDecoration, - child: DefaultButton( - onPressed: () { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return const VisitorPasswordDialog(); - }, - ); - }, - borderRadius: 8, - child: - const Text('+ Create Visitor Password ')), - ), - const SizedBox( - width: 10, - ), - Container( - width: size.width * 0.12, - decoration: containerDecoration, - child: DefaultButton( - borderRadius: 8, - backgroundColor: ColorsManager.whiteColors, - child: Text( - 'Admin Password', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: Colors.black), - ))) - ], - ), - const SizedBox( - height: 20, - ), - Expanded( - child: state is TableLoaded - ? DynamicTable( - withCheckBox: false, - size: size, - cellDecoration: containerDecoration, - headers: const [ - 'Name', - 'Access Type', - 'Access Period', - 'Device Id', - 'Authorizer', - 'Authorization Date & Time', - 'Access Status' - ], - data: state.data.map((item) { - return [ - item.passwodName.toString(), - item.passwordType.value, - ('${accessBloc.timestampToDate(item.effectiveTime)} - ${accessBloc.timestampToDate(item.invalidTime)}'), - item.deviceUuid.toString(), - '', - '', - '' - ]; - }).toList(), - ) - : const Center(child: CircularProgressIndicator()), - ) - ], - ), - ); - }))); + ); + }))); } - - // Container TableWidget(Size size, TableLoaded state,AccessBloc accessBloc) { - // return Container( - // decoration: containerDecoration, - // width: size.width, - // child: Padding( - // padding: const EdgeInsets.all(10.0), - // child: ListView( - // scrollDirection: Axis.horizontal, - // children: [ - // Container( - // width: size.width, - // height: size.height, - // child: Column( - // children: [ - // Container( - // color: ColorsManager.boxColor, - // child: Row( - // children: [ - // _buildTableHeaderCell('Password name'), - // _buildTableHeaderCell(' Password Type'), - // _buildTableHeaderCell('Start Time'), - // _buildTableHeaderCell('End Time'), - // _buildTableHeaderCell('Device Id'), - // // _buildTableHeaderCell('Authorization Source'), - // // _buildTableHeaderCell('Authorizer'), - // _buildTableHeaderCell('Password Created'), - // // _buildTableHeaderCell('Access Status'), - // _buildTableHeaderCell('Password Status'), - // ], - // ), - // ), - // Expanded( - // child: Container( - // width: size.width, - // color: ColorsManager.whiteColors, - // child: ListView( - // shrinkWrap: true, - // children: [ - // Column( - // children: state.data.map((item) { - // return Row( - // children: [ - // _buildTableCell(item.passwodName), - // _buildTableCell(item.passwordType), - // - // _buildTableCell(accessBloc.timestampToDateTime(item.effectiveTime).toString()), - // _buildTableCell(accessBloc.timestampToDateTime(item.invalidTime).toString()), - // _buildTableCell(item.deviceUuid.toString()), - // // _buildTableCell(item.authorizationSource), - // // _buildTableCell(item.authorizer), - // _buildTableCell(item.passwordCreated!=null?accessBloc.timestampToDateTime(item.passwordCreated).toString():'no data'), - // // _buildTableCell(item.accessStatus), - // _buildTableCell(item.passwordStatus.toString()), - // ], - // ); - // }).toList(), - // ), - // ], - // ), - // ), - // ), - // ], - // ), - // ), - // ], - // ), - // ), - // ); - // } } diff --git a/lib/pages/auth/bloc/auth_bloc.dart b/lib/pages/auth/bloc/auth_bloc.dart index 120de89a..514e158b 100644 --- a/lib/pages/auth/bloc/auth_bloc.dart +++ b/lib/pages/auth/bloc/auth_bloc.dart @@ -84,6 +84,9 @@ class AuthBloc extends Bloc { } 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!'; @@ -91,7 +94,7 @@ class AuthBloc extends Bloc { // emit(FailureForgetState(error: failure.toString())); } } - +//925207 String? validateCode(String? value) { if (value == null || value.isEmpty) { return 'Code is required'; diff --git a/lib/pages/auth/view/login_mobile_page.dart b/lib/pages/auth/view/login_mobile_page.dart index d2787f04..f86ec301 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/auth/view/login_web_page.dart b/lib/pages/auth/view/login_web_page.dart index 77c7afba..893c3a86 100644 --- a/lib/pages/auth/view/login_web_page.dart +++ b/lib/pages/auth/view/login_web_page.dart @@ -166,8 +166,7 @@ class _LoginWebPageState extends State { return DropdownMenuItem( value: region.id, child: SizedBox( - width: size.width*0.06, - + width: size.width*0.08, child: Text(region.name)), ); }).toList(), 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 93ae29ce..6b84a76e 100644 --- a/lib/pages/common/custom_table.dart +++ b/lib/pages/common/custom_table.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; class DynamicTable extends StatefulWidget { final List headers; @@ -8,20 +10,24 @@ class DynamicTable extends StatefulWidget { final BoxDecoration? cellDecoration; final Size size; final bool withCheckBox; + final bool isEmpty; final void Function(bool?)? selectAll; final void Function(int, bool?)? onRowCheckboxChanged; + final List? initialSelectedIds; const DynamicTable({ - Key? key, + super.key, required this.headers, required this.data, required this.size, + required this.isEmpty, required this.withCheckBox, this.headerDecoration, this.cellDecoration, this.selectAll, this.onRowCheckboxChanged, - }) : super(key: key); + this.initialSelectedIds, + }); @override _DynamicTableState createState() => _DynamicTableState(); @@ -34,7 +40,11 @@ class _DynamicTableState extends State { @override void initState() { super.initState(); - _selected = List.filled(widget.data.length, false); + _selected = List.generate(widget.data.length, (index) { + return widget.initialSelectedIds != null && + widget.initialSelectedIds!.contains(widget.data[index][1]); + }); + _selectAll = _selected.every((element) => element == true); } void _toggleSelectAll(bool? value) { @@ -62,7 +72,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, children: [ @@ -82,27 +92,60 @@ class _DynamicTableState extends State { ], ), ), - Expanded( - child: Container( - color: Colors.white, - child: ListView.builder( - shrinkWrap: true, - itemCount: widget.data.length, - itemBuilder: (context, index) { - final row = widget.data[index]; - return Row( + widget.isEmpty + ? Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - if (widget.withCheckBox) _buildRowCheckbox(index), - ...row - .map((cell) => - _buildTableCell(cell.toString())) - .toList(), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + children: [ + SvgPicture.asset(Assets.emptyTable), + const SizedBox( + height: 15, + ), + Text( + 'No Passwords', + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: ColorsManager.grayColor), + ) + ], + ), + ], + ), ], - ); - }, - ), - ), - ), + ), + ) + : Expanded( + child: Container( + color: Colors.white, + child: ListView.builder( + shrinkWrap: true, + itemCount: widget.data.length, + itemBuilder: (context, index) { + final row = widget.data[index]; + return Row( + children: [ + if (widget.withCheckBox) + _buildRowCheckbox( + index, widget.size.height * 0.10), + ...row + .map((cell) => _buildTableCell( + cell.toString(), + widget.size.height * 0.10)) + .toList(), + ], + ); + }, + ), + ), + ), ], ), ), @@ -113,8 +156,11 @@ 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, @@ -122,16 +168,26 @@ class _DynamicTableState extends State { ); } - Widget _buildRowCheckbox(int index) { - return SizedBox( - width: 50, - child: Checkbox( - value: _selected[index], - onChanged: (bool? value) { - _toggleRowSelection(index, value); - }, - ), - ); + 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) { @@ -146,17 +202,18 @@ class _DynamicTableState extends State { child: Text(title, style: const TextStyle( fontWeight: FontWeight.w400, - color: ColorsManager.lightGreyColor)), + fontSize: 13, + color: Color(0xFF999999))), ), ), ); } - 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( @@ -167,7 +224,8 @@ class _DynamicTableState extends State { alignment: Alignment.centerLeft, child: Text( content, - style: const TextStyle(color: Colors.black, fontSize: 12), + style: const TextStyle( + color: Colors.black, fontSize: 10, fontWeight: FontWeight.w400), ), ), ); diff --git a/lib/pages/common/date_time_widget.dart b/lib/pages/common/date_time_widget.dart index ace2cf8d..d8fbfe51 100644 --- a/lib/pages/common/date_time_widget.dart +++ b/lib/pages/common/date_time_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/style.dart'; @@ -13,6 +14,7 @@ class DateTimeWebWidget extends StatelessWidget { required this.endTime, required this.firstString, required this.secondString, + required this.icon, }); final Size size; @@ -20,6 +22,7 @@ class DateTimeWebWidget extends StatelessWidget { final bool isRequired; final String firstString; final String secondString; + final String icon; final Function()? startTime; final Function()? endTime; @@ -39,35 +42,52 @@ class DateTimeWebWidget extends StatelessWidget { .bodyMedium! .copyWith(color: Colors.red), ), - Text(title??''), + Text(title??'' , + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13),), ], ), - SizedBox(height: 8,), + const SizedBox(height: 8,), Container( - width: size.width * 0.25, - padding: EdgeInsets.all(10), - decoration: containerDecoration, - child: Column( - children: [ - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - InkWell( - onTap: startTime, - child: Text(firstString) - ), - const Icon(Icons.arrow_right_alt), - InkWell( - onTap:endTime, - child: Text(secondString)), - SvgPicture.asset( - Assets.calendarIcon, - ), - ], - ), - ], - )), + height:size.height * 0.055 , + padding: EdgeInsets.only(top: 10,bottom: 10,right: 30,left: 10), + decoration: containerDecoration, + child: FittedBox( + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + InkWell( + onTap: startTime, + child: FittedBox( + child: Text(firstString, + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor,fontSize: 12,fontWeight: FontWeight.w400),), + ) + ), + SizedBox(width: 30,), + const Icon(Icons.arrow_right_alt), + SizedBox(width: 30,), + + InkWell( + onTap:endTime, + child: FittedBox( + child: Text(secondString, + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor,fontSize: 12,fontWeight: FontWeight.w400), + ), + )), + SizedBox(width: 30,), + + SvgPicture.asset( + icon, + ), + ], + ), + ], + )), + ), ], ); } diff --git a/lib/pages/common/hour_picker_dialog.dart b/lib/pages/common/hour_picker_dialog.dart new file mode 100644 index 00000000..718f1ebf --- /dev/null +++ b/lib/pages/common/hour_picker_dialog.dart @@ -0,0 +1,92 @@ + + +import 'package:flutter/material.dart'; + +class HourPickerDialog extends StatefulWidget { + final TimeOfDay initialTime; + const HourPickerDialog({super.key, required this.initialTime}); + + @override + _HourPickerDialogState createState() => _HourPickerDialogState(); +} + +class _HourPickerDialogState extends State { + late int _selectedHour; + bool _isPm = false; + + @override + void initState() { + super.initState(); + _selectedHour = widget.initialTime.hour > 12 ? widget.initialTime.hour - 12 : widget.initialTime.hour; + _isPm = widget.initialTime.period == DayPeriod.pm; + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Select Hour'), + content: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + DropdownButton( + value: _selectedHour, + items: List.generate(12, (index) { + int displayHour = index + 1; + return DropdownMenuItem( + value: displayHour, + child: Text(displayHour.toString()), + ); + }), + onChanged: (value) { + setState(() { + _selectedHour = value!; + }); + }, + ), + SizedBox(width: 16.0), + DropdownButton( + value: _isPm, + items: const [ + DropdownMenuItem( + value: false, + child: Text('AM'), + ), + DropdownMenuItem( + value: true, + child: Text('PM'), + ), + ], + onChanged: (value) { + setState(() { + _isPm = value!; + }); + }, + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(null), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + int hour = _isPm ? _selectedHour + 12 : _selectedHour; + Navigator.of(context).pop(TimeOfDay(hour: hour, minute: 0)); + }, + child: const Text('OK'), + ), + ], + ); + } +} + +Future showHourPicker({ + required BuildContext context, + required TimeOfDay initialTime, +}) { + return showDialog( + context: context, + builder: (context) => HourPickerDialog(initialTime: initialTime), + ); +} 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/common/text_field/custom_web_textfield.dart b/lib/pages/common/text_field/custom_web_textfield.dart index 4ec0aa81..363a1994 100644 --- a/lib/pages/common/text_field/custom_web_textfield.dart +++ b/lib/pages/common/text_field/custom_web_textfield.dart @@ -33,23 +33,23 @@ class CustomWebTextField extends StatelessWidget { children: [ Text('* ', style: Theme.of(context) - .textTheme - .bodyMedium! + .textTheme.bodyMedium! .copyWith(color: Colors.red), ), - Text(textFieldName), + Text(textFieldName, style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black,fontSize: 13),), ], ), const SizedBox(width: 10,), - Text( - description??'', // ' The password will be sent to the visitor’s email address.', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith( - fontSize: 9, - fontWeight: FontWeight.w400, - color: ColorsManager.textGray), + Expanded( + child: Text( + description??'', + style: Theme.of(context) + .textTheme.bodySmall! + .copyWith(fontSize: 9, + fontWeight: FontWeight.w400, + color: ColorsManager.textGray), + ), ), ], ), @@ -62,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/home/bloc/home_bloc.dart b/lib/pages/home/bloc/home_bloc.dart index 2b0b5035..58d452ae 100644 --- a/lib/pages/home/bloc/home_bloc.dart +++ b/lib/pages/home/bloc/home_bloc.dart @@ -65,7 +65,7 @@ class HomeBloc extends Bloc { color: null, ), HomeItemModel( - title: 'Space\nManagement', + title: 'Space Management', icon: Assets.spaseManagementIcon, active: true, onPress: (context) {}, diff --git a/lib/pages/home/view/home_card.dart b/lib/pages/home/view/home_card.dart index 287bb4f4..ce152c1c 100644 --- a/lib/pages/home/view/home_card.dart +++ b/lib/pages/home/view/home_card.dart @@ -37,14 +37,16 @@ class HomeCard extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - FittedBox( - fit: BoxFit.scaleDown, - child: Text( - name, - style: const TextStyle( - fontSize: 20, - color: Colors.white, - fontWeight: FontWeight.bold, + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + name, + style: const TextStyle( + fontSize: 20, + color: Colors.white, + fontWeight: FontWeight.bold, + ), ), ), ), 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 d9f49e56..2a3e486f 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart @@ -1,18 +1,18 @@ 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'; 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'; - List selectedDevices = []; -// Define the BLoC class VisitorPasswordBloc extends Bloc { VisitorPasswordBloc() : super(VisitorPasswordInitial()) { @@ -24,13 +24,12 @@ class VisitorPasswordBloc on(toggleDaySelection); on(selectDevice); on(_onUpdateFilteredDevices); - on(postOnlineOneTimePassword); on(postOnlineMultipleTimePassword); - on(postOfflineMultipleTimePassword); on(postOfflineOneTimePassword); - + on(selectTimeOfLinePassword); + on(changeTime); } final TextEditingController userNameController = TextEditingController(); final TextEditingController emailController = TextEditingController(); @@ -38,14 +37,14 @@ class VisitorPasswordBloc final TextEditingController deviceNameController = TextEditingController(); final TextEditingController deviceIdController = TextEditingController(); final TextEditingController unitNameController = TextEditingController(); - final TextEditingController virtualAddressController = TextEditingController(); - - + final TextEditingController virtualAddressController = + TextEditingController(); + List selectedDevices = []; List data = []; List selectedDeviceIds = []; - - + String effectiveTime = 'Start Time'; + String expirationTime = 'End Time'; final forgetFormKey = GlobalKey(); @@ -58,18 +57,11 @@ class VisitorPasswordBloc int? effectiveTimeTimeStamp; int? expirationTimeTimeStamp; - int? repeatEffectiveTimeTimeStamp; - int? repeatExpirationTimeTimeStamp; + DateTime? startTime = DateTime.now(); + DateTime? endTime; - String startTime = 'Start Time'; - String endTime = 'End Time'; - - - String repeatStartTime = 'Start Time'; - String repeatEndTime = 'End Time'; - - // DateTime? repeatStartTime=DateTime.now(); - // DateTime? repeatEndTime; + String startTimeAccess = 'Start Time'; + String endTimeAccess = 'End Time'; selectAccessType( SelectPasswordType event, Emitter emit) { @@ -84,14 +76,16 @@ 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(), firstDate: DateTime(2015, 8), lastDate: DateTime(3101), ); + if (picked != null) { final TimeOfDay? timePicked = await showTimePicker( context: event.context, @@ -113,6 +107,7 @@ class VisitorPasswordBloc ); }, ); + if (timePicked != null) { final selectedDateTime = DateTime( picked.year, @@ -121,49 +116,35 @@ class VisitorPasswordBloc timePicked.hour, timePicked.minute, ); - final selectedTimestamp = DateTime( - selectedDateTime.year, - selectedDateTime.month, - selectedDateTime.day, - selectedDateTime.hour, - selectedDateTime.minute, - ).millisecondsSinceEpoch ~/ - 1000; // Divide by 1000 to remove milliseconds - if (event.isStart) { - if (expirationTimeTimeStamp != null && selectedTimestamp >expirationTimeTimeStamp!) { - CustomSnackBar.displaySnackBar('Effective Time cannot be later than Expiration Time.'); - } else { - if(event.isRepeat==true) - {repeatStartTime = selectedDateTime.toString().split('.').first;} - else // Remove seconds and milliseconds - {startTime = selectedDateTime.toString().split('.').first;} - effectiveTimeTimeStamp = selectedTimestamp; - emit(ChangeTimeState()); + final selectedTimestamp = + selectedDateTime.millisecondsSinceEpoch ~/ 1000; + + if (event.isStart) { + if (expirationTimeTimeStamp != null && + selectedTimestamp > expirationTimeTimeStamp!) { + CustomSnackBar.displaySnackBar( + 'Effective Time cannot be later than Expiration Time.', + ); + return; } - emit(ChangeTimeState()); + effectiveTimeTimeStamp = selectedTimestamp; + startTimeAccess = selectedDateTime.toString().split('.').first; } else { if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) { - CustomSnackBar.displaySnackBar('Expiration Time cannot be earlier than Effective Time.'); - } else { - if(event.isRepeat==true) - {repeatEndTime = selectedDateTime.toString().split('.').first;} - else - {endTime = selectedDateTime.toString().split('.').first;} - expirationTimeTimeStamp = selectedTimestamp; - emit(ChangeTimeState()); - + CustomSnackBar.displaySnackBar( + 'Expiration Time cannot be earlier than Effective Time.', + ); + return; } - emit(ChangeTimeState()); - - emit(VisitorPasswordInitial()); + expirationTimeTimeStamp = selectedTimestamp; + endTimeAccess = selectedDateTime.toString().split('.').first; } + emit(ChangeTimeState()); + emit(VisitorPasswordInitial()); } } - - // emit(AccessInitial()); - // emit(TableLoaded(data)); } bool toggleRepeat( @@ -211,67 +192,99 @@ class VisitorPasswordBloc } //online password - - Future postOnlineOneTimePassword( - OnlineOneTimePasswordEvent event, + Future postOnlineOneTimePassword(OnlineOneTimePasswordEvent event, Emitter emit) async { try { - print('selectedDevices$selectedDevices'); - // emit(DeviceLoaded()); - await AccessMangApi().postOnlineOneTime( + emit(LoadingInitialState()); + generate7DigitNumber(); + bool res = await AccessMangApi().postOnlineOneTime( email: event.email, + password: passwordController, devicesUuid: selectedDevices, - passwordName: event.passwordName); - // emit(TableLoaded(data)); + passwordName: event.passwordName, + effectiveTime: effectiveTimeTimeStamp.toString(), + invalidTime: expirationTimeTimeStamp.toString()); + if (res == true) { + 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(); - // emit(DeviceLoaded()); - await AccessMangApi().postOnlineMultipleTime( - scheduleList:[ - // if (repeat) - // Schedule( - // effectiveTime: getTimeOnly(repeatStartTime), - // invalidTime: getTimeOnly(repeatEndTime).toString(), - // workingDay: selectedDays, - // ), - ] , + emit(LoadingInitialState()); + + await generate7DigitNumber(); + bool res = await AccessMangApi().postOnlineMultipleTime( + scheduleList: [ + if (repeat) + Schedule( + effectiveTime: getTimeFromDateTimeString(expirationTime), + invalidTime: + getTimeFromDateTimeString(effectiveTime).toString(), + workingDay: selectedDays, + ), + ], password: passwordController, - invalidTime:event.invalidTime , - effectiveTime:event.effectiveTime , + invalidTime: expirationTimeTimeStamp.toString(), + effectiveTime: effectiveTimeTimeStamp.toString(), email: event.email, devicesUuid: selectedDevices, - passwordName: event.passwordName - ); - // emit(TableLoaded(data)); + passwordName: event.passwordName); + if (res == true) { + 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'); } } - //offline password - Future postOfflineOneTimePassword( - OfflineOneTimePasswordEvent event, + Future postOfflineOneTimePassword(OfflineOneTimePasswordEvent event, Emitter emit) async { try { - generate7DigitNumber(); - // emit(DeviceLoaded()); - await AccessMangApi().postOffLineOneTime( + emit(LoadingInitialState()); + await generate7DigitNumber(); + bool res = await AccessMangApi().postOffLineOneTime( email: event.email, devicesUuid: selectedDevices, - passwordName: event.passwordName - ); - // emit(TableLoaded(data)); + passwordName: event.passwordName); + 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'); } } @@ -279,22 +292,31 @@ class VisitorPasswordBloc OfflineMultipleTimePasswordEvent event, Emitter emit) async { try { - generate7DigitNumber(); - // emit(DeviceLoaded()); - await AccessMangApi().postOffLineMultipleTime( - email: event.email, - devicesUuid: selectedDevices, - passwordName: event.passwordName, - invalidTime:event.invalidTime , - effectiveTime:event.effectiveTime + emit(LoadingInitialState()); + await generate7DigitNumber(); + bool res = await AccessMangApi().postOffLineMultipleTime( + email: event.email, + devicesUuid: selectedDevices, + passwordName: event.passwordName, + invalidTime: expirationTimeTimeStamp.toString(), + effectiveTime: effectiveTimeTimeStamp.toString(), ); - // emit(TableLoaded(data)); + 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'); } } - void selectDevice( SelectDeviceEvent event, Emitter emit) { if (selectedDeviceIds.contains(event.deviceId)) { @@ -302,8 +324,6 @@ class VisitorPasswordBloc } else { selectedDeviceIds.add(event.deviceId); } - selectedDevices=selectedDeviceIds; - print(selectedDevices); } String? validate(String? value) { @@ -313,49 +333,165 @@ class VisitorPasswordBloc return null; } - Future generate7DigitNumber() async { - emit(LoadingInitialState()); - passwordController=''; + passwordController = ''; Random random = Random(); int min = 1000000; int max = 9999999; passwordController = (min + random.nextInt(max - min + 1)).toString(); - emit(GeneratePasswordState()); return passwordController; } + String getTimeOnly(DateTime? dateTime) { if (dateTime == null) return ''; return DateFormat('HH:mm').format(dateTime); } - void filterDevices() { final deviceName = deviceNameController.text.toLowerCase(); final deviceId = deviceIdController.text.toLowerCase(); 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 ; + return matchesDeviceName && matchesDeviceId; }).toList(); - // emit(TableLoaded(filteredData)); - - add(UpdateFilteredDevicesEvent(filteredData)); + add(UpdateFilteredDevicesEvent(filteredData)); } + @override - Stream mapEventToState(VisitorPasswordEvent event) async* { + Stream mapEventToState( + VisitorPasswordEvent event) async* { if (event is FetchDevice) { - // Fetching logic... } else if (event is UpdateFilteredDevicesEvent) { yield TableLoaded(event.filteredData); } } - void _onUpdateFilteredDevices(UpdateFilteredDevicesEvent event, Emitter emit) { + void _onUpdateFilteredDevices( + UpdateFilteredDevicesEvent event, Emitter emit) { emit(TableLoaded(event.filteredData)); } + + addDeviceToList(context) { + selectedDevices = selectedDeviceIds; + Navigator.of(context).pop(selectedDevices); + } + + Future selectTimeOfLinePassword( + SelectTimeEvent event, Emitter emit) async { + emit(ChangeTimeState()); + final DateTime? picked = await showDatePicker( + context: event.context, + initialDate: DateTime.now(), + firstDate: DateTime.now(), + lastDate: DateTime(3101), + ); + if (picked != null) { + final TimeOfDay? timePicked = await showHourPicker( + context: event.context, + initialTime: TimeOfDay.now(), + ); + if (timePicked != null) { + final selectedDateTime = DateTime( + picked.year, + picked.month, + picked.day, + timePicked.hour, + 0, + ); + final selectedTimestamp = DateTime( + selectedDateTime.year, + selectedDateTime.month, + selectedDateTime.day, + selectedDateTime.hour, + selectedDateTime.minute, + ).millisecondsSinceEpoch ~/ + 1000; // Divide by 1000 to remove milliseconds + if (event.isEffective) { + if (expirationTimeTimeStamp != null && + selectedTimestamp > expirationTimeTimeStamp!) { + CustomSnackBar.displaySnackBar( + 'Effective Time cannot be later than Expiration Time.'); + } else { + effectiveTime = selectedDateTime + .toString() + .split('.') + .first; // Remove seconds and milliseconds + effectiveTimeTimeStamp = selectedTimestamp; + } + } else { + if (effectiveTimeTimeStamp != null && + selectedTimestamp < effectiveTimeTimeStamp!) { + CustomSnackBar.displaySnackBar( + 'Expiration Time cannot be earlier than Effective Time.'); + } else { + expirationTime = selectedDateTime + .toString() + .split('.') + .first; // Remove seconds and milliseconds + expirationTimeTimeStamp = selectedTimestamp; + } + } + emit(TimeSelectedState()); + } + } + } + + changeTime(ChangeTimeEvent event, Emitter emit) { + if (event.isStartEndTime == true) { + startTime = event.val; + } else { + endTime = event.val; + } + } + + DateTime? convertStringToDateTime(String dateTimeString) { + try { + final DateFormat inputFormat = DateFormat('yyyy-MM-dd HH:mm:ss'); + DateTime dateTime = inputFormat.parse(dateTimeString); + return dateTime; + } catch (e) { + print("Error parsing date: $e"); + return null; + } + } + + String getTimeFromDateTimeString(String dateTimeString) { + DateTime? dateTime = convertStringToDateTime(dateTimeString); + if (dateTime == null) return ''; + return DateFormat('HH:mm').format(dateTime); + } + + String? validateEmail(String? value) { + if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value!)) { + return ''; + } + 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 9990ad9a..9526bf54 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_event.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_event.dart @@ -51,14 +51,16 @@ class ToggleDaySelectionEvent extends VisitorPasswordEvent { class ToggleRepeatEvent extends VisitorPasswordEvent {} class GeneratePasswordEvent extends VisitorPasswordEvent {} -class FetchDevice extends VisitorPasswordEvent {} +class FetchDevice extends VisitorPasswordEvent { +} //online password class OnlineOneTimePasswordEvent extends VisitorPasswordEvent { final String? email; final String? passwordName; + final BuildContext? context; - const OnlineOneTimePasswordEvent({this.email,this.passwordName}); + const OnlineOneTimePasswordEvent({this.email,this.passwordName,this.context}); @override List get props => [email!,passwordName!,]; @@ -68,18 +70,20 @@ class OnlineMultipleTimePasswordEvent extends VisitorPasswordEvent { final String? passwordName; final String? invalidTime; final String? effectiveTime; - const OnlineMultipleTimePasswordEvent({this.email,this.passwordName,this.invalidTime,this.effectiveTime}); + final BuildContext? context; + const OnlineMultipleTimePasswordEvent({this.email,this.passwordName,this.invalidTime,this.effectiveTime,this.context}); @override - List get props => [email!,passwordName!,invalidTime!,effectiveTime!]; + List get props => [email!,passwordName!,invalidTime!,effectiveTime!,context!]; } //offline password 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 { @@ -87,11 +91,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!]; } @@ -115,4 +120,18 @@ class UpdateFilteredDevicesEvent extends VisitorPasswordEvent { final List filteredData; UpdateFilteredDevicesEvent(this.filteredData); +}class SelectTimeEvent extends VisitorPasswordEvent { + final BuildContext context; + final bool isEffective; + const SelectTimeEvent({required this.context,required this.isEffective}); + @override + List get props => [context,isEffective]; +} +class ChangeTimeEvent extends VisitorPasswordEvent { + final dynamic val; + final bool isStartEndTime; + + const ChangeTimeEvent({required this.val,required this.isStartEndTime}); + @override + List get props => [val,isStartEndTime]; } \ No newline at end of file diff --git a/lib/pages/visitor_password/bloc/visitor_password_state.dart b/lib/pages/visitor_password/bloc/visitor_password_state.dart index 374433af..279c9809 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_state.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_state.dart @@ -41,8 +41,9 @@ class IsRepeatState extends VisitorPasswordState { 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 1ab68033..bcf14968 100644 --- a/lib/pages/visitor_password/view/add_device_dialog.dart +++ b/lib/pages/visitor_password/view/add_device_dialog.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_web/pages/common/buttons/search_reset_buttons.dart'; import 'package:syncrow_web/pages/common/custom_table.dart'; import 'package:syncrow_web/pages/common/text_field/custom_web_textfield.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; @@ -14,7 +13,8 @@ import 'package:syncrow_web/utils/constants/const.dart'; import 'package:syncrow_web/utils/style.dart'; class AddDeviceDialog extends StatelessWidget { - const AddDeviceDialog({super.key}); + final List? selectedDeviceIds; + const AddDeviceDialog({super.key, this.selectedDeviceIds}); @override Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; @@ -23,9 +23,23 @@ class AddDeviceDialog extends StatelessWidget { child: BlocBuilder( builder: (BuildContext context, VisitorPasswordState state) { final visitorBloc = BlocProvider.of(context); + if (state is TableLoaded) { + for (var device in selectedDeviceIds!) { + if (selectedDeviceIds!.contains(device)) { + visitorBloc.add(SelectDeviceEvent(device)); + } + } + } + return AlertDialog( backgroundColor: Colors.white, - title: const Text('Add Accessible Device'), + title: Text( + 'Add Accessible Device', + style: Theme.of(context).textTheme.headlineLarge!.copyWith( + fontWeight: FontWeight.w400, + fontSize: 24, + color: Colors.black), + ), content: Container( height: MediaQuery.of(context).size.height / 1.7, width: MediaQuery.of(context).size.width / 2, @@ -49,21 +63,34 @@ class AddDeviceDialog extends StatelessWidget { width: 15, ), ), - SizedBox( + const SizedBox( width: 10, ), - Text('Only online accessible devices can be added'), + Text( + 'Only online accessible devices can be added', + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + fontWeight: FontWeight.w400, + fontSize: 12, + color: ColorsManager.grayColor), + ), ], )), 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, @@ -73,7 +100,7 @@ class AddDeviceDialog extends StatelessWidget { ), const SizedBox(width: 10), Expanded( - flex: 2, + flex: 4, child: CustomWebTextField( controller: visitorBloc.deviceIdController, isRequired: true, @@ -83,7 +110,7 @@ class AddDeviceDialog extends StatelessWidget { ), const SizedBox(width: 10), Expanded( - flex: 2, + flex: 4, child: CustomWebTextField( controller: visitorBloc.unitNameController, isRequired: true, @@ -92,24 +119,58 @@ class AddDeviceDialog extends StatelessWidget { ), ), const SizedBox(width: 10), - SearchResetButtons( - onSearch: () { - visitorBloc.filterDevices(); - }, - onReset: () { - visitorBloc.deviceNameController.clear(); - visitorBloc.deviceIdController.clear(); - visitorBloc.unitNameController.clear(); - visitorBloc.add(FetchDevice()); - }, + 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), + 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( + initialSelectedIds: selectedDeviceIds, cellDecoration: containerDecoration, + isEmpty: visitorBloc.data.isEmpty, selectAll: (p0) { visitorBloc.selectedDeviceIds.clear(); for (var item in state.data) { @@ -167,7 +228,7 @@ class AddDeviceDialog extends StatelessWidget { width: size.width * 0.2, child: DefaultButton( onPressed: () { - Navigator.of(context).pop(); // Close the dialog + visitorBloc.addDeviceToList(context); }, borderRadius: 8, child: Text('Ok'), diff --git a/lib/pages/visitor_password/view/repeat_widget.dart b/lib/pages/visitor_password/view/repeat_widget.dart index ecbd2b89..ae37b1e3 100644 --- a/lib/pages/visitor_password/view/repeat_widget.dart +++ b/lib/pages/visitor_password/view/repeat_widget.dart @@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.da import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; class RepeatWidget extends StatelessWidget { const RepeatWidget({ @@ -14,34 +15,36 @@ class RepeatWidget extends StatelessWidget { @override Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; - return BlocBuilder( builder: (context, state) { - final smartDoorBloc = BlocProvider.of(context); + final visitorBloc = BlocProvider.of(context); return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - width: size.width * 0.8, - height: size.height * 0.06, // Adjust height as needed - child: ListView( - scrollDirection: Axis.horizontal, - children: smartDoorBloc.days.map((day) { + // Wrap the Row in a SingleChildScrollView to handle overflow + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: visitorBloc.days.map((day) { return Container( - width: size.width* 0.09, + width: 70, // Adjust width as needed + margin: EdgeInsets.all(5), child: CheckboxListTile( + contentPadding: EdgeInsets.zero, title: Text( day['day']!, style: TextStyle( - fontSize: 18, - color: smartDoorBloc.selectedDays.contains(day['key']) + fontSize: 10, + color: visitorBloc.selectedDays.contains(day['key']) ? Colors.black - : ColorsManager.grayColor, + : ColorsManager.blackColor, ), ), - value: smartDoorBloc.selectedDays.contains(day['key']), + value: visitorBloc.selectedDays.contains(day['key']), onChanged: (bool? value) { if (value != null) { - smartDoorBloc.add(ToggleDaySelectionEvent(key: day['key']!)); + visitorBloc.add(ToggleDaySelectionEvent(key: day['key']!)); } }, ), @@ -52,30 +55,32 @@ class RepeatWidget extends StatelessWidget { Padding( padding: const EdgeInsets.all(8.0), child: DateTimeWebWidget( + icon: Assets.timeIcon, isRequired: false, title: '', size: size, endTime: () { - smartDoorBloc.add(SelectTimeVisitorPassword( - isRepeat: true, - context: context, isStart: false - )); + visitorBloc.add(SelectTimeEvent( + context: context, + isEffective: false)); + Future.delayed(const Duration(milliseconds: 500), () { + visitorBloc.add(ChangeTimeEvent(val: visitorBloc.endTime, isStartEndTime: true)); + }); }, startTime: () { - smartDoorBloc.add(SelectTimeVisitorPassword( - isRepeat: true, - context: context, isStart: true - )); + Future.delayed(const Duration(milliseconds: 500), () { + visitorBloc.add(ChangeTimeEvent(val: visitorBloc.endTime, isStartEndTime: true)); + }); + visitorBloc.add(SelectTimeEvent(context: context, isEffective: true)); }, - firstString: smartDoorBloc.repeatStartTime.toString(), - secondString: smartDoorBloc.repeatEndTime.toString(), + firstString: visitorBloc.effectiveTime, + secondString: visitorBloc.expirationTime, ), ), - const SizedBox(height: 20), - ], ); - }); + } + ); } } diff --git a/lib/pages/visitor_password/view/visitor_password_dialog.dart b/lib/pages/visitor_password/view/visitor_password_dialog.dart index 949fc76d..d7999177 100644 --- a/lib/pages/visitor_password/view/visitor_password_dialog.dart +++ b/lib/pages/visitor_password/view/visitor_password_dialog.dart @@ -1,15 +1,17 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/common/text_field/custom_web_textfield.dart'; -import 'package:syncrow_web/pages/common/date_time_widget.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; -import 'package:syncrow_web/pages/common/info_dialog.dart'; +import 'package:syncrow_web/pages/common/date_time_widget.dart'; +import 'package:syncrow_web/pages/common/text_field/custom_web_textfield.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'; import 'package:syncrow_web/pages/visitor_password/view/add_device_dialog.dart'; import 'package:syncrow_web/pages/visitor_password/view/repeat_widget.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/style.dart'; class VisitorPasswordDialog extends StatelessWidget { @@ -18,370 +20,623 @@ 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: const Text('Create visitor password'), - content: SingleChildScrollView( - child: Form( - key: visitorBloc.forgetFormKey, - child: Padding( - padding: const EdgeInsets.all(10.0), - child: ListBody( - children: [ - Container( - child: Row( - children: [ - Expanded( - flex: 2, - child: CustomWebTextField( - validator: visitorBloc.validate, - controller: visitorBloc.userNameController, - isRequired: true, - textFieldName: 'Name', - description: '', + 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: [ + 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 Spacer(), - Expanded( - flex: 2, - child: CustomWebTextField( - validator: visitorBloc.validate, - controller: visitorBloc.emailController, - isRequired: true, - textFieldName: 'Email Address', - description: 'The password will be sent to the visitor’s email address.', + const SizedBox( + height: 15, ), - ), - const Spacer(), - ], + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + Text('Access Type', style: text), + ], + ), + Row( + children: [ + Flexible( + child: RadioListTile( + contentPadding: EdgeInsets.zero, + 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)); + } + }, + ), + ), + Flexible( + child: RadioListTile( + contentPadding: EdgeInsets.zero, + 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)); + } + }, + ), + ), + Flexible( + 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 = ''; + } + }, + ), + ), + ], + ), + 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' + ? 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: [ + Flexible( + child: RadioListTile( + contentPadding: EdgeInsets.zero, + 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< + VisitorPasswordBloc>() + .add( + SelectUsageFrequency( + value)); + } + }, + ), + ), + Flexible( + child: RadioListTile( + contentPadding: EdgeInsets.zero, + title: Text('Periodic', + style: text), + value: 'Periodic', + groupValue: (state + is UsageFrequencySelected) + ? state.selectedFrequency + : visitorBloc + .usageFrequencySelected, + onChanged: (String? value) { + if (value != null) { + context + .read< + VisitorPasswordBloc>() + .add( + SelectUsageFrequency( + value)); + } + }, + ), + ), + ], + ), + Text( + 'Within the validity period, each device can be unlocked only once.', + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: + ColorsManager.grayColor, + fontSize: 9), + ) + ], + ), + const SizedBox( + height: 20, + ), + if ((visitorBloc.usageFrequencySelected != + 'One-Time' || + visitorBloc.accessTypeSelected != + 'Offline Password') && + (visitorBloc.usageFrequencySelected != '')) + DateTimeWebWidget( + isRequired: true, + title: 'Access Period', + size: size, + endTime: () { + visitorBloc.add(SelectTimeVisitorPassword( + context: context, + isStart: false, + isRepeat: false)); + }, + startTime: () { + visitorBloc.add(SelectTimeVisitorPassword( + context: context, + isStart: true, + isRepeat: false)); + }, + firstString: + visitorBloc.startTimeAccess.toString(), + secondString: + visitorBloc.endTimeAccess.toString(), + icon: Assets.calendarIcon), + 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 / 9, + child: DefaultButton( + onPressed: () { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return AddDeviceDialog( + selectedDeviceIds: + visitorBloc.selectedDevices, + ); + }, + ).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), + ), + ), + ), + ], + ), + ], + ), ), ), - SizedBox(height: 20,), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - '* ', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Colors.red), - ), - const Text('Access Type'), - ], - ), - Row( - children: [ - SizedBox( - width: size.width * 0.15, - child: RadioListTile( - title: const Text('Online Password'), - 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: const Text('Offline Password'), - value: 'Offline 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: const Text('Dynamic Password'), - 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 Text('Only currently online devices can be selected. It is recommended to use when the device network is stable, and the system randomly generates a digital password'), - const SizedBox(height: 20,) - ], - ), - visitorBloc.accessTypeSelected=='Dynamic Password' ? - SizedBox(): - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - '* ', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Colors.red), - ), - const Text('Usage Frequency'), - ], - ), - Row( - children: [ - SizedBox( - width: 200, - child: RadioListTile( - title: const Text('One-Time'), - value: 'One-Time', - groupValue: (state is UsageFrequencySelected) - ? state.selectedFrequency - : visitorBloc.usageFrequencySelected, - onChanged: (String? value) { - if (value != null) { - print(value); - - context.read().add(SelectUsageFrequency(value)); - } - }, - ), - ), - SizedBox( - width: 200, - child: RadioListTile( - title: const Text('Periodic'), - value: 'Periodic', - groupValue: (state is UsageFrequencySelected) - ? state.selectedFrequency - : visitorBloc.usageFrequencySelected, - onChanged: (String? value) { - if (value != null) { - context.read().add(SelectUsageFrequency(value)); - } - }, - ), - ), - ], - ), - - const Text('Within the validity period, each device can be unlocked only once.') - ], - ), - const SizedBox(height: 20,), - if((visitorBloc.usageFrequencySelected!='One-Time'||visitorBloc.accessTypeSelected!='Offline Password')&&(visitorBloc.usageFrequencySelected!='')) - DateTimeWebWidget( - - isRequired: true, - title: 'Access Period', - size: size, - endTime: () { - visitorBloc.add(SelectTimeVisitorPassword( - context: context, isStart: false,isRepeat:false)); - }, - startTime: () { - visitorBloc.add(SelectTimeVisitorPassword( - context: context, isStart: true,isRepeat:false)); - }, - firstString: visitorBloc.startTime, - secondString: visitorBloc.endTime, - ), - - const SizedBox(height: 20,), - - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - '* ', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Colors.red), - ), - const Text('Access Devices'), - ], - ), - const Text('Within the validity period, each device can be unlocked only once.'), - const SizedBox(height: 20,), - if(visitorBloc.usageFrequencySelected=='Periodic'&&visitorBloc.accessTypeSelected=='Online Password') - SizedBox( - width: 100, - child: ListTile( - contentPadding: EdgeInsets.zero, - leading: const Text('Repeat'), - trailing: Transform.scale( - scale: .8, - child: CupertinoSwitch( - value: visitorBloc.repeat, - onChanged: (value) { - visitorBloc.add(ToggleRepeatEvent()); - }, - applyTheme: true, - ), - ), - ), - ), - if(visitorBloc.usageFrequencySelected=='Periodic'&&visitorBloc.accessTypeSelected=='Online Password') - isRepeat ? const RepeatWidget() : const SizedBox(), - Container( - decoration: containerDecoration, - width: size.width * 0.1, - child: DefaultButton( - onPressed: () { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return const AddDeviceDialog(); - }, - ); - }, - borderRadius: 8, - child: Text('+ Add Device'), - ), - ), - ], - ), - - ], + ), + actionsAlignment: MainAxisAlignment.center, + actions: [ + Container( + decoration: containerDecoration, + width: size.width * 0.2, + child: DefaultButton( + borderRadius: 8, + onPressed: () { + Navigator.of(context).pop(true); + }, + 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.2, - child: DefaultButton( - borderRadius: 8, - onPressed: () { - Navigator.of(context).pop(); // Close the dialog - }, - backgroundColor: Colors.white, - child: Text( - 'Cancel', - style: Theme.of(context).textTheme.bodyMedium!, + 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), + ), ), ), - ), - 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(); // Close the dialog - }, - backgroundColor: Colors.white, - child: Text( - 'Cancel', - style: Theme.of(context).textTheme.bodyMedium!, - ), - ), - ), - 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( - passwordName:visitorBloc.userNameController.text , - email: visitorBloc.emailController.text - ) - ); - } - else if(visitorBloc.usageFrequencySelected=='Periodic'&&visitorBloc.accessTypeSelected=='Online Password') { - visitorBloc.add(OnlineMultipleTimePasswordEvent( - passwordName:visitorBloc.userNameController.text , - email: visitorBloc.emailController.text, - effectiveTime:visitorBloc.effectiveTimeTimeStamp.toString() , - invalidTime:visitorBloc.expirationTimeTimeStamp.toString() - ) - ); - } - else if(visitorBloc.usageFrequencySelected=='One-Time'&&visitorBloc.accessTypeSelected=='Offline Password') { - visitorBloc.add(OfflineOneTimePasswordEvent( - passwordName:visitorBloc.userNameController.text , - email: visitorBloc.emailController.text, - ) - ); - } - else if(visitorBloc.usageFrequencySelected=='Periodic'&&visitorBloc.accessTypeSelected=='Offline Password') { - visitorBloc.add(OfflineMultipleTimePasswordEvent( - passwordName:visitorBloc.userNameController.text , - email: visitorBloc.emailController.text, - effectiveTime:visitorBloc.effectiveTimeTimeStamp.toString() , - invalidTime:visitorBloc.expirationTimeTimeStamp.toString() - ) - ); - } - }, - child: const Text( - 'Ok', - ), - ), - ), - ],); - }, - ); - } - }, - borderRadius: 8, - child: Text('Ok'), - ), - ), - ], - ); - }, + ], + ); + }, + ), ), ); } + + 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.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: () { + 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 38730553..6a2cf40d 100644 --- a/lib/services/access_mang_api.dart +++ b/lib/services/access_mang_api.dart @@ -1,12 +1,12 @@ 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 { @@ -16,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(); @@ -37,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(); @@ -51,34 +49,38 @@ class AccessMangApi{ } } - Future postOnlineOneTime({String? email,String? passwordName,List? devicesUuid}) async { + Future postOnlineOneTime({ + String? email, + String? passwordName, + String? password, + String? effectiveTime, + String? invalidTime, + List? devicesUuid}) async { try { - - print('postOfflineOneTime List: ${ - { - "email": email, - "passwordName": passwordName, - "devicesUuid": devicesUuid - } - }'); - final response = await HTTPService().post( path: ApiEndpoints.sendOnlineOneTime, body: jsonEncode({ "email": email, "passwordName": passwordName, - "devicesUuid": devicesUuid + "password": password, + "devicesUuid": devicesUuid, + "effectiveTime":effectiveTime , + "invalidTime": invalidTime }), showServerMessage: true, expectedResponseModel: (json) { - List jsonData = json; - print('postOfflineOneTime List: $json'); + if(json['statusCode'].toString()=='201'){ + return true; + }else{ + return false; + } }, ); return response; - } catch (e) { - debugPrint('Error fetching $e'); - return []; + } on DioException catch (e) { + debugPrint('Error: ${e.message}'); + debugPrint('Error fetching ${e.response!.statusMessage}'); + return false; } } @@ -99,25 +101,26 @@ class AccessMangApi{ "effectiveTime": effectiveTime, "invalidTime": invalidTime, }; - print('createPassword =${scheduleList![0].workingDay}'); if (scheduleList != null) { body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList(); } - print('createPassword =$body'); - final response = await HTTPService().post( path: ApiEndpoints.sendOnlineMultipleTime, body: jsonEncode(body), showServerMessage: true, expectedResponseModel: (json) { - List jsonData = json; - print('postOfflineOneTime List: $json'); + if(json['data']['successOperations'][0]['success'].toString()=='true'){ + return true; + }else{ + return false; + } }, ); return response; - } catch (e) { - debugPrint('Error fetching $e'); - return []; + } on DioException catch (e){ + debugPrint('Error fetching ${e.type.name}'); + debugPrint('Error fetching ${e.response!.statusMessage}'); + return false; } } @@ -125,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({ @@ -142,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) { @@ -163,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({ @@ -182,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/utils/color_manager.dart b/lib/utils/color_manager.dart index f0d64814..17ee82a9 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -26,7 +26,7 @@ abstract class ColorsManager { static const Color blackColor = Color(0xFF000000); static const Color lightGreen = Color(0xFF00FF0A); static const Color grayColor = Color(0xFF999999); - static const Color red = Colors.red; + static const Color red = Color(0xFFFF0000); static const Color graysColor = Color(0xffEBEBEB); static const Color textGray = Color(0xffD5D5D5); static const Color btnColor = Color(0xFF00008B); diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 2c2a6e43..f4a2859e 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -25,4 +25,6 @@ class Assets { static const String assetIcon = "assets/images/asset_icon.svg"; static const String calendarIcon = "assets/images/calendar_icon.svg"; static const String deviceNoteIcon = "assets/images/device_note.svg"; + static const String timeIcon = "assets/images/time_icon.svg"; + static const String emptyTable = "assets/images/empty_table.svg"; } diff --git a/lib/utils/constants/const.dart b/lib/utils/constants/const.dart index 7afa399d..77beff69 100644 --- a/lib/utils/constants/const.dart +++ b/lib/utils/constants/const.dart @@ -1,6 +1,4 @@ - - enum AccessType { onlineOnetime, onlineMultiple, @@ -43,8 +41,6 @@ extension AccessTypeExtension on AccessType { - - enum DeviseStatus { online, offline, @@ -74,6 +70,40 @@ extension OnlineTypeExtension on DeviseStatus { } +enum AccessStatus { + expired , + effective , + toBeEffective, +} + +extension AccessStatusExtension on AccessStatus { + String get value { + switch (this) { + case AccessStatus.expired: + return "Expired"; + case AccessStatus.effective: + return "Effective" ; + case AccessStatus.toBeEffective: + return "To be effective"; + + } + } + + static AccessStatus fromString(String value) { + switch (value) { + case "EXPIRED" : + return AccessStatus.expired; + case "EFFECTIVE" : + return AccessStatus.effective; + case "TO_BE_EFFECTIVE": + return AccessStatus.toBeEffective; + default: + throw ArgumentError("Invalid access type: $value"); + } + } +} + + 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(