diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 6094b4c6..dffe452f 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -14,6 +14,8 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + E44A9405B1EB1B638DD05A58 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7ABF0EC746A2D686A0ED574F /* Pods_RunnerTests.framework */; }; + FF49F60EC38658783D8D66DA /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2AFAE479A87ECDEBD5D6EB30 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,12 +44,19 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 22428D486F110EE0B969469D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 253C5EA6840355311DB030EA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 2AFAE479A87ECDEBD5D6EB30 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2C0D722D2ED971BF672D18D5 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 544621C7727C798253BAB2C8 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7ABF0EC746A2D686A0ED574F /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 877FDC97D8B87080E35B3EB7 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -55,19 +64,52 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D3AD250AADBF93406007C9EB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 759A57780A409ED209817654 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E44A9405B1EB1B638DD05A58 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + FF49F60EC38658783D8D66DA /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 1454C118FFCECEEDF59152D2 /* Pods */ = { + isa = PBXGroup; + children = ( + 253C5EA6840355311DB030EA /* Pods-Runner.debug.xcconfig */, + 22428D486F110EE0B969469D /* Pods-Runner.release.xcconfig */, + D3AD250AADBF93406007C9EB /* Pods-Runner.profile.xcconfig */, + 2C0D722D2ED971BF672D18D5 /* Pods-RunnerTests.debug.xcconfig */, + 877FDC97D8B87080E35B3EB7 /* Pods-RunnerTests.release.xcconfig */, + 544621C7727C798253BAB2C8 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 20A3C64D2B1CFED5A81C3251 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 2AFAE479A87ECDEBD5D6EB30 /* Pods_Runner.framework */, + 7ABF0EC746A2D686A0ED574F /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( @@ -94,6 +136,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, + 1454C118FFCECEEDF59152D2 /* Pods */, + 20A3C64D2B1CFED5A81C3251 /* Frameworks */, ); sourceTree = ""; }; @@ -128,8 +172,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + B9A66CAAF434B6A1BD8C4E09 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, + 759A57780A409ED209817654 /* Frameworks */, ); buildRules = ( ); @@ -145,12 +191,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + C1C48B0232C0B26BFF405512 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 33590C9CD073D3D5EBA02CDE /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -222,6 +270,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 33590C9CD073D3D5EBA02CDE /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -253,6 +318,50 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + B9A66CAAF434B6A1BD8C4E09 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + C1C48B0232C0B26BFF405512 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -378,6 +487,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 2C0D722D2ED971BF672D18D5 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -395,6 +505,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 877FDC97D8B87080E35B3EB7 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -410,6 +521,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 544621C7727C798253BAB2C8 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16..21a3cc14 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/lib/main.dart b/lib/main.dart index e57db364..ad7b1260 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -59,8 +59,8 @@ class MyApp extends StatelessWidget { colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), // Set up color scheme useMaterial3: true, // Enable Material 3 ), - // home: AddDeviceDialog() - home:isLoggedIn == 'Success' ? const HomePage() : const LoginPage(), + home: VisitorPasswordDialog() + // 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 aef9508c..7c57bfa4 100644 --- a/lib/pages/access_management/bloc/access_bloc.dart +++ b/lib/pages/access_management/bloc/access_bloc.dart @@ -155,7 +155,7 @@ class AccessBloc extends Bloc { } return matchesCriteria; }).toList(); - print('Filtered data: $filteredData'); // Print to debug filtered data + print('Filtered data: $filteredData'); emit(TableLoaded(filteredData)); } catch (e) { print('Error occurred during filtering: $e'); diff --git a/lib/pages/access_management/view/access_management.dart b/lib/pages/access_management/view/access_management.dart index 265c96c0..2e543b09 100644 --- a/lib/pages/access_management/view/access_management.dart +++ b/lib/pages/access_management/view/access_management.dart @@ -235,8 +235,9 @@ class AccessManagementPage extends StatelessWidget { Expanded( child: state is TableLoaded ? DynamicTable( + withCheckBox: false, size: size, - cellDecoration: containerDecoration, + // cellDecoration: containerDecoration, headers: const [ 'Name', 'Access Type', diff --git a/lib/pages/common/custom_table.dart b/lib/pages/common/custom_table.dart index 25c821fb..ca1ff664 100644 --- a/lib/pages/common/custom_table.dart +++ b/lib/pages/common/custom_table.dart @@ -1,55 +1,107 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -class DynamicTable extends StatelessWidget { +class DynamicTable extends StatefulWidget { final List headers; final List> data; final BoxDecoration? headerDecoration; final BoxDecoration? cellDecoration; final Size size; + final bool withCheckBox; + final void Function(bool?)? onChanged; + final void Function(bool?)? selectAll; + final void Function(int, bool?)? onRowCheckboxChanged; const DynamicTable({ Key? key, required this.headers, required this.data, required this.size, + required this.withCheckBox, this.headerDecoration, this.cellDecoration, + this.onChanged, + this.selectAll, + this.onRowCheckboxChanged, }) : super(key: key); + @override + _DynamicTableState createState() => _DynamicTableState(); +} + +class _DynamicTableState extends State { + late List _selected; + bool _selectAll = false; + + @override + void initState() { + super.initState(); + _selected = List.filled(widget.data.length, false); + } + + void _toggleSelectAll(bool? value) { + setState(() { + _selectAll = value ?? false; + _selected = List.filled(widget.data.length, _selectAll); + if (widget.selectAll != null) { + widget.selectAll!(_selectAll); + } + }); + } + + void _toggleRowSelection(int index, bool? value) { + setState(() { + _selected[index] = value ?? false; + _selectAll = _selected.every((element) => element == true); + if (widget.onRowCheckboxChanged != null) { + widget.onRowCheckboxChanged!(index, _selected[index]); + } + }); + } + @override Widget build(BuildContext context) { return Container( - - decoration: cellDecoration, + decoration: widget.cellDecoration, child: Padding( padding: const EdgeInsets.all(10.0), child: ListView( scrollDirection: Axis.horizontal, children: [ - Container( - width:size.width, + SizedBox( + width: widget.size.width, child: Column( children: [ Container( - decoration: headerDecoration ?? + decoration: widget.headerDecoration ?? BoxDecoration(color: Colors.grey[200]), child: Row( - children: headers.map((header) => - _buildTableHeaderCell(header)).toList(), + children: [ + if (widget.withCheckBox) + _buildSelectAllCheckbox(), + ...widget.headers + .map((header) => _buildTableHeaderCell(header)) + .toList(), + ], ), ), Expanded( child: Container( color: Colors.white, - child: ListView( + child: ListView.builder( shrinkWrap: true, - children: data.map((row) { + itemCount: widget.data.length, + itemBuilder: (context, index) { + final row = widget.data[index]; return Row( - children: row.map((cell) => - _buildTableCell(cell.toString())).toList(), + children: [ + if (widget.withCheckBox) + _buildRowCheckbox(index), + ...row.map((cell) => + _buildTableCell(cell.toString())).toList(), + ], ); - }).toList(), + }, ), ), ), @@ -61,43 +113,62 @@ class DynamicTable extends StatelessWidget { ), ); } -} -Widget _buildTableHeaderCell(String title) { - return Expanded( - child: Container( - decoration: const BoxDecoration( - border: Border.symmetric( - vertical: BorderSide(color: ColorsManager.boxDivider))), - alignment: Alignment.centerLeft, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text(title, style: TextStyle(fontWeight: FontWeight.bold)), + + Widget _buildSelectAllCheckbox() { + return SizedBox( + width: 50, + child: Checkbox( + value: _selectAll, + onChanged: _toggleSelectAll, ), - ), - ); -} + ); + } -Widget _buildTableCell(String content) { - return Expanded( - child: Container( - height: 80, - padding: const EdgeInsets.all(20.0), - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide( // <--- right side - color: ColorsManager.boxDivider, - width: 1.0, - ), - ) + Widget _buildRowCheckbox(int index) { + return SizedBox( + width: 50, + child: Checkbox( + value: _selected[index], + onChanged: (bool? value) { + _toggleRowSelection(index, value); + }, ), - alignment: Alignment.centerLeft, - child: Text( - content, - style: TextStyle(color: Colors.black, fontSize: 12), + ); + } + + Widget _buildTableHeaderCell(String title) { + return Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border.symmetric( + vertical: BorderSide(color: ColorsManager.boxDivider))), + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)), + ), ), - ), - ); + ); + } + + Widget _buildTableCell(String content) { + return Expanded( + child: Container( + height: 80, + padding: const EdgeInsets.all(20.0), + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide( + color: ColorsManager.boxDivider, + width: 1.0, + ), + )), + alignment: Alignment.centerLeft, + child: Text( + content, + style: const TextStyle(color: Colors.black, fontSize: 12), + ), + ), + ); + } } - - - diff --git a/lib/pages/common/custom_web_textfield.dart b/lib/pages/common/custom_web_textfield.dart index 0662199b..0190ccfb 100644 --- a/lib/pages/common/custom_web_textfield.dart +++ b/lib/pages/common/custom_web_textfield.dart @@ -9,12 +9,14 @@ class CustomWebTextField extends StatelessWidget { required this.textFieldName, required this.controller, this.description, + this.validator, }); final bool isRequired; final String textFieldName; final String? description; final TextEditingController? controller; + final String? Function(String?)? validator; @override @@ -53,7 +55,6 @@ class CustomWebTextField extends StatelessWidget { ), const SizedBox(height: 7,), Container( - height: MediaQuery.of(context).size.height*0.05, decoration: containerDecoration.copyWith( color: const Color(0xFFF5F6F7), boxShadow: [ @@ -62,13 +63,17 @@ class CustomWebTextField extends StatelessWidget { spreadRadius:2, blurRadius: 3, offset: Offset(1, 1), // changes position of shadow - ), ] + ), + ] ), - child: TextFormField( - controller: controller, - style: const TextStyle(color: Colors.black), - decoration: textBoxDecoration()! - .copyWith(hintText: 'Please enter'), + child: Container( + child: TextFormField( + validator: validator, + controller: controller, + style: const TextStyle(color: Colors.black), + decoration: textBoxDecoration()! + .copyWith(hintText: 'Please enter'), + ), ), ), ], diff --git a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart index c6ce5188..0b4b3388 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart @@ -1,14 +1,19 @@ +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/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/snack_bar.dart'; // Define the BLoC -class VisitorPasswordBloc extends Bloc { +class VisitorPasswordBloc + extends Bloc { VisitorPasswordBloc() : super(VisitorPasswordInitial()) { on(selectUsageFrequency); on(_onFetchDevice); @@ -17,25 +22,25 @@ class VisitorPasswordBloc extends Bloc(selectTimeVisitorPassword); on(toggleRepeat); on(toggleDaySelection); + on(selectDevice); + on(postOnlineOneTimePassword); + on(postOnlineMultipleTimePassword); } final TextEditingController userNameController = TextEditingController(); final TextEditingController emailController = TextEditingController(); - - final TextEditingController deviceNameController = TextEditingController(); final TextEditingController deviceIdController = TextEditingController(); final TextEditingController unitNameController = TextEditingController(); final TextEditingController virtualAddressController = TextEditingController(); - List data=[]; + List data = []; + List selectedDeviceIds = ['909e052c-6934-48f3-b22d-67ee04ad7265']; + final forgetFormKey = GlobalKey(); - - - - - String accessTypeSelected='Offline Password'; - String usageFrequencySelected='One-Time'; + String accessTypeSelected = 'Online Password'; + String usageFrequencySelected = 'One-Time'; + String passwordController = ''; bool repeat = false; @@ -45,18 +50,24 @@ class VisitorPasswordBloc extends Bloc emit) { - accessTypeSelected=event.type; - emit(PasswordTypeSelected(event.type)); + DateTime? repeatStartTime=DateTime.now(); + DateTime? repeatEndTime; + + selectAccessType( + SelectPasswordType event, Emitter emit) { + accessTypeSelected = event.type; + emit(PasswordTypeSelected(event.type)); } - selectUsageFrequency(SelectUsageFrequency event, Emitter emit) { - usageFrequencySelected=event.usageType; - emit(UsageFrequencySelected(event.usageType)); + selectUsageFrequency( + SelectUsageFrequency event, Emitter emit) { + usageFrequencySelected = event.usageType; + emit(UsageFrequencySelected(event.usageType)); } - - Future selectTimeVisitorPassword(SelectTimeVisitorPassword event, Emitter emit) async { + Future selectTimeVisitorPassword( + SelectTimeVisitorPassword event, + Emitter emit) async { final DateTime? picked = await showDatePicker( context: event.context, initialDate: DateTime.now(), @@ -67,7 +78,6 @@ class VisitorPasswordBloc extends Bloc expirationTimeTimeStamp!) { - CustomSnackBar.displaySnackBar('Effective Time cannot be later than Expiration Time.'); + 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 + startTime = selectedDateTime + .toString() + .split('.') + .first; // Remove seconds and milliseconds effectiveTimeTimeStamp = selectedTimestamp; } + emit(ChangeTimeState()); + } else { - if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) { - CustomSnackBar.displaySnackBar('Expiration Time cannot be earlier than Effective Time.'); + 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 + endTime = selectedDateTime + .toString() + .split('.') + .first; // Remove seconds and milliseconds expirationTimeTimeStamp = selectedTimestamp; } + emit(VisitorPasswordInitial()); + } } } + // emit(AccessInitial()); // emit(TableLoaded(data)); } - - bool toggleRepeat(ToggleRepeatEvent event, Emitter emit) { + bool toggleRepeat( + ToggleRepeatEvent event, Emitter emit) { emit(LoadingInitialState()); repeat = !repeat; emit(IsRepeatState(repeat: repeat)); return repeat; } - - List> days = [ {"day": "Sun", "key": "Sun"}, {"day": "Mon", "key": "Mon"}, @@ -143,7 +167,10 @@ class VisitorPasswordBloc extends Bloc selectedDays = []; - Future toggleDaySelection(ToggleDaySelectionEvent event, Emitter emit,)async { + Future toggleDaySelection( + ToggleDaySelectionEvent event, + Emitter emit, + ) async { emit(LoadingInitialState()); if (selectedDays.contains(event.key)) { selectedDays.remove(event.key); @@ -152,17 +179,90 @@ class VisitorPasswordBloc extends Bloc _onFetchDevice( FetchDevice event, Emitter emit) async { try { emit(DeviceLoaded()); - data = await AccessMangApi().fetchDevices(); + data = await AccessMangApi().fetchDevices(); emit(TableLoaded(data)); } catch (e) { emit(FailedState(e.toString())); } } + Future postOnlineOneTimePassword( + OnlineOneTimePasswordEvent event, + Emitter emit) async { + try { + // emit(DeviceLoaded()); + await AccessMangApi().postOnlineOneTime( + email: event.email, + devicesUuid: selectedDeviceIds, + passwordName: event.passwordName); + // emit(TableLoaded(data)); + } catch (e) { + emit(FailedState(e.toString())); + } + } + 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, + ), + ] , + password: passwordController, + invalidTime:event.invalidTime , + effectiveTime:event.effectiveTime , + email: event.email, + devicesUuid: selectedDeviceIds, + passwordName: event.passwordName + ); + // emit(TableLoaded(data)); + } catch (e) { + emit(FailedState(e.toString())); + } + } + + void selectDevice( + SelectDeviceEvent event, Emitter emit) { + if (selectedDeviceIds.contains(event.deviceId)) { + selectedDeviceIds.remove(event.deviceId); + } else { + selectedDeviceIds.add(event.deviceId); + } + print(selectedDeviceIds); + } + + String? validate(String? value) { + if (value == null || value.isEmpty) { + return 'Field is required'; + } + return null; + } + + + Future generate7DigitNumber() async { + emit(LoadingInitialState()); + passwordController=''; + Random random = Random(); + int min = 1000000; + int max = 9999999; + passwordController = (min + random.nextInt(max - min + 1)).toString(); + emit(GeneratePasswordState()); + return passwordController; + } + String getTimeOnly(DateTime? dateTime) { + if (dateTime == null) return ''; + return DateFormat('HH:mm').format(dateTime); + } } diff --git a/lib/pages/visitor_password/bloc/visitor_password_event.dart b/lib/pages/visitor_password/bloc/visitor_password_event.dart index 8195e155..40fe2542 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_event.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_event.dart @@ -47,4 +47,32 @@ class ToggleDaySelectionEvent extends VisitorPasswordEvent { class ToggleRepeatEvent extends VisitorPasswordEvent {} +class GeneratePasswordEvent extends VisitorPasswordEvent {} + class FetchDevice extends VisitorPasswordEvent {} + +class OnlineOneTimePasswordEvent extends VisitorPasswordEvent { + final String? email; + final String? passwordName; + + const OnlineOneTimePasswordEvent({this.email,this.passwordName}); + + @override + List get props => [email!,passwordName!,]; +} +class OnlineMultipleTimePasswordEvent extends VisitorPasswordEvent { + final String? email; + final String? passwordName; + final String? invalidTime; + final String? effectiveTime; + + const OnlineMultipleTimePasswordEvent({this.email,this.passwordName,this.invalidTime,this.effectiveTime}); + + @override + List get props => [email!,passwordName!,invalidTime!,effectiveTime!]; +} + +class SelectDeviceEvent extends VisitorPasswordEvent { + final String deviceId; + const SelectDeviceEvent(this.deviceId); +} \ 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 88b14ec9..9ec77f4a 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_state.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_state.dart @@ -44,6 +44,7 @@ class IsRepeatState extends VisitorPasswordState { class LoadingInitialState extends VisitorPasswordState {} class ChangeTimeState extends VisitorPasswordState {} class DeviceLoaded extends VisitorPasswordState {} +class GeneratePasswordState extends VisitorPasswordState {} class FailedState extends VisitorPasswordState { final String message; @@ -59,4 +60,9 @@ class TableLoaded extends VisitorPasswordState { @override List get props => [data]; +} + +class DeviceSelectionUpdated extends VisitorPasswordState { + final List selectedDeviceIds; + const DeviceSelectionUpdated(this.selectedDeviceIds); } \ No newline at end of file diff --git a/lib/pages/visitor_password/model/schedule_model.dart b/lib/pages/visitor_password/model/schedule_model.dart new file mode 100644 index 00000000..efeeff35 --- /dev/null +++ b/lib/pages/visitor_password/model/schedule_model.dart @@ -0,0 +1,27 @@ +class Schedule { + final String effectiveTime; + final String invalidTime; + final List workingDay; + + Schedule({ + required this.effectiveTime, + required this.invalidTime, + required this.workingDay, + }); + + factory Schedule.fromJson(Map json) { + return Schedule( + effectiveTime: json['effectiveTime'], + invalidTime: json['invalidTime'], + workingDay: List.from(json['workingDay']), + ); + } + + Map toJson() { + return { + 'effectiveTime': effectiveTime, + 'invalidTime': invalidTime, + 'workingDay': workingDay, + }; + } +} \ No newline at end of file diff --git a/lib/pages/visitor_password/view/add_device_dialog.dart b/lib/pages/visitor_password/view/add_device_dialog.dart index d987621e..1b237759 100644 --- a/lib/pages/visitor_password/view/add_device_dialog.dart +++ b/lib/pages/visitor_password/view/add_device_dialog.dart @@ -15,7 +15,6 @@ import '../../common/custom_table.dart'; class AddDeviceDialog extends StatelessWidget { const AddDeviceDialog({super.key}); - @override Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; @@ -138,41 +137,42 @@ class AddDeviceDialog extends StatelessWidget { ), ), ), - ], ), ], ), - SizedBox(height: 20), - Container( - child: Expanded( - child: state is TableLoaded - ? Container( - decoration: containerDecoration, - child: DynamicTable( - size: size*0.5, - headers: ['Device Name', 'Device ID', 'Access Type', 'Unit Name', 'Status'], - data: state.data.map((item) { - return [ - item.name.toString(), - item.uuid.toString(), - item.productType.toString(), - '', - item.online.toString(), - // item.categoryName.toString(), - // accessBloc.timestampToDateTime(item.effectiveTime).toString(), - // accessBloc.timestampToDateTime(item.invalidTime).toString(), - // item.deviceUuid.toString(), - // item.passwordCreated != null ? accessBloc.timestampToDateTime(item.passwordCreated).toString() : 'no data', - // item.passwordStatus.toString(), - ]; - }).toList(), - ), - ) - // TableWidget(size: size, headers: ['Device Name', 'Device ID', 'Access Type', 'Unit Name', 'Status', 'Virtual Address',], data: [], bloc: bloc) - : const Center(child: CircularProgressIndicator())), - ) + const SizedBox(height: 20), + Expanded( + child: state is TableLoaded + ? Container( + decoration: containerDecoration, + child: DynamicTable( + selectAll: (p0) { + visitorBloc.selectedDeviceIds.clear(); + for (var item in state.data) { + visitorBloc.add(SelectDeviceEvent(item.uuid)); + } + }, + onRowCheckboxChanged: (index, isSelected) { + final deviceId = state.data[index].uuid; // Adjust as per your actual data structure + visitorBloc.add(SelectDeviceEvent(deviceId)); + }, + withCheckBox: true, + size: size*0.5, + headers: const [ 'Device Name', 'Device ID', 'Access Type', 'Unit Name', 'Status'], + data: state.data.map((item) { + return [ + item.name.toString(), + item.uuid.toString(), + item.productType.toString(), + '', + item.online.toString(), + ]; + }).toList(), + ), + ) + : const Center(child: CircularProgressIndicator())) ], ), ), @@ -195,9 +195,14 @@ class AddDeviceDialog extends StatelessWidget { ), ), Container( + decoration: containerDecoration, width: size.width * 0.2, - child: const DefaultButton( + child: DefaultButton( + onPressed: () { + Navigator.of(context).pop(); // Close the dialog + + }, borderRadius: 8, child: Text('Ok'), ), diff --git a/lib/pages/visitor_password/view/visitor_password_dialog.dart b/lib/pages/visitor_password/view/visitor_password_dialog.dart index 0693f724..b752c3a9 100644 --- a/lib/pages/visitor_password/view/visitor_password_dialog.dart +++ b/lib/pages/visitor_password/view/visitor_password_dialog.dart @@ -27,238 +27,246 @@ class VisitorPasswordDialog extends StatelessWidget { backgroundColor: Colors.white, title: const Text('Create visitor password'), content: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(10.0), - child: ListBody( - children: [ - Container( - child: Row( + 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: '', + ), + ), + 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 Spacer(), + ], + ), + ), + SizedBox(height: 20,), + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - flex: 2, - child: CustomWebTextField( - controller: visitorBloc.userNameController, - isRequired: true, - textFieldName: 'Name', - description: '', - ), + Row( + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + const Text('Access Type'), + ], ), - const Spacer(), - Expanded( - flex: 2, - child: CustomWebTextField( - controller: visitorBloc.emailController, - isRequired: true, - textFieldName: 'Email Address', - description: 'The password will be sent to the visitor’s email address.', - ), + 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) { + 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) { + 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)); + } + }, + ), + ), + ], ), - const Spacer(), + 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'), + SizedBox(height: 20,) ], ), - ), - 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('Offline Password'), - value: 'Offline Password', - groupValue: (state is PasswordTypeSelected) - ? state.selectedType - : visitorBloc.accessTypeSelected, - onChanged: (String? value) { - if (value != null) { - context.read().add(SelectPasswordType(value)); - } - }, + visitorBloc.accessTypeSelected=='Dynamic Password' ? + SizedBox(): + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), ), - ), - 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) { - context.read().add(SelectPasswordType(value)); - } - }, + 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) { + context.read().add(SelectUsageFrequency(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)); - } - }, + 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('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'), - 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) { - context.read().add(SelectUsageFrequency(value)); - } - }, - ), - ), - SizedBox( - width: 200, - child: RadioListTile( - title: const Text('Periodic'), - value: 'Periodic', - groupValue: (state is UsageFrequencySelected) - ? state.selectedFrequency - : visitorBloc.usageFrequencySelected, - onChanged: (String? value) { - if (value != null) { - context.read().add(SelectUsageFrequency(value)); - } - }, - ), - ), - ], - ), + ], + ), - Text('Within the validity period, each device can be unlocked only once.') - ], - ), + Text('Within the validity period, each device can be unlocked only once.') + ], + ), - const SizedBox(height: 20,), + const SizedBox(height: 20,), - visitorBloc.accessTypeSelected=='Dynamic Password' ? - SizedBox(): - DateTimeWebWidget( - isRequired: true, - title: 'Access Period', - size: size, - endTime: () { - visitorBloc.add(SelectTimeVisitorPassword(context: context, isStart: false)); - }, - startTime: () { - visitorBloc.add(SelectTimeVisitorPassword(context: context, isStart: true)); - }, - firstString: visitorBloc.startTime, - secondString: visitorBloc.endTime, - ), - const SizedBox(height: 20,), + visitorBloc.accessTypeSelected=='Dynamic Password' ? + SizedBox(): + DateTimeWebWidget( + isRequired: true, + title: 'Access Period', + size: size, + endTime: () { + visitorBloc.add(SelectTimeVisitorPassword(context: context, isStart: false)); + }, + startTime: () { + visitorBloc.add(SelectTimeVisitorPassword(context: context, isStart: true)); + }, + 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,), - visitorBloc.usageFrequencySelected=='Periodic'&&visitorBloc.accessTypeSelected=='Offline 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, + 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, + ), ), ), ), - ):const SizedBox(), - 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(); + 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'), + }, + ); + }, + borderRadius: 8, + child: Text('+ Add Device'), + ), ), - ), - ], - ), + ], + ), - ], + ], + ), ), ), ), @@ -282,7 +290,36 @@ class VisitorPasswordDialog extends StatelessWidget { Container( decoration: containerDecoration, width: size.width * 0.2, - child: const DefaultButton( + child: DefaultButton( + onPressed: () { + if(visitorBloc.forgetFormKey.currentState!.validate()){ + 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=='Periodic'&&visitorBloc.accessTypeSelected=='Online Password') { + visitorBloc.add(OnlineMultipleTimePasswordEvent( + passwordName:visitorBloc.userNameController.text , + email: visitorBloc.emailController.text, + effectiveTime:visitorBloc.effectiveTimeTimeStamp.toString() , + invalidTime:visitorBloc.expirationTimeTimeStamp.toString() + ) + ); + } + } + + }, borderRadius: 8, child: Text('Ok'), ), diff --git a/lib/services/access_mang_api.dart b/lib/services/access_mang_api.dart index de14f4ea..f755209d 100644 --- a/lib/services/access_mang_api.dart +++ b/lib/services/access_mang_api.dart @@ -1,8 +1,7 @@ import 'dart:convert'; - -import 'package:dio/dio.dart'; import 'package:flutter/cupertino.dart'; import 'package:syncrow_web/pages/access_management/model/password_model.dart'; +import 'package:syncrow_web/pages/visitor_password/model/schedule_model.dart'; import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; @@ -10,8 +9,6 @@ import '../pages/visitor_password/model/device_model.dart'; class AccessMangApi{ - - Future> fetchVisitorPassword() async { try { final response = await HTTPService().get( @@ -54,6 +51,76 @@ class AccessMangApi{ } } + Future postOnlineOneTime({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({ + "email": email, + "passwordName": passwordName, + "devicesUuid": devicesUuid + }), + showServerMessage: true, + expectedResponseModel: (json) { + List jsonData = json; + print('postOfflineOneTime List: $json'); + }, + ); + return response; + } catch (e) { + debugPrint('Error fetching $e'); + return []; + } + } + + Future postOnlineMultipleTime({ + String? effectiveTime, + String? invalidTime, + String? email, + String? password, + String? passwordName, + List? scheduleList, + List? devicesUuid}) async { + try { + Map body = { + "email": email, + "devicesUuid": devicesUuid, + "passwordName": passwordName, + "password": password, + "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.sendOfflineMultipleTime, + body: jsonEncode(body), + showServerMessage: true, + expectedResponseModel: (json) { + List jsonData = json; + print('postOfflineOneTime List: $json'); + }, + ); + return response; + } catch (e) { + debugPrint('Error fetching $e'); + return []; + } + } + diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index b9ee2f5e..b2db133a 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -11,5 +11,11 @@ abstract class ApiEndpoints { static const String getRegion = '$baseUrl/region'; static const String visitorPassword = '$baseUrl/visitor-password'; static const String getDevices = '$baseUrl/visitor-password/devices'; + + + static const String sendOfflineOneTime = '$baseUrl/visitor-password/temporary-password/online/one-time'; + static const String sendOfflineMultipleTime = '$baseUrl/visitor-password/temporary-password/online/multiple-time'; + + static const String getUser = '$baseUrl/user/{userUuid}'; } diff --git a/pubspec.lock b/pubspec.lock index d22f2d41..b8984d5a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -240,6 +240,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" js: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c1fd3f66..faaa7ddb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,7 +44,7 @@ dependencies: flutter_secure_storage: ^9.2.2 shared_preferences: ^2.3.0 data_table_2: ^2.5.15 - + intl: ^0.19.0 dev_dependencies: flutter_test: sdk: flutter