diff --git a/assets/dome.json b/assets/dome.json index 39e6ee7c..91186608 100644 --- a/assets/dome.json +++ b/assets/dome.json @@ -2,7 +2,8 @@ { "accessUser": "Ali Doe", "accessType": "Admin", - "accessPeriod": "2023-08-01", + "startTime": "2023-08-01", + "endTime": "2023-08-02", "accessibleDevice": "Smart Door", "authorizationSource": "System", "authorizer": "Jane Smith", @@ -12,8 +13,9 @@ }, { "accessUser": "oamr Doe", "accessType": "Admin", - "accessPeriod": "2023-08-01", - "accessibleDevice": "Smart Door", + "startTime": "2023-08-01", + "endTime": "2023-08-05", + "accessibleDevice": "Smart Door", "authorizationSource": "System", "authorizer": "Jane Smith", "authorizationTime": "2023-08-01 10:00 AM", @@ -22,7 +24,8 @@ }, { "accessUser": "John Doe", "accessType": "Admin", - "accessPeriod": "2023-08-01", + "startTime": "2023-08-01", + "endTime": "2023-08-10", "accessibleDevice": "Smart Door", "authorizationSource": "System", "authorizer": "Jane Smith", @@ -34,7 +37,8 @@ { "accessUser": "John Doe", "accessType": "Admin", - "accessPeriod": "2023-08-01", + "startTime": "2023-08-01", + "endTime": "2023-10-10", "accessibleDevice": "Smart Door", "authorizationSource": "System", "authorizer": "Jane Smith", @@ -45,7 +49,8 @@ { "accessUser": "John Doe", "accessType": "Admin", - "accessPeriod": "2023-08-01", + "startTime": "2023-03-01", + "endTime": "2023-05-10", "accessibleDevice": "Smart Door", "authorizationSource": "System", "authorizer": "Jane Smith", @@ -56,7 +61,8 @@ { "accessUser": "John Doe", "accessType": "Admin", - "accessPeriod": "2023-08-01 ", + "startTime": "2023-07-01", + "endTime": "2023-08-10", "accessibleDevice": "Smart Door", "authorizationSource": "System", "authorizer": "Jane Smith", @@ -66,7 +72,8 @@ }, { "accessUser": "John Doe", "accessType": "Admin", - "accessPeriod": "2023-08-01 ", + "startTime": "2023-01-01", + "endTime": "2023-09-05", "accessibleDevice": "Smart Door", "authorizationSource": "System", "authorizer": "Jane Smith", @@ -77,7 +84,8 @@ { "accessUser": "Alice Johnson", "accessType": "User", - "accessPeriod": "2023-08-01 to 2023-08-31", + "startTime": "2023-08-01", + "endTime": "2023-08-10", "accessibleDevice": "Smart Lock", "authorizationSource": "Admin", "authorizer": "John Doe", diff --git a/lib/main.dart b/lib/main.dart index 615851eb..ab12a5fa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,10 +1,12 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/view/access_management.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/auth/view/login_page.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/view/home_page.dart'; +import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart'; import 'package:syncrow_web/services/locator.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -56,7 +58,8 @@ class MyApp extends StatelessWidget { colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), // Set up color scheme useMaterial3: true, // Enable Material 3 ), - home: isLoggedIn == 'Success' ? const HomePage() : const LoginPage(), + home: VisitorPasswordDialog() + // 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 8938fc36..e8be33f4 100644 --- a/lib/pages/access_management/bloc/access_bloc.dart +++ b/lib/pages/access_management/bloc/access_bloc.dart @@ -1,26 +1,181 @@ +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_event.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_state.dart'; -import 'package:syncrow_web/pages/access_management/model/access_manag_model.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/snack_bar.dart'; class AccessBloc extends Bloc { AccessBloc() : super((AccessInitial())) { on(_onFetchTableData); + on(selectFilterTap); + on(selectTime); + on(_filterData); + on(resetSearch); } String startTime = 'Start Time'; String endTime = 'End Time'; + int? effectiveTimeTimeStamp; + int? expirationTimeTimeStamp; + TextEditingController passwordName= TextEditingController(); + List filteredData = []; // To store filtered data + List data=[]; + Future _onFetchTableData( FetchTableData event, Emitter emit) async { try { emit(AccessLoaded()); - List data = await AccessMangApi().fetchInfo(); - print('objectwww888888${data[0].accessPeriod}'); - - emit(TableLoaded(data)); + data = await AccessMangApi().fetchVisitorPassword(); + emit(TableLoaded(data)); } catch (e) { emit(FailedState(e.toString())); } } + + int selectedIndex = 0; + + final List tabs = [ + 'All', + 'To Be Effective (0)', + 'Effective (0)', + 'Expired' + ]; + + + Future selectFilterTap(TabChangedEvent event, Emitter emit) async { + try { + emit(AccessLoaded()); + selectedIndex= event.selectedIndex; + emit(AccessInitial()); + emit(TableLoaded(data)); + + } catch (e) { + emit(FailedState( e.toString())); + return; + } + } + + + Future selectTime(SelectTime event, Emitter emit) async { + final DateTime? picked = await showDatePicker( + context: event.context, + initialDate: DateTime.now(), + firstDate: DateTime(2015, 8), + lastDate: DateTime(2101), + ); + if (picked != null) { + final TimeOfDay? timePicked = await showTimePicker( + context: event.context, + initialTime: TimeOfDay.now(), + + builder: (context, child) { + return Theme( + data: ThemeData.light().copyWith( + colorScheme: const ColorScheme.light( + primary: ColorsManager.primaryColor, + onSurface: Colors.black, + ), + buttonTheme: const ButtonThemeData( + colorScheme: ColorScheme.light( + primary: Colors.green, + ), + ), + ), + child: child!, + ); + }, + ); + if (timePicked != null) { + final selectedDateTime = DateTime( + picked.year, + picked.month, + picked.day, + 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 { + startTime = 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 { + endTime = selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds + expirationTimeTimeStamp = selectedTimestamp; + } + } + } + } + emit(AccessInitial()); + emit(TableLoaded(data)); + } + + 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!); + 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) { + matchesCriteria = false; + } else { + final bool matchesStartTime = effectiveTime >= event.startTime!; + final bool matchesEndTime = invalidTime <= event.endTime!; + if (!matchesStartTime || !matchesEndTime) { + matchesCriteria = false; + } + } + } + return matchesCriteria; + }).toList(); + print('Filtered data: $filteredData'); // Print to debug filtered data + emit(TableLoaded(filteredData)); + } catch (e) { + print('Error occurred during filtering: $e'); + } + } + // ResetSearch + resetSearch(ResetSearch event, Emitter emit) async{ + emit(AccessLoaded()); + startTime = 'Start Time'; + endTime = 'End Time'; + passwordName.clear(); + add(FetchTableData()); + + } + + + DateTime timestampToDateTime(dynamic timestamp) { + return DateTime.fromMillisecondsSinceEpoch(int.parse(timestamp) * 1000); + } + + + } diff --git a/lib/pages/access_management/bloc/access_event.dart b/lib/pages/access_management/bloc/access_event.dart index 60512387..3a64a11a 100644 --- a/lib/pages/access_management/bloc/access_event.dart +++ b/lib/pages/access_management/bloc/access_event.dart @@ -1,5 +1,6 @@ import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; abstract class AccessEvent extends Equatable { const AccessEvent(); @@ -8,4 +9,32 @@ abstract class AccessEvent extends Equatable { List get props => []; } class FetchTableData extends AccessEvent {} +class ResetSearch extends AccessEvent {} +class TabChangedEvent extends AccessEvent { + final int selectedIndex; + + const TabChangedEvent(this.selectedIndex); +} + + +class SelectTime extends AccessEvent { + final BuildContext context; + final bool isStart; + const SelectTime({required this.context,required this.isStart}); + @override + List get props => [context,isStart]; +} + + +class FilterDataEvent extends AccessEvent { + final String? passwordName; + final int? startTime; + final int? endTime; + + const FilterDataEvent({ + this.passwordName, + this.startTime, + this.endTime, + }); +} diff --git a/lib/pages/access_management/bloc/access_state.dart b/lib/pages/access_management/bloc/access_state.dart index 94bbc90c..11253d1f 100644 --- a/lib/pages/access_management/bloc/access_state.dart +++ b/lib/pages/access_management/bloc/access_state.dart @@ -1,5 +1,5 @@ import 'package:equatable/equatable.dart'; -import 'package:syncrow_web/pages/access_management/model/access_manag_model.dart'; +import 'package:syncrow_web/pages/access_management/model/password_model.dart'; abstract class AccessState extends Equatable { const AccessState(); @@ -21,7 +21,7 @@ class FailedState extends AccessState { } class TableLoaded extends AccessState { - final List data; + final List data; const TableLoaded(this.data); @@ -29,3 +29,12 @@ class TableLoaded extends AccessState { List get props => [data]; } +class TabState extends AccessState { + final int selectedIndex; + + const TabState({required this.selectedIndex}); +} + +class ChangeTimeState extends AccessState {} + +class TimeSelectedState extends AccessState {} diff --git a/lib/pages/access_management/model/access_manag_model.dart b/lib/pages/access_management/model/access_manag_model.dart deleted file mode 100644 index ef468c8e..00000000 --- a/lib/pages/access_management/model/access_manag_model.dart +++ /dev/null @@ -1,37 +0,0 @@ -class AccessManagModel { - final String accessUser; - final String accessType; - final String accessPeriod; - final String accessibleDevice; - final String authorizationSource; - final String authorizer; - final String authorizationTime; - final String accessStatus; - final String actions; - - AccessManagModel({ - required this.accessUser, - required this.accessType, - required this.accessPeriod, - required this.accessibleDevice, - required this.authorizationSource, - required this.authorizer, - required this.authorizationTime, - required this.accessStatus, - required this.actions, - }); - - factory AccessManagModel.fromJson(Map json) { - return AccessManagModel( - accessUser: json['accessUser'], - accessType: json['accessType'], - accessPeriod: json['accessPeriod'], - accessibleDevice: json['accessibleDevice'], - authorizationSource: json['authorizationSource'], - authorizer: json['authorizer'], - authorizationTime: json['authorizationTime'], - accessStatus: json['accessStatus'], - actions: json['actions'], - ); - } -} diff --git a/lib/pages/access_management/model/password_model.dart b/lib/pages/access_management/model/password_model.dart new file mode 100644 index 00000000..0377bfb7 --- /dev/null +++ b/lib/pages/access_management/model/password_model.dart @@ -0,0 +1,54 @@ +class PasswordModel { + final dynamic passwordId; + final dynamic invalidTime; + final dynamic effectiveTime; + final dynamic passwordCreated; + final dynamic createdTime; + final dynamic passwodName; // New field + final dynamic passwordStatus; + final dynamic passwordType; + final dynamic deviceUuid; + + PasswordModel({ + this.passwordId, + this.invalidTime, + this.effectiveTime, + this.passwordCreated, + this.createdTime, + this.passwodName, // New field + this.passwordStatus, + this.passwordType, + this.deviceUuid, + }); + + factory PasswordModel.fromJson(Map json) { + return PasswordModel( + passwordId: json['passwordId'], + invalidTime: json['invalidTime'], + effectiveTime: json['effectiveTime'], + passwordCreated: json['passwordCreated'], + createdTime: json['createdTime'], + passwodName: json['passwodName']??'No name', // New field + passwordStatus: json['passwordStatus'], + passwordType: json['passwordType'], + deviceUuid: json['deviceUuid'], + ); + } + + Map toJson() { + return { + 'passwordId': passwordId, + 'invalidTime': invalidTime, + 'effectiveTime': effectiveTime, + 'passwordCreated': passwordCreated, + 'createdTime': createdTime, + 'passwodName': passwodName, // 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 6893b205..943849d8 100644 --- a/lib/pages/access_management/view/access_management.dart +++ b/lib/pages/access_management/view/access_management.dart @@ -5,9 +5,9 @@ import 'package:syncrow_web/pages/access_management/bloc/access_bloc.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_event.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_state.dart'; import 'package:syncrow_web/pages/common/default_button.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/snack_bar.dart'; import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; @@ -35,16 +35,15 @@ class AccessManagementPage extends StatelessWidget { .copyWith(color: Colors.white), ), ], - scaffoldBody: BlocProvider( - create: (BuildContext context) => AccessBloc()..add(FetchTableData() ), - child: BlocConsumer( - listener: (context, state) { + scaffoldBody: BlocProvider(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, @@ -53,233 +52,294 @@ class AccessManagementPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - width: size.width * 0.3, height: size.height * 0.05, + width:size.width * 0.26 , decoration: containerDecoration, - child: const Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text('All'), - Text('To Be Effective (0)'), - Text('Effective (0)'), - Text('Expired'), - ], + child: Center( + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: BlocProvider.of(context).tabs.length, + 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, ), Wrap( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text('User Name'), - Container( - width: size.width*0.15, - decoration: containerDecoration, - child: TextFormField( - decoration: textBoxDecoration()! - .copyWith(hintText: 'Please enter'), - )), - ], - ), - const SizedBox(width: 15,), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Text('Email Address'), - Container( - width: size.width*0.15, - decoration: containerDecoration, - child: TextFormField( - decoration: textBoxDecoration()! - .copyWith(hintText: 'Please enter'), - )), - ], - ), - const SizedBox(width: 15,), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Text('Access Time'), - Container( - width: size.width*0.18, - padding: EdgeInsets.all(10), - decoration: containerDecoration, - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - InkWell(child: Text(BlocProvider.of(context).startTime)), - const Icon(Icons.arrow_right_alt), - InkWell(child: Text(BlocProvider.of(context).endTime)), - SvgPicture.asset( - Assets.calendarIcon, - ), - ], - ), - ], - )), - ], - ), - const SizedBox(width: 15,), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Text('Authorization Source'), - Container( - width: size.width*0.18, - decoration: containerDecoration, - child: TextFormField( - decoration: textBoxDecoration(), - )), - ], - ), - const SizedBox(width: 15,), - SizedBox( - width: size.width*0.06, - child: Column( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, children: [ - Text(''), + const Text('Password Name'), Container( + width: size.width * 0.15, decoration: containerDecoration, - - child: DefaultButton(child: Text('Search'),borderRadius: 9)), + child: TextFormField( + controller: accessBloc.passwordName, + style: TextStyle(color: Colors.black), + decoration: textBoxDecoration()! + .copyWith(hintText: 'Please enter'), + )), ], ), - ), - const SizedBox(width: 10,), - SizedBox( - width: size.width*0.06, - child: Column( + const SizedBox( + width: 15, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, children: [ - Text(''), + const Text('Access Time'), Container( - decoration: containerDecoration, - child: DefaultButton( - 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: const DefaultButton( - borderRadius: 8, - child: 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? - 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 , + width: size.width * 0.25, + padding: EdgeInsets.all(10), + decoration: containerDecoration, child: Column( children: [ - - Container( - color: ColorsManager.boxColor, - child: Row( - children: [ - _buildTableHeaderCell('Access User'), - _buildTableHeaderCell('Access Type'), - _buildTableHeaderCell('Access Period'), - _buildTableHeaderCell('Accessible Device'), - _buildTableHeaderCell('Authorization Source'), - _buildTableHeaderCell('Authorizer'), - _buildTableHeaderCell('Authorization Time'), - _buildTableHeaderCell('Access Status'), - _buildTableHeaderCell('Actions'), - ], - ), - ), - - 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.accessUser), - _buildTableCell(item.accessType), - _buildTableCell(item.accessPeriod), - _buildTableCell(item.accessibleDevice), - _buildTableCell(item.authorizationSource), - _buildTableCell(item.authorizer), - _buildTableCell(item.authorizationTime), - _buildTableCell(item.accessStatus), - _buildTableCell(item.actions), - ], - ); - }).toList(), - ), - ], + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + InkWell( + onTap: () { + accessBloc.add(SelectTime(context: context, isStart: true)); + }, + child: Text(BlocProvider.of(context).startTime) ), - ), + const Icon(Icons.arrow_right_alt), + InkWell( + onTap: () { + accessBloc.add(SelectTime(context: context, isStart: false)); + }, + child: Text(BlocProvider.of(context).endTime)), + SvgPicture.asset( + Assets.calendarIcon, + ), + ], ), ], + )), + ], + ), + const SizedBox( + width: 15, + ), + + SizedBox( + width: size.width * 0.06, + child: Column( + 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), + ), ), ), ], ), ), - ):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: 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 + ? TableWidget(size, state,accessBloc) + : 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(), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ); + } } Widget _buildTableHeaderCell(String title) { return Expanded( child: Container( decoration: const BoxDecoration( - border: Border.symmetric(vertical: BorderSide(color: ColorsManager.boxDivider)) - ), + border: Border.symmetric( + vertical: BorderSide(color: ColorsManager.boxDivider))), alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.all(8.0), @@ -292,12 +352,21 @@ Widget _buildTableHeaderCell(String title) { Widget _buildTableCell(String content) { return Expanded( child: Container( + height: 80, padding: const EdgeInsets.all(20.0), - decoration: const BoxDecoration( - border: Border.symmetric(horizontal: BorderSide(color: ColorsManager.boxDivider)) + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( // <--- right side + color: ColorsManager.boxDivider, + width: 1.0, + ), + ) ), alignment: Alignment.centerLeft, - child: Text(content,style: TextStyle(color: Colors.black,fontSize: 12),), + child: Text( + content, + style: TextStyle(color: Colors.black, fontSize: 12), + ), ), ); } diff --git a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart new file mode 100644 index 00000000..41510a8d --- /dev/null +++ b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart @@ -0,0 +1,13 @@ +import 'package:flutter_bloc/flutter_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'; + +// Define the BLoC +class VisitorPasswordBloc extends Bloc { + VisitorPasswordBloc() : super(VisitorPasswordInitial()) { + on((event, emit) { + // Handle the event and emit the new state + emit(PasswordTypeSelected(event.type)); + }); + } +} diff --git a/lib/pages/visitor_password/bloc/visitor_password_event.dart b/lib/pages/visitor_password/bloc/visitor_password_event.dart new file mode 100644 index 00000000..37083997 --- /dev/null +++ b/lib/pages/visitor_password/bloc/visitor_password_event.dart @@ -0,0 +1,23 @@ + + + + +import 'package:equatable/equatable.dart'; + +abstract class VisitorPasswordEvent extends Equatable { + const VisitorPasswordEvent( + + ); + + @override + List get props => []; +} + +class SelectPasswordType extends VisitorPasswordEvent { + final String type; + + const SelectPasswordType(this.type); + + @override + List get props => [type]; +} diff --git a/lib/pages/visitor_password/bloc/visitor_password_state.dart b/lib/pages/visitor_password/bloc/visitor_password_state.dart new file mode 100644 index 00000000..c13d27db --- /dev/null +++ b/lib/pages/visitor_password/bloc/visitor_password_state.dart @@ -0,0 +1,23 @@ + + +import 'package:equatable/equatable.dart'; + +abstract class VisitorPasswordState extends Equatable { + const VisitorPasswordState(); + + @override + List get props => []; +} + +class VisitorPasswordInitial extends VisitorPasswordState {} + + + +class PasswordTypeSelected extends VisitorPasswordState { + final String selectedType; + + PasswordTypeSelected(this.selectedType); + + @override + List get props => [selectedType]; +} \ No newline at end of file diff --git a/lib/pages/visitor_password/view/visitor_password_dialog.dart b/lib/pages/visitor_password/view/visitor_password_dialog.dart new file mode 100644 index 00000000..ef9639c6 --- /dev/null +++ b/lib/pages/visitor_password/view/visitor_password_dialog.dart @@ -0,0 +1,290 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.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/utils/color_manager.dart'; +import 'package:syncrow_web/utils/style.dart'; + +class VisitorPasswordDialog extends StatelessWidget { + const VisitorPasswordDialog({super.key}); + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + + return BlocProvider( + create: (context) => VisitorPasswordBloc(), + child: BlocBuilder( + builder: (context, state) { + return AlertDialog( + title: const Text('Create visitor password'), + content: SingleChildScrollView( + child: ListBody( + children: [ + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + const Text('User Name'), + ], + ), + Container( + width: size.width * 0.15, + decoration: containerDecoration, + child: TextFormField( + style: TextStyle(color: Colors.black), + decoration: textBoxDecoration()! + .copyWith(hintText: 'Please enter'), + ), + ), + ], + ), + SizedBox(width: size.width * 0.05), // Add spacing between columns + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + const Text('Email Address'), + ], + ), + Container( + width: size.width * 0.15, + decoration: containerDecoration, + child: TextFormField( + style: TextStyle(color: Colors.black), + decoration: textBoxDecoration()! + .copyWith(hintText: 'Please enter'), + ), + ), + ], + ), + ], + ), + SizedBox(height: size.height * 0.02), // Add spacing + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + const Text('Access Type'), + ], + ), + Row( + children: [ + SizedBox( + width: 200, + child: RadioListTile( + title: Text('Offline Password'), + value: 'Offline Password', + groupValue: (state is PasswordTypeSelected) + ? state.selectedType + : 'Offline Password', + onChanged: (String? value) { + if (value != null) { + context + .read() + .add(SelectPasswordType(value)); + } + }, + ), + ), + SizedBox( + width: 200, + + child: RadioListTile( + title: Text('Online Password'), + value: 'Online Password', + groupValue: (state is PasswordTypeSelected) + ? state.selectedType + : 'Offline Password', + onChanged: (String? value) { + if (value != null) { + context + .read() + .add(SelectPasswordType(value)); + } + }, + ), + ), + SizedBox( + width: 200, + + child: RadioListTile( + title: Text('Dynamic Password'), + value: 'Dynamic Password', + groupValue: (state is PasswordTypeSelected) + ? state.selectedType + : 'Offline Password', + onChanged: (String? value) { + if (value != null) { + context + .read() + .add(SelectPasswordType(value)); + } + }, + ), + ), + ], + ) + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.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 PasswordTypeSelected) + ? state.selectedType + : 'One-Time', + onChanged: (String? value) { + if (value != null) { + context + .read() + .add(SelectPasswordType(value)); + } + }, + ), + ), + SizedBox( + width: 200, + + child: RadioListTile( + title: Text('Periodic'), + value: 'Periodic', + groupValue: (state is PasswordTypeSelected) + ? state.selectedType + : 'Periodic', + onChanged: (String? value) { + if (value != null) { + context + .read() + .add(SelectPasswordType(value)); + } + }, + ), + ), + ], + ) + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + const Text('Access Period'), + ], + ), + Row( + children: [ + SizedBox( + width: 200, + child: RadioListTile( + title: const Text('One-Time'), + value: 'One-Time', + groupValue: (state is PasswordTypeSelected) + ? state.selectedType + : 'One-Time', + onChanged: (String? value) { + if (value != null) { + context + .read() + .add(SelectPasswordType(value)); + } + }, + ), + ), + SizedBox( + width: 200, + + child: RadioListTile( + title: Text('Periodic'), + value: 'Periodic', + groupValue: (state is PasswordTypeSelected) + ? state.selectedType + : 'Periodic', + onChanged: (String? value) { + if (value != null) { + context + .read() + .add(SelectPasswordType(value)); + } + }, + ), + ), + ], + ) + ], + ), + ], + ), + ), + actions: [ + TextButton( + child: const Text('Approve'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ), + ); + } +} diff --git a/lib/services/access_mang_api.dart b/lib/services/access_mang_api.dart index 0882e98b..49870143 100644 --- a/lib/services/access_mang_api.dart +++ b/lib/services/access_mang_api.dart @@ -1,36 +1,37 @@ import 'dart:convert'; -import 'package:flutter/services.dart'; -import 'package:syncrow_web/pages/access_management/model/access_manag_model.dart'; -import 'package:syncrow_web/pages/auth/model/user_model.dart'; +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/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; class AccessMangApi{ - // Future> fetchInfo() async { - // final response = await HTTPService().get( - // path: '/Users/mohammad/StudioProjects/web_auth/assets/demo.json', - // showServerMessage: true, - // expectedResponseModel: (json) { - // print('fetchInfo=$json'); - // return (json as List).map((item) => AccessManagModel.fromJson(item)).toList(); - // }, - // ); - // return response; - // } - Future> fetchInfo() async { - // Load the JSON file - final jsonString = await rootBundle.loadString('assets/dome.json'); - // Parse the JSON string - final List jsonList = json.decode(jsonString); - print('jsonList=${jsonList.runtimeType}'); - print('jsonList=${jsonList}'); - // Convert the list of JSON objects to a list of AccessManagModel instances - final List accessList = jsonList.map((item) => AccessManagModel.fromJson(item)).toList(); - - return accessList; + Future> fetchVisitorPassword() async { + try { + final response = await HTTPService().get( + path: ApiEndpoints.visitorPassword, + showServerMessage: true, + expectedResponseModel: (json) { + List jsonData = json; + print('Password List: $json'); + List passwordList = jsonData.map((jsonItem) { + return PasswordModel.fromJson(jsonItem); + }).toList(); + return passwordList; + }, + ); + return response; + } catch (e) { + debugPrint('Error fetching visitor passwords: $e'); + return []; + } } + + + + } \ No newline at end of file diff --git a/lib/services/auth_api.dart b/lib/services/auth_api.dart index a0c0b87f..7be7f328 100644 --- a/lib/services/auth_api.dart +++ b/lib/services/auth_api.dart @@ -50,8 +50,7 @@ class AuthenticationAPI { } ); return 30; - } on DioError catch (e) { - + } on DioException catch (e) { if (e.response != null) { if (e.response!.statusCode == 400) { // Handle 400 Bad Request @@ -64,7 +63,6 @@ class AuthenticationAPI { int cooldown = errorData['data']['cooldown'] ?? 1; return cooldown; } - } else { debugPrint('Error: ${e.response!.statusCode} - ${e.response!.statusMessage}'); return 1; @@ -73,7 +71,6 @@ class AuthenticationAPI { debugPrint('Error: ${e.message}'); return 1; } - return 1; } catch (e) { debugPrint('Unexpected Error: $e'); return 1; diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 4eb6ceed..d689ce79 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -9,5 +9,6 @@ abstract class ApiEndpoints { static const String sendOtp = '$baseUrl/authentication/user/send-otp'; static const String verifyOtp = '$baseUrl/authentication/user/verify-otp'; static const String getRegion = '$baseUrl/region'; + static const String visitorPassword = '$baseUrl/visitor-password'; static const String getUser = '$baseUrl/user/{userUuid}'; }