diff --git a/lib/core/theme/app_theme.dart b/lib/core/theme/app_theme.dart index e69de29b..8b137891 100644 --- a/lib/core/theme/app_theme.dart +++ b/lib/core/theme/app_theme.dart @@ -0,0 +1 @@ + diff --git a/lib/pages/access_management/bloc/access_bloc.dart b/lib/pages/access_management/bloc/access_bloc.dart index 47f865ef..d5b42bd1 100644 --- a/lib/pages/access_management/bloc/access_bloc.dart +++ b/lib/pages/access_management/bloc/access_bloc.dart @@ -22,33 +22,36 @@ class AccessBloc extends Bloc { int? effectiveTimeTimeStamp; int? expirationTimeTimeStamp; - TextEditingController passwordName= TextEditingController(); + TextEditingController passwordName = TextEditingController(); List filteredData = []; - List data=[]; + List data = []; Future _onFetchTableData( FetchTableData event, Emitter emit) async { try { emit(AccessLoaded()); - data = await AccessMangApi().fetchVisitorPassword(); - filteredData= data; + data = await AccessMangApi().fetchVisitorPassword(); + 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; + 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', @@ -57,20 +60,19 @@ class AccessBloc extends Bloc { 'Expired' ]; - - Future selectFilterTap(TabChangedEvent event, Emitter emit) async { + Future selectFilterTap( + TabChangedEvent event, Emitter emit) async { try { emit(AccessLoaded()); - selectedIndex= event.selectedIndex; + selectedIndex = event.selectedIndex; emit(AccessInitial()); emit(TableLoaded(data)); } catch (e) { - emit(FailedState( e.toString())); + emit(FailedState(e.toString())); return; } } - Future selectTime(SelectTime event, Emitter emit) async { emit(AccessLoaded()); @@ -84,7 +86,6 @@ class AccessBloc extends Bloc { final TimeOfDay? timePicked = await showTimePicker( context: event.context, initialTime: TimeOfDay.now(), - builder: (context, child) { return Theme( data: ThemeData.light().copyWith( @@ -111,24 +112,35 @@ class AccessBloc extends Bloc { timePicked.minute, ); final selectedTimestamp = DateTime( - selectedDateTime.year, - selectedDateTime.month, - selectedDateTime.day, - selectedDateTime.hour, - selectedDateTime.minute, - ).millisecondsSinceEpoch ~/ 1000; // Divide by 1000 to remove milliseconds + 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.'); + 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; } } 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; } } @@ -137,8 +149,8 @@ class AccessBloc extends Bloc { emit(ChangeTimeState()); } - - Future _filterData(FilterDataEvent event, Emitter emit) async { + Future _filterData( + FilterDataEvent event, Emitter emit) async { emit(AccessLoaded()); try { filteredData = data.where((item) { @@ -151,7 +163,8 @@ class AccessBloc extends Bloc { } } if (event.startTime != null && event.endTime != null) { - final int? effectiveTime = int.tryParse(item.effectiveTime.toString()); + final int? effectiveTime = + int.tryParse(item.effectiveTime.toString()); final int? invalidTime = int.tryParse(item.invalidTime.toString()); if (effectiveTime == null || invalidTime == null) { matchesCriteria = false; @@ -163,11 +176,14 @@ class AccessBloc extends Bloc { } } } - if (event.selectedTabIndex == 1 && item.passwordStatus.value != 'To Be Effective') { + if (event.selectedTabIndex == 1 && + item.passwordStatus.value != 'To Be Effective') { matchesCriteria = false; - } else if (event.selectedTabIndex == 2 && item.passwordStatus.value != 'Effective') { + } else if (event.selectedTabIndex == 2 && + item.passwordStatus.value != 'Effective') { matchesCriteria = false; - } else if (event.selectedTabIndex == 3 && item.passwordStatus.value != 'Expired') { + } else if (event.selectedTabIndex == 3 && + item.passwordStatus.value != 'Expired') { matchesCriteria = false; } return matchesCriteria; @@ -178,23 +194,25 @@ class AccessBloc extends Bloc { } } - resetSearch(ResetSearch event, Emitter emit) async{ + resetSearch(ResetSearch event, Emitter emit) async { emit(AccessLoaded()); - startTime = 'Start Time'; - endTime = 'End Time'; - passwordName.clear(); - selectedIndex=0; - effectiveTimeTimeStamp=null; - expirationTimeTimeStamp=null; + startTime = 'Start Time'; + endTime = 'End Time'; + passwordName.clear(); + selectedIndex = 0; + effectiveTimeTimeStamp = null; + expirationTimeTimeStamp = null; add(FetchTableData()); } String timestampToDate(dynamic timestamp) { - DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(int.parse(timestamp) * 1000); + DateTime dateTime = + DateTime.fromMillisecondsSinceEpoch(int.parse(timestamp) * 1000); return "${dateTime.year}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.day.toString().padLeft(2, '0')}"; } - Future onTabChanged(TabChangedEvent event, Emitter emit) async { + Future onTabChanged( + TabChangedEvent event, Emitter emit) async { try { emit(AccessLoaded()); selectedIndex = event.selectedIndex; @@ -203,13 +221,19 @@ class AccessBloc extends Bloc { filteredData = data; break; case 1: // To Be Effective - filteredData = data.where((item) => item.passwordStatus.value == "To Be Effective").toList(); + filteredData = data + .where((item) => item.passwordStatus.value == "To Be Effective") + .toList(); break; case 2: // Effective - filteredData = data.where((item) => item.passwordStatus.value == "Effective").toList(); + filteredData = data + .where((item) => item.passwordStatus.value == "Effective") + .toList(); break; case 3: // Expired - filteredData = data.where((item) => item.passwordStatus.value == "Expired").toList(); + filteredData = data + .where((item) => item.passwordStatus.value == "Expired") + .toList(); break; default: filteredData = data; @@ -218,12 +242,10 @@ class AccessBloc extends Bloc { selectedTabIndex: selectedIndex, passwordName: passwordName.text.toLowerCase(), startTime: effectiveTimeTimeStamp, - endTime: expirationTimeTimeStamp - )); + 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 f2f631b4..1bd7dbd3 100644 --- a/lib/pages/access_management/bloc/access_event.dart +++ b/lib/pages/access_management/bloc/access_event.dart @@ -1,14 +1,15 @@ - import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; -abstract class AccessEvent extends Equatable { +abstract class AccessEvent extends Equatable { const AccessEvent(); @override List get props => []; } + class FetchTableData extends AccessEvent {} + class ResetSearch extends AccessEvent {} class TabChangedEvent extends AccessEvent { @@ -17,16 +18,14 @@ class TabChangedEvent extends AccessEvent { const TabChangedEvent(this.selectedIndex); } - class SelectTime extends AccessEvent { - final BuildContext context; + final BuildContext context; final bool isStart; - const SelectTime({required this.context,required this.isStart}); + const SelectTime({required this.context, required this.isStart}); @override - List get props => [context,isStart]; + List get props => [context, isStart]; } - class FilterDataEvent extends AccessEvent { final String? passwordName; final int? startTime; @@ -34,13 +33,9 @@ class FilterDataEvent extends AccessEvent { final int selectedTabIndex; // Add this field const FilterDataEvent({ - this.passwordName, + this.passwordName, this.startTime, this.endTime, required this.selectedTabIndex, // Initialize this field - }); } - - - diff --git a/lib/pages/access_management/bloc/access_state.dart b/lib/pages/access_management/bloc/access_state.dart index 11253d1f..0790a735 100644 --- a/lib/pages/access_management/bloc/access_state.dart +++ b/lib/pages/access_management/bloc/access_state.dart @@ -11,6 +11,7 @@ abstract class AccessState extends Equatable { class AccessInitial extends AccessState {} class AccessLoaded extends AccessState {} + class FailedState extends AccessState { final String message; @@ -29,7 +30,7 @@ class TableLoaded extends AccessState { List get props => [data]; } -class TabState extends AccessState { +class TabState extends AccessState { final int selectedIndex; const TabState({required this.selectedIndex}); diff --git a/lib/pages/access_management/model/password_model.dart b/lib/pages/access_management/model/password_model.dart index 584e9b7e..e79528fc 100644 --- a/lib/pages/access_management/model/password_model.dart +++ b/lib/pages/access_management/model/password_model.dart @@ -30,9 +30,9 @@ class PasswordModel { effectiveTime: json['effectiveTime'], passwordCreated: json['passwordCreated'], createdTime: json['createdTime'], - passwordName: json['passwordName']??'No name', // New field - passwordStatus:AccessStatusExtension.fromString(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'], ); } @@ -50,5 +50,4 @@ class PasswordModel { 'deviceUuid': deviceUuid, }; } - } diff --git a/lib/pages/access_management/view/access_management.dart b/lib/pages/access_management/view/access_management.dart index 4c7023bb..bebc9f92 100644 --- a/lib/pages/access_management/view/access_management.dart +++ b/lib/pages/access_management/view/access_management.dart @@ -6,7 +6,6 @@ import 'package:syncrow_web/pages/access_management/bloc/access_state.dart'; import 'package:syncrow_web/pages/common/custom_table.dart'; import 'package:syncrow_web/pages/common/date_time_widget.dart'; 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'; diff --git a/lib/pages/auth/bloc/auth_bloc.dart b/lib/pages/auth/bloc/auth_bloc.dart index 514e158b..ea53fdd3 100644 --- a/lib/pages/auth/bloc/auth_bloc.dart +++ b/lib/pages/auth/bloc/auth_bloc.dart @@ -84,7 +84,7 @@ class AuthBloc extends Bloc { } else if (response == "You entered wrong otp") { forgetValidate = 'Wrong one time password.'; emit(AuthInitialState()); - }else if (response == "OTP expired") { + } else if (response == "OTP expired") { forgetValidate = 'One time password has been expired.'; emit(AuthInitialState()); } @@ -94,6 +94,7 @@ class AuthBloc extends Bloc { // emit(FailureForgetState(error: failure.toString())); } } + //925207 String? validateCode(String? value) { if (value == null || value.isEmpty) { diff --git a/lib/pages/auth/bloc/auth_event.dart b/lib/pages/auth/bloc/auth_event.dart index 4f80a2db..a8786cbc 100644 --- a/lib/pages/auth/bloc/auth_event.dart +++ b/lib/pages/auth/bloc/auth_event.dart @@ -13,47 +13,60 @@ class LoginButtonPressed extends AuthEvent { final String password; final String regionUuid; - const LoginButtonPressed({required this.username, required this.password, required this.regionUuid, }); + const LoginButtonPressed({ + required this.username, + required this.password, + required this.regionUuid, + }); @override - List get props => [username, password,regionUuid]; + List get props => [username, password, regionUuid]; } class CheckBoxEvent extends AuthEvent { final bool? newValue; - const CheckBoxEvent({required this.newValue,}); + const CheckBoxEvent({ + required this.newValue, + }); @override - List get props => [newValue!,]; + List get props => [ + newValue!, + ]; } -class GetCodeEvent extends AuthEvent{} +class GetCodeEvent extends AuthEvent {} -class SubmitEvent extends AuthEvent{} +class SubmitEvent extends AuthEvent {} -class StartTimerEvent extends AuthEvent{} +class StartTimerEvent extends AuthEvent {} -class StopTimerEvent extends AuthEvent{} +class StopTimerEvent extends AuthEvent {} class UpdateTimerEvent extends AuthEvent { final int remainingTime; final bool isButtonEnabled; - const UpdateTimerEvent({required this.remainingTime, required this.isButtonEnabled}); + const UpdateTimerEvent( + {required this.remainingTime, required this.isButtonEnabled}); } -class ChangePasswordEvent extends AuthEvent{} +class ChangePasswordEvent extends AuthEvent {} -class SendOtpEvent extends AuthEvent{} +class SendOtpEvent extends AuthEvent {} -class PasswordVisibleEvent extends AuthEvent{ +class PasswordVisibleEvent extends AuthEvent { final bool? newValue; - const PasswordVisibleEvent({required this.newValue,}); + const PasswordVisibleEvent({ + required this.newValue, + }); } class RegionInitialEvent extends AuthEvent {} + class CheckEnableEvent extends AuthEvent {} + class ChangeValidateEvent extends AuthEvent {} class SelectRegionEvent extends AuthEvent { @@ -62,4 +75,3 @@ class SelectRegionEvent extends AuthEvent { @override List get props => [val]; } - diff --git a/lib/pages/auth/bloc/auth_state.dart b/lib/pages/auth/bloc/auth_state.dart index 973ee400..8f2f6bd5 100644 --- a/lib/pages/auth/bloc/auth_state.dart +++ b/lib/pages/auth/bloc/auth_state.dart @@ -12,6 +12,7 @@ class LoginInitial extends AuthState {} class AuthTokenLoading extends AuthState {} class AuthLoading extends AuthState {} + class AuthInitialState extends AuthState {} class LoginSuccess extends AuthState {} @@ -55,7 +56,8 @@ class TimerState extends AuthState { final bool isButtonEnabled; final int remainingTime; - const TimerState({required this.isButtonEnabled, required this.remainingTime}); + const TimerState( + {required this.isButtonEnabled, required this.remainingTime}); @override List get props => [isButtonEnabled, remainingTime]; @@ -74,6 +76,7 @@ class AuthTokenError extends AuthError { class AuthSuccess extends AuthState {} class AuthTokenSuccess extends AuthSuccess {} + class TimerUpdated extends AuthState { final String formattedTime; final bool isButtonEnabled; diff --git a/lib/pages/auth/model/region_model.dart b/lib/pages/auth/model/region_model.dart index fd6306a2..3a2afcd1 100644 --- a/lib/pages/auth/model/region_model.dart +++ b/lib/pages/auth/model/region_model.dart @@ -1,5 +1,3 @@ - - class RegionModel { final String name; final String id; diff --git a/lib/pages/auth/model/token.dart b/lib/pages/auth/model/token.dart index 3e0728bc..53c6f401 100644 --- a/lib/pages/auth/model/token.dart +++ b/lib/pages/auth/model/token.dart @@ -42,14 +42,11 @@ class Token { //save token to secure storage var storage = const FlutterSecureStorage(); storage.write( - key: loginAccessTokenKey, - value: json[loginAccessTokenKey] ?? ''); + key: loginAccessTokenKey, value: json[loginAccessTokenKey] ?? ''); storage.write( - key: loginRefreshTokenKey, - value: json[loginRefreshTokenKey] ?? ''); + key: loginRefreshTokenKey, value: json[loginRefreshTokenKey] ?? ''); //create token object ? - return Token( - json[loginAccessTokenKey] ?? '', + return Token(json[loginAccessTokenKey] ?? '', json[loginRefreshTokenKey] ?? '', '', 0, 0); } diff --git a/lib/pages/auth/model/user_model.dart b/lib/pages/auth/model/user_model.dart index cbae385a..84d4661f 100644 --- a/lib/pages/auth/model/user_model.dart +++ b/lib/pages/auth/model/user_model.dart @@ -1,5 +1,3 @@ - - import 'package:syncrow_web/pages/auth/model/token.dart'; class UserModel { diff --git a/lib/pages/auth/model/verify_code.dart b/lib/pages/auth/model/verify_code.dart index da29c25b..a4a451ee 100644 --- a/lib/pages/auth/model/verify_code.dart +++ b/lib/pages/auth/model/verify_code.dart @@ -10,7 +10,10 @@ class VerifyPassCode { final String deviceId; VerifyPassCode( - {required this.phone, required this.passCode, required this.agent, required this.deviceId}); + {required this.phone, + required this.passCode, + required this.agent, + required this.deviceId}); factory VerifyPassCode.fromJson(Map json) => VerifyPassCode( phone: json[verificationPhone], diff --git a/lib/pages/auth/view/forget_password_mobile_page.dart b/lib/pages/auth/view/forget_password_mobile_page.dart index 2aab5894..05d472ba 100644 --- a/lib/pages/auth/view/forget_password_mobile_page.dart +++ b/lib/pages/auth/view/forget_password_mobile_page.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; class ForgetPasswordMobilePage extends StatelessWidget { diff --git a/lib/pages/auth/view/forget_password_page.dart b/lib/pages/auth/view/forget_password_page.dart index f90b2ef4..63b4d0f9 100644 --- a/lib/pages/auth/view/forget_password_page.dart +++ b/lib/pages/auth/view/forget_password_page.dart @@ -1,19 +1,15 @@ - import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/auth/view/forget_password_mobile_page.dart'; import 'package:syncrow_web/pages/auth/view/forget_password_web_page.dart'; import 'package:syncrow_web/utils/responsive_layout.dart'; - class ForgetPasswordPage extends StatelessWidget { const ForgetPasswordPage({super.key}); @override Widget build(BuildContext context) { - return const ResponsiveLayout( + return const ResponsiveLayout( desktopBody: ForgetPasswordWebPage(), - mobileBody:ForgetPasswordWebPage() - ); + mobileBody: ForgetPasswordWebPage()); } } - diff --git a/lib/pages/auth/view/forget_password_web_page.dart b/lib/pages/auth/view/forget_password_web_page.dart index 47f0beb7..907569ee 100644 --- a/lib/pages/auth/view/forget_password_web_page.dart +++ b/lib/pages/auth/view/forget_password_web_page.dart @@ -36,7 +36,7 @@ class ForgetPasswordWebPage extends StatelessWidget { } }, builder: (context, state) { - return _buildForm(context, state); + return _buildForm(context, state); }, ), ), @@ -66,7 +66,7 @@ class ForgetPasswordWebPage extends StatelessWidget { child: Stack( children: [ if (state is AuthLoading) - const Center(child: CircularProgressIndicator()), + const Center(child: CircularProgressIndicator()), ListView( shrinkWrap: true, controller: _scrollController, @@ -99,7 +99,8 @@ class ForgetPasswordWebPage extends StatelessWidget { borderRadius: const BorderRadius.all(Radius.circular(30)), border: Border.all( - color: ColorsManager.graysColor.withOpacity(0.2)), + color: + ColorsManager.graysColor.withOpacity(0.2)), ), child: Form( key: forgetBloc.forgetFormKey, @@ -108,7 +109,8 @@ class ForgetPasswordWebPage extends StatelessWidget { horizontal: size.width * 0.02, vertical: size.width * 0.003), child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 10), @@ -122,21 +124,27 @@ class ForgetPasswordWebPage extends StatelessWidget { const SizedBox(height: 10), Text( 'Please fill in your account information to\nretrieve your password', - style: Theme.of(context).textTheme.bodySmall!.copyWith( - fontSize: 14, - fontWeight: FontWeight.w400), + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + fontSize: 14, + fontWeight: FontWeight.w400), ), const SizedBox(height: 10), Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text( "Country/Region", - style: Theme.of(context).textTheme.bodySmall!.copyWith( - fontSize: 14, - fontWeight: FontWeight.w400), - + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + fontSize: 14, + fontWeight: FontWeight.w400), ), const SizedBox(height: 10), SizedBox( @@ -145,7 +153,8 @@ class ForgetPasswordWebPage extends StatelessWidget { icon: const Icon( Icons.keyboard_arrow_down_outlined, ), - decoration: textBoxDecoration()!.copyWith( + decoration: + textBoxDecoration()!.copyWith( hintText: null, ), hint: SizedBox( @@ -160,15 +169,14 @@ class ForgetPasswordWebPage extends StatelessWidget { ), ), isDense: true, - style: - const TextStyle(color: Colors.black), + style: const TextStyle( + color: Colors.black), items: forgetBloc.regionList! .map((RegionModel region) { return DropdownMenuItem( value: region.id, child: SizedBox( - width: size.width*0.06, - + width: size.width * 0.06, child: Text(region.name)), ); }).toList(), @@ -183,13 +191,18 @@ class ForgetPasswordWebPage extends StatelessWidget { ), const SizedBox(height: 20), Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text( "Account", - style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 14,fontWeight: FontWeight.w400), - + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + fontSize: 14, + fontWeight: FontWeight.w400), ), const SizedBox(height: 10), SizedBox( @@ -197,22 +210,29 @@ class ForgetPasswordWebPage extends StatelessWidget { validator: forgetBloc.validateEmail, controller: forgetBloc.forgetEmailController, - decoration: textBoxDecoration()!.copyWith( - hintText: 'Enter your email'), - style: - const TextStyle(color: Colors.black), + decoration: textBoxDecoration()! + .copyWith( + hintText: 'Enter your email'), + style: const TextStyle( + color: Colors.black), ), ), ], ), const SizedBox(height: 20.0), Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text( "One Time Password", - style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 14,fontWeight: FontWeight.w400), + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + fontSize: 14, + fontWeight: FontWeight.w400), ), const SizedBox(height: 10), SizedBox( @@ -221,65 +241,88 @@ class ForgetPasswordWebPage extends StatelessWidget { keyboardType: TextInputType.visiblePassword, controller: forgetBloc.forgetOtp, - decoration: textBoxDecoration()!.copyWith( + decoration: + textBoxDecoration()!.copyWith( hintText: 'Enter Code', suffixIcon: SizedBox( width: 100, child: Center( child: InkWell( - onTap:state is TimerState && !state.isButtonEnabled && state.remainingTime!=1?null: () { - forgetBloc.add(StartTimerEvent()); - }, + onTap: state is TimerState && + !state + .isButtonEnabled && + state.remainingTime != + 1 + ? null + : () { + forgetBloc.add( + StartTimerEvent()); + }, child: Text( - 'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime!=1? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}', + 'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}', style: TextStyle( - color: state is TimerState && - !state.isButtonEnabled + color: state + is TimerState && + !state + .isButtonEnabled ? Colors.grey - : ColorsManager.btnColor, + : ColorsManager + .btnColor, ), ), ), ), ), ), - style: - const TextStyle(color: Colors.black), + style: const TextStyle( + color: Colors.black), ), ), - if (forgetBloc.forgetValidate != '') // Check if there is a validation message + if (forgetBloc.forgetValidate != + '') // Check if there is a validation message Padding( - padding: const EdgeInsets.only(top: 8.0), + padding: + const EdgeInsets.only(top: 8.0), child: Text( forgetBloc.forgetValidate, style: const TextStyle( - color: ColorsManager.red, - fontSize: 10, - fontWeight: FontWeight.w700 - ), + color: ColorsManager.red, + fontSize: 10, + fontWeight: FontWeight.w700), ), ), ], ), const SizedBox(height: 20.0), Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text( "Password", - style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 14,fontWeight: FontWeight.w400), + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + fontSize: 14, + fontWeight: FontWeight.w400), ), const SizedBox(height: 10), SizedBox( child: TextFormField( - validator: forgetBloc.passwordValidator, - keyboardType: TextInputType.visiblePassword, - controller: forgetBloc.forgetPasswordController, - decoration: textBoxDecoration()!.copyWith( + validator: + forgetBloc.passwordValidator, + keyboardType: + TextInputType.visiblePassword, + controller: forgetBloc + .forgetPasswordController, + decoration: + textBoxDecoration()!.copyWith( hintText: 'At least 8 characters', ), - style: const TextStyle(color: Colors.black), + style: const TextStyle( + color: Colors.black), ), ), ], @@ -289,17 +332,22 @@ class ForgetPasswordWebPage extends StatelessWidget { ), const SizedBox(height: 20.0), Row( - crossAxisAlignment: CrossAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: size.width * 0.2, child: DefaultButton( - backgroundColor: ColorsManager.btnColor, + backgroundColor: + ColorsManager.btnColor, child: const Text('Submit'), onPressed: () { - if (forgetBloc.forgetFormKey.currentState!.validate()) { - forgetBloc.add(ChangePasswordEvent()); + if (forgetBloc + .forgetFormKey.currentState! + .validate()) { + forgetBloc + .add(ChangePasswordEvent()); } }, ), @@ -321,8 +369,10 @@ class ForgetPasswordWebPage extends StatelessWidget { SizedBox( width: size.width * 0.2, child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, children: [ const Flexible( child: Text( diff --git a/lib/pages/auth/view/login_mobile_page.dart b/lib/pages/auth/view/login_mobile_page.dart index f86ec301..1413db5d 100644 --- a/lib/pages/auth/view/login_mobile_page.dart +++ b/lib/pages/auth/view/login_mobile_page.dart @@ -27,7 +27,7 @@ class LoginMobilePage extends StatelessWidget { // Navigate to home screen after successful login Navigator.pushReplacement( context, - MaterialPageRoute(builder: (context) => HomePage()), + MaterialPageRoute(builder: (context) => HomePage()), ); } else if (state is LoginFailure) { // Show error message @@ -148,14 +148,14 @@ class LoginMobilePage extends StatelessWidget { ), isDense: true, style: const TextStyle(color: Colors.black), - items:loginBloc.regionList!.map((RegionModel region) { + items: loginBloc.regionList! + .map((RegionModel region) { return DropdownMenuItem( value: region.name, child: Text(region.name), ); }).toList(), - onChanged: (String? value) { - }, + onChanged: (String? value) {}, ), ) ], @@ -194,7 +194,8 @@ class LoginMobilePage extends StatelessWidget { validator: loginBloc.validatePassword, obscureText: loginBloc.obscureText, keyboardType: TextInputType.visiblePassword, - controller: loginBloc.loginPasswordController, + controller: + loginBloc.loginPasswordController, decoration: textBoxDecoration()!.copyWith( hintText: 'At least 8 characters', ), @@ -220,7 +221,8 @@ class LoginMobilePage extends StatelessWidget { }, child: Text( "Forgot Password?", - style: Theme.of(context).textTheme.bodySmall, + style: + Theme.of(context).textTheme.bodySmall, ), ), ], @@ -295,12 +297,15 @@ class LoginMobilePage extends StatelessWidget { : ColorsManager.grayColor, child: const Text('Sign in'), onPressed: () { - if (loginBloc.loginFormKey.currentState!.validate()) { + if (loginBloc.loginFormKey.currentState! + .validate()) { loginBloc.add( LoginButtonPressed( - regionUuid:'' , - username: loginBloc.loginEmailController.text, - password: loginBloc.loginPasswordController.text, + regionUuid: '', + username: + loginBloc.loginEmailController.text, + password: loginBloc + .loginPasswordController.text, ), ); } diff --git a/lib/pages/auth/view/login_page.dart b/lib/pages/auth/view/login_page.dart index 92487fe4..31907c68 100644 --- a/lib/pages/auth/view/login_page.dart +++ b/lib/pages/auth/view/login_page.dart @@ -9,8 +9,6 @@ class LoginPage extends StatelessWidget { @override Widget build(BuildContext context) { return const ResponsiveLayout( - desktopBody: LoginWebPage(), - mobileBody:LoginWebPage() - ); + desktopBody: LoginWebPage(), mobileBody: LoginWebPage()); } } diff --git a/lib/pages/auth/view/login_web_page.dart b/lib/pages/auth/view/login_web_page.dart index 893c3a86..cce4f1a8 100644 --- a/lib/pages/auth/view/login_web_page.dart +++ b/lib/pages/auth/view/login_web_page.dart @@ -24,7 +24,6 @@ class LoginWebPage extends StatefulWidget { } class _LoginWebPageState extends State { - @override Widget build(BuildContext context) { return Scaffold( @@ -36,7 +35,7 @@ class _LoginWebPageState extends State { // Navigate to home screen after successful login Navigator.pushReplacement( context, - MaterialPageRoute(builder: (context) => HomePage()), + MaterialPageRoute(builder: (context) => HomePage()), ); } else if (state is LoginFailure) { // Show error message @@ -48,26 +47,28 @@ class _LoginWebPageState extends State { } }, builder: (context, state) { - return _buildLoginForm(context,state); + return _buildLoginForm(context, state); }, ), ), ); } - Widget _buildLoginForm(BuildContext context,AuthState state) { + Widget _buildLoginForm(BuildContext context, AuthState state) { final loginBloc = BlocProvider.of(context); - Size size = MediaQuery.of(context).size; + Size size = MediaQuery.of(context).size; late ScrollController _scrollController; _scrollController = ScrollController(); void _scrollToCenter() { - final double middlePosition = _scrollController.position.maxScrollExtent / 2; + final double middlePosition = + _scrollController.position.maxScrollExtent / 2; _scrollController.animateTo( middlePosition, duration: const Duration(seconds: 1), curve: Curves.easeInOut, ); } + WidgetsBinding.instance.addPostFrameCallback((_) { _scrollToCenter(); }); @@ -80,14 +81,14 @@ class _LoginWebPageState extends State { shrinkWrap: true, children: [ Container( - padding: EdgeInsets.all(size.width*0.02) , - margin: EdgeInsets.all(size.width*0.09), + padding: EdgeInsets.all(size.width * 0.02), + margin: EdgeInsets.all(size.width * 0.09), decoration: BoxDecoration( color: Colors.black.withOpacity(0.3), borderRadius: const BorderRadius.all(Radius.circular(20)), ), child: Center( - child:Row( + child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -101,92 +102,140 @@ class _LoginWebPageState extends State { const Spacer(), Expanded( flex: 3, - child: Container( + child: Container( decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), - borderRadius: const BorderRadius.all(Radius.circular(30)), - border: Border.all(color: ColorsManager.graysColor.withOpacity(0.2))), + borderRadius: const BorderRadius.all( + Radius.circular(30)), + border: Border.all( + color: ColorsManager.graysColor + .withOpacity(0.2))), child: Form( key: loginBloc.loginFormKey, child: Padding( - padding: EdgeInsets.symmetric( - horizontal: size.width*0.02, - vertical: size.width*0.003), + padding: EdgeInsets.symmetric( + horizontal: size.width * 0.02, + vertical: size.width * 0.003), child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + crossAxisAlignment: + CrossAxisAlignment.start, children: [ const SizedBox(height: 40), - Text( - 'Login', - style:Theme.of(context).textTheme.headlineLarge), - SizedBox(height: size.height*0.03), + Text('Login', + style: Theme.of(context) + .textTheme + .headlineLarge), + SizedBox(height: size.height * 0.03), Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.start, children: [ Text( "Country/Region", - style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 14,fontWeight: FontWeight.w400), + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + fontSize: 14, + fontWeight: + FontWeight.w400), + ), + const SizedBox( + height: 10, ), - const SizedBox(height: 10,), - SizedBox( - child: DropdownButtonFormField( + child: DropdownButtonFormField< + String>( padding: EdgeInsets.zero, - value: loginBloc.regionList!.any((region) => region.id == loginBloc.regionUuid) + value: loginBloc.regionList! + .any((region) => + region.id == + loginBloc + .regionUuid) ? loginBloc.regionUuid : null, - - validator: loginBloc.validateRegion, + validator: + loginBloc.validateRegion, icon: const Icon( - Icons.keyboard_arrow_down_outlined, + Icons + .keyboard_arrow_down_outlined, ), - decoration: textBoxDecoration()!.copyWith( - errorStyle: const TextStyle(height: 0), + decoration: textBoxDecoration()! + .copyWith( + errorStyle: const TextStyle( + height: 0), hintText: null, ), hint: SizedBox( width: size.width * 0.12, - child: Align( - alignment: Alignment.centerLeft, + child: Align( + alignment: + Alignment.centerLeft, child: Text( 'Select your region/country', - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: ColorsManager.grayColor, - fontWeight: FontWeight.w400), - overflow: TextOverflow.ellipsis, + textAlign: + TextAlign.center, + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: + ColorsManager + .grayColor, + fontWeight: + FontWeight + .w400), + overflow: + TextOverflow.ellipsis, ), ), ), isDense: true, - style: const TextStyle(color: Colors.black), - items: loginBloc.regionList!.map((RegionModel region) { - return DropdownMenuItem( + style: const TextStyle( + color: Colors.black), + items: loginBloc.regionList! + .map((RegionModel region) { + return DropdownMenuItem< + String>( value: region.id, child: SizedBox( - width: size.width*0.08, - child: Text(region.name)), + width: + size.width * 0.08, + child: + Text(region.name)), ); }).toList(), onChanged: (String? value) { - loginBloc.add(CheckEnableEvent()); - loginBloc.add(SelectRegionEvent(val: value!)); + loginBloc + .add(CheckEnableEvent()); + loginBloc.add( + SelectRegionEvent( + val: value!)); }, ), ) - - ], ), const SizedBox(height: 20.0), Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.start, children: [ - Text("Email", - style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 14,fontWeight: FontWeight.w400), + Text( + "Email", + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + fontSize: 14, + fontWeight: + FontWeight.w400), ), const SizedBox( height: 10, @@ -194,30 +243,53 @@ class _LoginWebPageState extends State { SizedBox( child: TextFormField( onChanged: (value) { - loginBloc.add(CheckEnableEvent()); - // print(loginBloc.checkEnable()); + loginBloc + .add(CheckEnableEvent()); + // print(loginBloc.checkEnable()); }, - validator:loginBloc.loginValidateEmail , - controller:loginBloc.loginEmailController, - decoration: textBoxDecoration()!.copyWith( - errorStyle: const TextStyle(height: 0), // Hide the error text space - hintText: 'Enter your email address', - hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( - color: ColorsManager.grayColor, - fontWeight: FontWeight.w400) - ), - style: const TextStyle(color: Colors.black), + validator: loginBloc + .loginValidateEmail, + controller: loginBloc + .loginEmailController, + decoration: textBoxDecoration()! + .copyWith( + errorStyle: const TextStyle( + height: + 0), // Hide the error text space + hintText: + 'Enter your email address', + hintStyle: Theme.of( + context) + .textTheme + .bodySmall! + .copyWith( + color: ColorsManager + .grayColor, + fontWeight: + FontWeight + .w400)), + style: const TextStyle( + color: Colors.black), ), ), ], ), const SizedBox(height: 20.0), Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.start, children: [ - Text("Password", - style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 14,fontWeight: FontWeight.w400), + Text( + "Password", + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + fontSize: 14, + fontWeight: + FontWeight.w400), ), const SizedBox( height: 10, @@ -225,33 +297,54 @@ class _LoginWebPageState extends State { SizedBox( child: TextFormField( onChanged: (value) { - loginBloc.add(CheckEnableEvent()); - }, - validator:loginBloc.validatePassword, - obscureText:loginBloc.obscureText, - keyboardType: TextInputType.visiblePassword, - controller:loginBloc.loginPasswordController, - decoration: textBoxDecoration()!.copyWith( - hintText: 'At least 8 characters', - hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( - color: ColorsManager.grayColor, - fontWeight: FontWeight.w400), - suffixIcon: IconButton(onPressed: () { - loginBloc.add(PasswordVisibleEvent(newValue: loginBloc.obscureText)); + loginBloc + .add(CheckEnableEvent()); + }, + validator: + loginBloc.validatePassword, + obscureText: + loginBloc.obscureText, + keyboardType: TextInputType + .visiblePassword, + controller: loginBloc + .loginPasswordController, + decoration: textBoxDecoration()! + .copyWith( + hintText: + 'At least 8 characters', + hintStyle: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: ColorsManager + .grayColor, + fontWeight: + FontWeight.w400), + suffixIcon: IconButton( + onPressed: () { + loginBloc.add( + PasswordVisibleEvent( + newValue: loginBloc + .obscureText)); }, - icon: SizedBox( - child: SvgPicture.asset( - loginBloc.obscureText? - Assets.visiblePassword : - Assets.invisiblePassword, - height: 15, - width: 15, - ), + icon: SizedBox( + child: SvgPicture.asset( + loginBloc.obscureText + ? Assets + .visiblePassword + : Assets + .invisiblePassword, + height: 15, + width: 15, ), ), - errorStyle: const TextStyle(height: 0), // Hide the error text space + ), + errorStyle: const TextStyle( + height: + 0), // Hide the error text space ), - style: const TextStyle(color: Colors.black), + style: const TextStyle( + color: Colors.black), ), ), ], @@ -261,15 +354,27 @@ class _LoginWebPageState extends State { ), SizedBox( child: Row( - mainAxisAlignment: MainAxisAlignment.end, + mainAxisAlignment: + MainAxisAlignment.end, children: [ InkWell( onTap: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) => const ForgetPasswordPage(),)); + Navigator.of(context) + .push(MaterialPageRoute( + builder: (context) => + const ForgetPasswordPage(), + )); }, child: Text( "Forgot Password?", - style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black,fontSize: 14,fontWeight: FontWeight.w400), + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: Colors.black, + fontSize: 14, + fontWeight: + FontWeight.w400), ), ), ], @@ -281,53 +386,65 @@ class _LoginWebPageState extends State { Row( children: [ Transform.scale( - scale: 1.2, // Adjust the scale as needed + scale: + 1.2, // Adjust the scale as needed child: Checkbox( - fillColor: MaterialStateProperty.all(Colors.white), + fillColor: MaterialStateProperty + .all(Colors.white), activeColor: Colors.white, - value:loginBloc.isChecked, + value: loginBloc.isChecked, checkColor: Colors.black, shape: const CircleBorder(), onChanged: (bool? newValue) { - loginBloc.add(CheckBoxEvent(newValue: newValue)); + loginBloc.add(CheckBoxEvent( + newValue: newValue)); }, ), ), SizedBox( - width:size.width * 0.14, + width: size.width * 0.14, child: RichText( text: TextSpan( text: 'Agree to ', - style: const TextStyle(color: Colors.white), + style: const TextStyle( + color: Colors.white), children: [ TextSpan( - text: '(Terms of Service)', + text: + '(Terms of Service)', style: const TextStyle( - color: Colors.black,), - recognizer: TapGestureRecognizer() - ..onTap = () { - loginBloc.launchURL( - 'https://example.com/terms'); - }, + color: Colors.black, + ), + recognizer: + TapGestureRecognizer() + ..onTap = () { + loginBloc.launchURL( + 'https://example.com/terms'); + }, ), TextSpan( - text: ' (Legal Statement)', - style: const TextStyle(color: Colors.black), - recognizer: TapGestureRecognizer() - ..onTap = () { - loginBloc.launchURL( - 'https://example.com/legal'); - }, - ), - TextSpan( - text: ' (Privacy Statement)', + text: + ' (Legal Statement)', style: const TextStyle( color: Colors.black), - recognizer: TapGestureRecognizer() - ..onTap = () { - loginBloc.launchURL( - 'https://example.com/privacy'); - }, + recognizer: + TapGestureRecognizer() + ..onTap = () { + loginBloc.launchURL( + 'https://example.com/legal'); + }, + ), + TextSpan( + text: + ' (Privacy Statement)', + style: const TextStyle( + color: Colors.black), + recognizer: + TapGestureRecognizer() + ..onTap = () { + loginBloc.launchURL( + 'https://example.com/privacy'); + }, ), ], ), @@ -337,57 +454,87 @@ class _LoginWebPageState extends State { ), const SizedBox(height: 20.0), Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, children: [ SizedBox( - width:size.width * 0.2, + width: size.width * 0.2, child: DefaultButton( - enabled: loginBloc.checkValidate, - child:Text('Sign in', - style: Theme.of(context).textTheme.labelLarge !.copyWith( - fontSize: 14, - color: - loginBloc.checkValidate ? - ColorsManager.whiteColors:ColorsManager.whiteColors.withOpacity(0.2), - ) - ), + enabled: + loginBloc.checkValidate, + child: Text('Sign in', + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith( + fontSize: 14, + color: loginBloc + .checkValidate + ? ColorsManager + .whiteColors + : ColorsManager + .whiteColors + .withOpacity( + 0.2), + )), onPressed: () { - if(loginBloc.loginFormKey.currentState!.validate() ){ - loginBloc.add(LoginButtonPressed( - regionUuid:loginBloc.regionUuid, - username: loginBloc.loginEmailController.text, - password: loginBloc.loginPasswordController.text, + if (loginBloc.loginFormKey + .currentState! + .validate()) { + loginBloc + .add(LoginButtonPressed( + regionUuid: + loginBloc.regionUuid, + username: loginBloc + .loginEmailController + .text, + password: loginBloc + .loginPasswordController + .text, )); - }else{ - loginBloc.add(ChangeValidateEvent()); + } else { + loginBloc.add( + ChangeValidateEvent()); } - }, + }, ), ), ], ), const SizedBox(height: 15.0), Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ SizedBox(child: Text(loginBloc.validate, - style: const TextStyle(fontWeight: FontWeight.w700,color: ColorsManager.red ),),)],) + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + SizedBox( + child: Text( + loginBloc.validate, + style: const TextStyle( + fontWeight: FontWeight.w700, + color: ColorsManager.red), + ), + ) + ], + ) ], ), ), - ) - )), + ))), const Spacer(), ], - ),), + ), + ), ), ], ), ), ), - if (state is AuthLoading) - const Center(child: CircularProgressIndicator()) + if (state is AuthLoading) + const Center(child: CircularProgressIndicator()) ], ); } diff --git a/lib/pages/common/buttons/default_button.dart b/lib/pages/common/buttons/default_button.dart index 181e093a..5aa506f8 100644 --- a/lib/pages/common/buttons/default_button.dart +++ b/lib/pages/common/buttons/default_button.dart @@ -38,60 +38,58 @@ class DefaultButton extends StatelessWidget { style: isSecondary ? null : customButtonStyle ?? - ButtonStyle( - textStyle: MaterialStateProperty.all( - customTextStyle - ?? Theme.of(context).textTheme.bodySmall!.copyWith( - fontSize: 13, - color: foregroundColor, - fontWeight: FontWeight.normal - ), - ), - - foregroundColor: MaterialStateProperty.all( - isSecondary - ? Colors.black - : enabled - ? foregroundColor ?? Colors.white - : Colors.black, - ), - backgroundColor: MaterialStateProperty.resolveWith( + ButtonStyle( + textStyle: MaterialStateProperty.all( + customTextStyle ?? + Theme.of(context).textTheme.bodySmall!.copyWith( + fontSize: 13, + color: foregroundColor, + fontWeight: FontWeight.normal), + ), + foregroundColor: MaterialStateProperty.all( + isSecondary + ? Colors.black + : enabled + ? foregroundColor ?? Colors.white + : Colors.black, + ), + backgroundColor: MaterialStateProperty.resolveWith( (Set states) { return enabled ? backgroundColor ?? ColorsManager.primaryColor : Colors.black.withOpacity(0.2); }), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(borderRadius ?? 20), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius ?? 20), + ), + ), + fixedSize: MaterialStateProperty.all( + const Size.fromHeight(50), + ), + padding: MaterialStateProperty.all( + EdgeInsets.all(padding ?? 10), + ), + minimumSize: MaterialStateProperty.all( + const Size.fromHeight(50), + ), ), - ), - fixedSize: MaterialStateProperty.all( - const Size.fromHeight(50), - ), - padding: MaterialStateProperty.all( - EdgeInsets.all(padding ?? 10), - ), - minimumSize: MaterialStateProperty.all( - const Size.fromHeight(50), - ), - ), child: SizedBox( height: height ?? 50, child: Center( child: isLoading ? const SizedBox.square( - dimension: 24, - child: CircularProgressIndicator( - color: Colors.white, - ), - ) + dimension: 24, + child: CircularProgressIndicator( + color: Colors.white, + ), + ) : isDone - ? const Icon( - Icons.check_circle_outline, - color: Colors.white, - ) - : child, + ? const Icon( + Icons.check_circle_outline, + color: Colors.white, + ) + : child, ), ), ); diff --git a/lib/pages/common/custom_dialog.dart b/lib/pages/common/custom_dialog.dart index 3b667811..363d6900 100644 --- a/lib/pages/common/custom_dialog.dart +++ b/lib/pages/common/custom_dialog.dart @@ -11,7 +11,8 @@ Future showCustomDialog({ double? iconHeight, double? iconWidth, VoidCallback? onOkPressed, - bool barrierDismissible = false, required actions, + bool barrierDismissible = false, + required actions, }) { return showDialog( context: context, diff --git a/lib/pages/common/custom_table.dart b/lib/pages/common/custom_table.dart index 6b84a76e..63fdc28a 100644 --- a/lib/pages/common/custom_table.dart +++ b/lib/pages/common/custom_table.dart @@ -224,8 +224,14 @@ class _DynamicTableState extends State { alignment: Alignment.centerLeft, child: Text( content, - style: const TextStyle( - color: Colors.black, fontSize: 10, fontWeight: FontWeight.w400), + style: TextStyle( + color: content == 'Online' + ? ColorsManager.green + : content == 'Offline' + ? ColorsManager.red + : Colors.black, + fontSize: 12, + fontWeight: FontWeight.w400), ), ), ); diff --git a/lib/pages/common/date_time_widget.dart b/lib/pages/common/date_time_widget.dart index d8fbfe51..233ac772 100644 --- a/lib/pages/common/date_time_widget.dart +++ b/lib/pages/common/date_time_widget.dart @@ -1,7 +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'; class DateTimeWebWidget extends StatelessWidget { @@ -23,8 +22,8 @@ class DateTimeWebWidget extends StatelessWidget { final String firstString; final String secondString; final String icon; - final Function()? startTime; - final Function()? endTime; + final Function()? startTime; + final Function()? endTime; @override Widget build(BuildContext context) { @@ -32,61 +31,84 @@ class DateTimeWebWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - Row( - children: [ - if(isRequired) - Text( - '* ', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Colors.red), - ), - Text(title??'' , - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: Colors.black,fontSize: 13),), - ], - ), - const SizedBox(height: 8,), + Row( + children: [ + if (isRequired) + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + Text( + title, + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(color: Colors.black, fontSize: 13), + ), + ], + ), + const SizedBox( + height: 8, + ), Container( - height:size.height * 0.055 , - padding: EdgeInsets.only(top: 10,bottom: 10,right: 30,left: 10), + height: size.height * 0.055, + padding: + const EdgeInsets.only(top: 10, bottom: 10, right: 30, left: 10), decoration: containerDecoration, child: FittedBox( child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, 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, - ), - ], + InkWell( + onTap: startTime, + child: FittedBox( + child: Text( + firstString, + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: ColorsManager.grayColor, + fontSize: 12, + fontWeight: FontWeight.w400), + ), + )), + const SizedBox( + width: 30, + ), + const Icon(Icons.arrow_right_alt), + const 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), + ), + )), + const SizedBox( + width: 30, + ), + SvgPicture.asset( + icon, ), ], - )), + ), + ], + )), ), ], ); diff --git a/lib/pages/common/first_layer.dart b/lib/pages/common/first_layer.dart index a566f6d9..8941950e 100644 --- a/lib/pages/common/first_layer.dart +++ b/lib/pages/common/first_layer.dart @@ -4,7 +4,7 @@ import 'package:syncrow_web/utils/constants/assets.dart'; class FirstLayer extends StatelessWidget { final Widget? second; - const FirstLayer({super.key,this.second}); + const FirstLayer({super.key, this.second}); @override Widget build(BuildContext context) { diff --git a/lib/pages/common/hour_picker_dialog.dart b/lib/pages/common/hour_picker_dialog.dart index 718f1ebf..2c89b710 100644 --- a/lib/pages/common/hour_picker_dialog.dart +++ b/lib/pages/common/hour_picker_dialog.dart @@ -1,5 +1,3 @@ - - import 'package:flutter/material.dart'; class HourPickerDialog extends StatefulWidget { @@ -17,7 +15,9 @@ class _HourPickerDialogState extends State { @override void initState() { super.initState(); - _selectedHour = widget.initialTime.hour > 12 ? widget.initialTime.hour - 12 : widget.initialTime.hour; + _selectedHour = widget.initialTime.hour > 12 + ? widget.initialTime.hour - 12 + : widget.initialTime.hour; _isPm = widget.initialTime.period == DayPeriod.pm; } diff --git a/lib/pages/common/info_dialog.dart b/lib/pages/common/info_dialog.dart index cfd2cbd4..97656418 100644 --- a/lib/pages/common/info_dialog.dart +++ b/lib/pages/common/info_dialog.dart @@ -35,7 +35,6 @@ class InfoDialog extends StatelessWidget { width: 35, ), ), - Text( title, style: Theme.of(context).textTheme.headlineLarge!.copyWith( diff --git a/lib/pages/common/text_field/custom_text_field.dart b/lib/pages/common/text_field/custom_text_field.dart index abc26f22..587a1f4f 100644 --- a/lib/pages/common/text_field/custom_text_field.dart +++ b/lib/pages/common/text_field/custom_text_field.dart @@ -8,37 +8,25 @@ class StatefulTextField extends StatefulWidget { this.hintText = 'Please enter', required this.width, this.elevation = 0, + required this.controller, // Add the controller }); final String title; final String hintText; final double width; final double elevation; + final TextEditingController controller; @override State createState() => _StatefulTextFieldState(); } class _StatefulTextFieldState extends State { - late TextEditingController _controller; - - @override - void initState() { - super.initState(); - _controller = TextEditingController(); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { return CustomTextField( title: widget.title, - controller: _controller, + controller: widget.controller, hintText: widget.hintText, width: widget.width, elevation: widget.elevation, diff --git a/lib/pages/common/text_field/custom_web_textfield.dart b/lib/pages/common/text_field/custom_web_textfield.dart index 363a1994..cc29a423 100644 --- a/lib/pages/common/text_field/custom_web_textfield.dart +++ b/lib/pages/common/text_field/custom_web_textfield.dart @@ -1,23 +1,22 @@ - import 'package:flutter/material.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/style.dart'; + class CustomWebTextField extends StatelessWidget { - const CustomWebTextField({ + const CustomWebTextField({ super.key, - required this.isRequired, + required this.isRequired, required this.textFieldName, required this.controller, - this.description, - this.validator, + this.description, + this.validator, }); final bool isRequired; final String textFieldName; final String? description; final TextEditingController? controller; - final String? Function(String?)? validator; - + final String? Function(String?)? validator; @override Widget build(BuildContext context) { @@ -28,51 +27,59 @@ class CustomWebTextField extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - if(isRequired) - Row( - children: [ - Text('* ', - style: Theme.of(context) - .textTheme.bodyMedium! - .copyWith(color: Colors.red), - ), - Text(textFieldName, style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: Colors.black,fontSize: 13),), - ], + if (isRequired) + Row( + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + Text( + textFieldName, + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(color: Colors.black, fontSize: 13), + ), + ], + ), + const SizedBox( + width: 10, ), - const SizedBox(width: 10,), Expanded( child: Text( - description??'', - style: Theme.of(context) - .textTheme.bodySmall! - .copyWith(fontSize: 9, + description ?? '', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontSize: 9, fontWeight: FontWeight.w400, color: ColorsManager.textGray), ), ), ], ), - const SizedBox(height: 7,), + const SizedBox( + height: 7, + ), Container( - decoration: containerDecoration.copyWith( - color: const Color(0xFFF5F6F7), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.3), - spreadRadius:2, - blurRadius: 3, - offset: const Offset(1, 1), // changes position of shadow - ), - ] - ), + decoration: containerDecoration + .copyWith(color: const Color(0xFFF5F6F7), boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.3), + spreadRadius: 2, + blurRadius: 3, + offset: const Offset(1, 1), // changes position of shadow + ), + ]), child: TextFormField( validator: validator, controller: controller, style: const TextStyle(color: Colors.black), - decoration: textBoxDecoration()! - .copyWith( - errorStyle: const TextStyle(height: 0), // Hide the error text space + decoration: textBoxDecoration()!.copyWith( + errorStyle: + const TextStyle(height: 0), // Hide the error text space hintText: 'Please enter'), ), diff --git a/lib/pages/device_managment/bloc/device_managment_bloc.dart b/lib/pages/device_managment/bloc/device_managment_bloc.dart index d2b8c72c..edc6e7ba 100644 --- a/lib/pages/device_managment/bloc/device_managment_bloc.dart +++ b/lib/pages/device_managment/bloc/device_managment_bloc.dart @@ -1,13 +1,130 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/device_managment/models/devices_model.dart'; +import 'package:syncrow_web/services/devices_mang_api.dart'; part 'device_managment_event.dart'; part 'device_managment_state.dart'; -class DeviceManagmentBloc extends Bloc { - DeviceManagmentBloc() : super(DeviceManagmentInitial()) { - on((event, emit) { - // TODO: implement event handler - }); +class DeviceManagementBloc + extends Bloc { + int _selectedIndex = 0; + List _devices = []; + int _onlineCount = 0; + int _offlineCount = 0; + int _lowBatteryCount = 0; + + DeviceManagementBloc() : super(DeviceManagementInitial()) { + on(_onFetchDevices); + on(_onFilterDevices); + on(_onSelectedFilterChanged); + on(_onSearchDevices); + } + + Future _onFetchDevices( + FetchDevices event, Emitter emit) async { + emit(DeviceManagementLoading()); + try { + final devices = await DevicesManagementApi().fetchDevices(); + _devices = devices; + _calculateDeviceCounts(); + emit(DeviceManagementLoaded( + devices: devices, + selectedIndex: _selectedIndex, + onlineCount: _onlineCount, + offlineCount: _offlineCount, + lowBatteryCount: _lowBatteryCount, + )); + } catch (e) { + emit(DeviceManagementInitial()); + } + } + + void _onFilterDevices( + FilterDevices event, Emitter emit) { + if (_devices.isNotEmpty) { + final filteredDevices = _devices.where((device) { + switch (event.filter) { + case 'Online': + return device.online == true; + case 'Offline': + return device.online == false; + case 'Low Battery': + return device.batteryLevel != null && device.batteryLevel! < 20; + default: + return true; + } + }).toList(); + emit(DeviceManagementFiltered( + filteredDevices: filteredDevices, + selectedIndex: _selectedIndex, + onlineCount: _onlineCount, + offlineCount: _offlineCount, + lowBatteryCount: _lowBatteryCount, + )); + } + } + + void _onSelectedFilterChanged( + SelectedFilterChanged event, Emitter emit) { + _selectedIndex = event.selectedIndex; + add(FilterDevices(_getFilterFromIndex(_selectedIndex))); + } + + void _calculateDeviceCounts() { + _onlineCount = _devices.where((device) => device.online == true).length; + _offlineCount = _devices.where((device) => device.online == false).length; + _lowBatteryCount = _devices + .where((device) => + device.batteryLevel != null && device.batteryLevel! < 20) + .length; + } + + String _getFilterFromIndex(int index) { + switch (index) { + case 1: + return 'Online'; + case 2: + return 'Offline'; + case 3: + return 'Low Battery'; + default: + return 'All'; + } + } + + void _onSearchDevices( + SearchDevices event, Emitter emit) { + if (_devices.isNotEmpty) { + final filteredDevices = _devices.where((device) { + final matchesCommunity = event.community == null || + event.community!.isEmpty || + (device.room?.name + ?.toLowerCase() + .contains(event.community!.toLowerCase()) ?? + false); + final matchesUnit = event.unitName == null || + event.unitName!.isEmpty || + (device.unit?.name + ?.toLowerCase() + .contains(event.unitName!.toLowerCase()) ?? + false); + final matchesProductName = event.productName == null || + event.productName!.isEmpty || + (device.name + ?.toLowerCase() + .contains(event.productName!.toLowerCase()) ?? + false); + return matchesCommunity && matchesUnit && matchesProductName; + }).toList(); + + emit(DeviceManagementFiltered( + filteredDevices: filteredDevices, + selectedIndex: _selectedIndex, + onlineCount: _onlineCount, + offlineCount: _offlineCount, + lowBatteryCount: _lowBatteryCount, + )); + } } } diff --git a/lib/pages/device_managment/bloc/device_managment_event.dart b/lib/pages/device_managment/bloc/device_managment_event.dart index 2ea61dc2..a03b2659 100644 --- a/lib/pages/device_managment/bloc/device_managment_event.dart +++ b/lib/pages/device_managment/bloc/device_managment_event.dart @@ -1,8 +1,43 @@ part of 'device_managment_bloc.dart'; -sealed class DeviceManagmentEvent extends Equatable { - const DeviceManagmentEvent(); +abstract class DeviceManagementEvent extends Equatable { + const DeviceManagementEvent(); @override - List get props => []; + List get props => []; +} + +class FetchDevices extends DeviceManagementEvent {} + +class FilterDevices extends DeviceManagementEvent { + final String filter; + + const FilterDevices(this.filter); + + @override + List get props => [filter]; +} + +class SelectedFilterChanged extends DeviceManagementEvent { + final int selectedIndex; + + const SelectedFilterChanged(this.selectedIndex); + + @override + List get props => [selectedIndex]; +} + +class SearchDevices extends DeviceManagementEvent { + final String? community; + final String? unitName; + final String? productName; + + const SearchDevices({ + this.community, + this.unitName, + this.productName, + }); + + @override + List get props => [community, unitName, productName]; } diff --git a/lib/pages/device_managment/bloc/device_managment_state.dart b/lib/pages/device_managment/bloc/device_managment_state.dart index 49977564..4b2cacd7 100644 --- a/lib/pages/device_managment/bloc/device_managment_state.dart +++ b/lib/pages/device_managment/bloc/device_managment_state.dart @@ -1,10 +1,57 @@ part of 'device_managment_bloc.dart'; -sealed class DeviceManagmentState extends Equatable { - const DeviceManagmentState(); - +abstract class DeviceManagementState extends Equatable { + const DeviceManagementState(); + @override - List get props => []; + List get props => []; } -final class DeviceManagmentInitial extends DeviceManagmentState {} +class DeviceManagementInitial extends DeviceManagementState {} + +class DeviceManagementLoading extends DeviceManagementState {} + +class DeviceManagementLoaded extends DeviceManagementState { + final List devices; + final int selectedIndex; + final int onlineCount; + final int offlineCount; + final int lowBatteryCount; + + const DeviceManagementLoaded({ + required this.devices, + required this.selectedIndex, + required this.onlineCount, + required this.offlineCount, + required this.lowBatteryCount, + }); + + @override + List get props => + [devices, selectedIndex, onlineCount, offlineCount, lowBatteryCount]; +} + +class DeviceManagementFiltered extends DeviceManagementState { + final List filteredDevices; + final int selectedIndex; + final int onlineCount; + final int offlineCount; + final int lowBatteryCount; + + const DeviceManagementFiltered({ + required this.filteredDevices, + required this.selectedIndex, + required this.onlineCount, + required this.offlineCount, + required this.lowBatteryCount, + }); + + @override + List get props => [ + filteredDevices, + selectedIndex, + onlineCount, + offlineCount, + lowBatteryCount + ]; +} diff --git a/lib/pages/device_managment/models/devices_model.dart b/lib/pages/device_managment/models/devices_model.dart new file mode 100644 index 00000000..5ee2d05f --- /dev/null +++ b/lib/pages/device_managment/models/devices_model.dart @@ -0,0 +1,157 @@ +import 'package:syncrow_web/pages/device_managment/models/room.dart'; +import 'package:syncrow_web/pages/device_managment/models/unit.dart'; + +class AllDevicesModel { +/* +{ + "room": { + "uuid": "75ea7d60-5104-4726-b5f8-ea426c0c6a1b", + "name": "Room 1" + }, + "unit": { + "uuid": "04fd1dcf-f24a-40db-970d-d0be884ed30f", + "name": "unit 1" + }, + "productUuid": "894aad5c-ce03-423a-9d61-2fd0c3f67ebf", + "productType": "3G", + "permissionType": "CONTROLLABLE", + "activeTime": 1722173778, + "category": "kg", + "categoryName": "Switch", + "createTime": 1722173778, + "gatewayId": "bf0294123ed2c19067skrk", + "icon": "smart/icon/bay1642572935385vcsA/2b1f5efbaa5bbf81c3164fa312cf2032.png", + "ip": "", + "lat": "31.97", + "localKey": "T/39+:9M", + "lon": "35.89", + "model": "S01ZLSWBSA3", + "name": "3 Gang Button Switch L-L", + "nodeId": "60a423fffed5a7f6", + "online": true, + "ownerId": "199200732", + "sub": true, + "timeZone": "+03:00", + "updateTime": 1723626515, + "uuid": "5b31dae4-ce9c-4c70-b52b-7e15011163bf" +} +*/ + + DevicesModelRoom? room; + DevicesModelUnit? unit; + String? productUuid; + String? productType; + String? permissionType; + int? activeTime; + String? category; + String? categoryName; + int? createTime; + String? gatewayId; + String? icon; + String? ip; + String? lat; + String? localKey; + String? lon; + String? model; + String? name; + String? nodeId; + bool? online; + String? ownerId; + bool? sub; + String? timeZone; + int? updateTime; + String? uuid; + int? batteryLevel; + + AllDevicesModel({ + this.room, + this.unit, + this.productUuid, + this.productType, + this.permissionType, + this.activeTime, + this.category, + this.categoryName, + this.createTime, + this.gatewayId, + this.icon, + this.ip, + this.lat, + this.localKey, + this.lon, + this.model, + this.name, + this.nodeId, + this.online, + this.ownerId, + this.sub, + this.timeZone, + this.updateTime, + this.uuid, + this.batteryLevel, + }); + AllDevicesModel.fromJson(Map json) { + room = (json['room'] != null && (json['room'] is Map)) + ? DevicesModelRoom.fromJson(json['room']) + : null; + unit = (json['unit'] != null && (json['unit'] is Map)) + ? DevicesModelUnit.fromJson(json['unit']) + : null; + productUuid = json['productUuid']?.toString(); + productType = json['productType']?.toString(); + permissionType = json['permissionType']?.toString(); + activeTime = int.tryParse(json['activeTime']?.toString() ?? ''); + category = json['category']?.toString(); + categoryName = json['categoryName']?.toString(); + createTime = int.tryParse(json['createTime']?.toString() ?? ''); + gatewayId = json['gatewayId']?.toString(); + icon = json['icon']?.toString(); + ip = json['ip']?.toString(); + lat = json['lat']?.toString(); + localKey = json['localKey']?.toString(); + lon = json['lon']?.toString(); + model = json['model']?.toString(); + name = json['name']?.toString(); + nodeId = json['nodeId']?.toString(); + online = json['online']; + ownerId = json['ownerId']?.toString(); + sub = json['sub']; + timeZone = json['timeZone']?.toString(); + updateTime = int.tryParse(json['updateTime']?.toString() ?? ''); + uuid = json['uuid']?.toString(); + batteryLevel = int.tryParse(json['batteryLevel']?.toString() ?? ''); + } + Map toJson() { + final data = {}; + if (room != null) { + data['room'] = room!.toJson(); + } + if (unit != null) { + data['unit'] = unit!.toJson(); + } + data['productUuid'] = productUuid; + data['productType'] = productType; + data['permissionType'] = permissionType; + data['activeTime'] = activeTime; + data['category'] = category; + data['categoryName'] = categoryName; + data['createTime'] = createTime; + data['gatewayId'] = gatewayId; + data['icon'] = icon; + data['ip'] = ip; + data['lat'] = lat; + data['localKey'] = localKey; + data['lon'] = lon; + data['model'] = model; + data['name'] = name; + data['nodeId'] = nodeId; + data['online'] = online; + data['ownerId'] = ownerId; + data['sub'] = sub; + data['timeZone'] = timeZone; + data['updateTime'] = updateTime; + data['uuid'] = uuid; + data['batteryLevel'] = batteryLevel; + return data; + } +} diff --git a/lib/pages/device_managment/models/room.dart b/lib/pages/device_managment/models/room.dart new file mode 100644 index 00000000..9f0f1268 --- /dev/null +++ b/lib/pages/device_managment/models/room.dart @@ -0,0 +1,26 @@ +class DevicesModelRoom { +/* +{ + "uuid": "75ea7d60-5104-4726-b5f8-ea426c0c6a1b", + "name": "Room 1" +} +*/ + + String? uuid; + String? name; + + DevicesModelRoom({ + this.uuid, + this.name, + }); + DevicesModelRoom.fromJson(Map json) { + uuid = json['uuid']?.toString(); + name = json['name']?.toString(); + } + Map toJson() { + final data = {}; + data['uuid'] = uuid; + data['name'] = name; + return data; + } +} diff --git a/lib/pages/device_managment/models/unit.dart b/lib/pages/device_managment/models/unit.dart new file mode 100644 index 00000000..bea9936f --- /dev/null +++ b/lib/pages/device_managment/models/unit.dart @@ -0,0 +1,26 @@ +class DevicesModelUnit { +/* +{ + "uuid": "04fd1dcf-f24a-40db-970d-d0be884ed30f", + "name": "unit 1" +} +*/ + + String? uuid; + String? name; + + DevicesModelUnit({ + this.uuid, + this.name, + }); + DevicesModelUnit.fromJson(Map json) { + uuid = json['uuid']?.toString(); + name = json['name']?.toString(); + } + Map toJson() { + final data = {}; + data['uuid'] = uuid; + data['name'] = name; + return data; + } +} diff --git a/lib/pages/device_managment/view/device_managment_page.dart b/lib/pages/device_managment/view/device_managment_page.dart index 6891e943..5bb26e1c 100644 --- a/lib/pages/device_managment/view/device_managment_page.dart +++ b/lib/pages/device_managment/view/device_managment_page.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/bloc/device_managment_bloc.dart'; import 'package:syncrow_web/pages/device_managment/widgets/device_managment_body.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; @@ -7,17 +9,31 @@ class DeviceManagementPage extends StatelessWidget { @override Widget build(BuildContext context) { - return WebScaffold( - enableMenuSideba: true, - appBarTitle: Row( - children: [ - Text( - 'Device Management', - style: Theme.of(context).textTheme.headlineLarge, - ) - ], + return BlocProvider( + create: (context) => DeviceManagementBloc()..add(FetchDevices()), + child: WebScaffold( + appBarTitle: Text( + 'Device Management', + style: Theme.of(context).textTheme.headlineLarge, + ), + enableMenuSideba: true, + scaffoldBody: BlocBuilder( + builder: (context, state) { + if (state is DeviceManagementLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is DeviceManagementLoaded || + state is DeviceManagementFiltered) { + final devices = state is DeviceManagementLoaded + ? state.devices + : (state as DeviceManagementFiltered).filteredDevices; + + return DeviceManagementBody(devices: devices); + } else { + return const Center(child: Text('No Devices Found')); + } + }, + ), ), - scaffoldBody: const DeviceManagementBody(), ); } } diff --git a/lib/pages/device_managment/widgets/device_managment_body.dart b/lib/pages/device_managment/widgets/device_managment_body.dart index a3c2a807..fd72b0ae 100644 --- a/lib/pages/device_managment/widgets/device_managment_body.dart +++ b/lib/pages/device_managment/widgets/device_managment_body.dart @@ -1,92 +1,94 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/core/extension/build_context_x.dart'; -import 'package:syncrow_web/pages/common/buttons/search_reset_buttons.dart'; +import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/custom_table.dart'; import 'package:syncrow_web/pages/common/filter/filter_widget.dart'; -import 'package:syncrow_web/pages/common/text_field/custom_text_field.dart'; +import 'package:syncrow_web/pages/device_managment/bloc/device_managment_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/models/devices_model.dart'; +import 'package:syncrow_web/pages/device_managment/widgets/device_search_filters.dart'; +import 'package:syncrow_web/utils/format_date_time.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/style.dart'; class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { - const DeviceManagementBody({super.key}); + const DeviceManagementBody({super.key, required this.devices}); + + final List devices; @override Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(30), - height: context.screenHeight, - width: context.screenWidth, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FilterWidget( - size: context.screenSize, - tabs: ['All', 'Online', 'Offline', 'Low Battery'], - selectedIndex: 0, - onTabChanged: (index) {}, - ), - const SizedBox( - height: 20, - ), - if (isLargeScreenSize(context)) ...[ - Row( - children: [ - const StatefulTextField( - title: "Community", - width: 200, - elevation: 2, + return BlocBuilder( + builder: (context, state) { + List devicesToShow = []; + int selectedIndex = 0; + int onlineCount = 0; + int offlineCount = 0; + int lowBatteryCount = 0; + + if (state is DeviceManagementLoaded) { + devicesToShow = state.devices; + selectedIndex = state.selectedIndex; + onlineCount = state.onlineCount; + offlineCount = state.offlineCount; + lowBatteryCount = state.lowBatteryCount; + } else if (state is DeviceManagementFiltered) { + devicesToShow = state.filteredDevices; + selectedIndex = state.selectedIndex; + onlineCount = state.onlineCount; + offlineCount = state.offlineCount; + lowBatteryCount = state.lowBatteryCount; + } + + // Create tab labels with counts + final tabs = [ + 'All (${devices.length})', + 'Online ($onlineCount)', + 'Offline ($offlineCount)', + 'Low Battery ($lowBatteryCount)', + ]; + + return Container( + padding: const EdgeInsets.all(30), + height: context.screenHeight, + width: context.screenWidth, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FilterWidget( + size: MediaQuery.of(context).size, + tabs: tabs, + selectedIndex: selectedIndex, + onTabChanged: (index) { + context + .read() + .add(SelectedFilterChanged(index)); + }, + ), + const SizedBox( + height: 20, + ), + const DeviceSearchFilters(), + const SizedBox( + height: 12, + ), + Container( + height: 43, + width: 100, + decoration: containerDecoration, + child: Center( + child: DefaultButton( + onPressed: () {}, + borderRadius: 9, + child: const Text('Control'), + ), ), - const SizedBox(width: 20), - const StatefulTextField( - title: "Unit Name", - width: 200, - elevation: 2, - ), - const SizedBox(width: 20), - const StatefulTextField( - title: "Device Name / Product Name", - width: 300, - elevation: 2, - ), - const SizedBox(width: 20), - SearchResetButtons( - onSearch: () {}, - onReset: () {}, - ), - ], - ), - ] else ...[ - Wrap( - spacing: 20, - runSpacing: 10, - children: [ - const StatefulTextField( - title: "Community", - width: 200, - elevation: 2, - ), - const StatefulTextField( - title: "Unit Name", - width: 200, - elevation: 2, - ), - const StatefulTextField( - title: "Device Name / Product Name", - width: 300, - elevation: 2, - ), - SearchResetButtons( - onSearch: () {}, - onReset: () {}, - ), - ], - ), - ], - const SizedBox( - height: 12, - ), - Expanded( - child: DynamicTable( + ), + const SizedBox( + height: 12, + ), + Expanded( + child: DynamicTable( cellDecoration: containerDecoration, selectAll: (p0) { // visitorBloc.selectedDeviceIds.clear(); @@ -101,29 +103,38 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { withCheckBox: true, size: context.screenSize, headers: const [ - 'Device Name', - 'Product Name', - 'Device ID', - 'Unit Name', - 'Room', - 'Battery Level', - 'Installation Date and Time', - 'Status', - 'Last Offline Date and Time', - ], - data: [] - // state.data.map((item) { - // return [ - // item.name.toString(), - // item.uuid.toString(), - // item.productType.toString(), - // '', - // item.online.value.toString(), - // ]; - // }).toList(), - )), - ], - ), + 'Device Name', + 'Product Name', + 'Device ID', + 'Unit Name', + 'Room', + 'Battery Level', + 'Installation Date and Time', + 'Status', + 'Last Offline Date and Time', + ], + data: devicesToShow.map((device) { + return [ + device.categoryName ?? '', + device.name ?? '', + device.uuid ?? '', + device.unit?.name ?? '', + device.room?.name ?? '', + device.batteryLevel?.toString() ?? '', + formatDateTime(DateTime.fromMillisecondsSinceEpoch( + device.createTime ?? 0)), + device.online == true ? 'Online' : 'Offline', + formatDateTime(DateTime.fromMillisecondsSinceEpoch( + device.updateTime ?? 0)), + ]; + }).toList(), + isEmpty: devicesToShow.isEmpty, + ), + ), + ], + ), + ); + }, ); } } diff --git a/lib/pages/device_managment/widgets/device_search_filters.dart b/lib/pages/device_managment/widgets/device_search_filters.dart new file mode 100644 index 00000000..988287f4 --- /dev/null +++ b/lib/pages/device_managment/widgets/device_search_filters.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/common/text_field/custom_text_field.dart'; +import 'package:syncrow_web/pages/device_managment/bloc/device_managment_bloc.dart'; +import 'package:syncrow_web/pages/common/buttons/search_reset_buttons.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; + +class DeviceSearchFilters extends StatefulWidget { + const DeviceSearchFilters({super.key}); + + @override + State createState() => _DeviceSearchFiltersState(); +} + +class _DeviceSearchFiltersState extends State + with HelperResponsiveLayout { + final TextEditingController communityController = TextEditingController(); + final TextEditingController unitNameController = TextEditingController(); + final TextEditingController productNameController = TextEditingController(); + + @override + void dispose() { + communityController.dispose(); + unitNameController.dispose(); + productNameController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return isLargeScreenSize(context) + ? Row( + children: [ + _buildSearchField("Community", communityController, 200), + const SizedBox(width: 20), + _buildSearchField("Unit Name", unitNameController, 200), + const SizedBox(width: 20), + _buildSearchField( + "Device Name / Product Name", productNameController, 300), + const SizedBox(width: 20), + _buildSearchResetButtons(), + ], + ) + : Wrap( + spacing: 20, + runSpacing: 10, + children: [ + _buildSearchField("Community", communityController, 200), + _buildSearchField("Unit Name", unitNameController, 200), + _buildSearchField( + "Device Name / Product Name", productNameController, 300), + _buildSearchResetButtons(), + ], + ); + } + + Widget _buildSearchField( + String title, TextEditingController controller, double width) { + return StatefulTextField( + title: title, + width: width, + elevation: 2, + controller: controller, + ); + } + + Widget _buildSearchResetButtons() { + return SearchResetButtons( + onSearch: () { + context.read().add(SearchDevices( + community: communityController.text, + unitName: unitNameController.text, + productName: productNameController.text, + )); + }, + onReset: () { + communityController.clear(); + unitNameController.clear(); + productNameController.clear(); + context.read().add(FetchDevices()); + }, + ); + } +} diff --git a/lib/pages/home/bloc/home_state.dart b/lib/pages/home/bloc/home_state.dart index 7a1be0be..dda3fa50 100644 --- a/lib/pages/home/bloc/home_state.dart +++ b/lib/pages/home/bloc/home_state.dart @@ -25,6 +25,7 @@ class HomeUpdateTree extends HomeState { @override List get props => [graph, builder]; } + class HomeUserInfoLoaded extends HomeState { final UserModel user; diff --git a/lib/pages/home/home_model/home_item_model.dart b/lib/pages/home/home_model/home_item_model.dart index 5626cd3f..dee44346 100644 --- a/lib/pages/home/home_model/home_item_model.dart +++ b/lib/pages/home/home_model/home_item_model.dart @@ -1,7 +1,3 @@ - - - - import 'package:flutter/cupertino.dart'; class HomeItemModel { @@ -11,12 +7,11 @@ class HomeItemModel { final bool? active; final void Function(BuildContext context) onPress; - HomeItemModel({ - this.title, - this.icon, - this.color, - this.active, + this.title, + this.icon, + this.color, + this.active, required this.onPress, }); -} \ No newline at end of file +} diff --git a/lib/pages/home/view/home_card.dart b/lib/pages/home/view/home_card.dart index ce152c1c..5c5e8dea 100644 --- a/lib/pages/home/view/home_card.dart +++ b/lib/pages/home/view/home_card.dart @@ -22,11 +22,12 @@ class HomeCard extends StatelessWidget { return InkWell( onTap: active ? onTap : null, child: Container( - padding: const EdgeInsets.only(left: 10,right: 10,bottom: 10), + padding: const EdgeInsets.only(left: 10, right: 10, bottom: 10), decoration: BoxDecoration( - color: evenNumbers && active? - ColorsManager.blueColor.withOpacity(0.8) : - (active ?ColorsManager.blueColor + color: evenNumbers && active + ? ColorsManager.blueColor.withOpacity(0.8) + : (active + ? ColorsManager.blueColor : ColorsManager.blueColor.withOpacity(0.2)), borderRadius: BorderRadius.circular(30), ), diff --git a/lib/pages/home/view/home_page.dart b/lib/pages/home/view/home_page.dart index b4225ae2..75937cf2 100644 --- a/lib/pages/home/view/home_page.dart +++ b/lib/pages/home/view/home_page.dart @@ -8,9 +8,7 @@ class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { - return ResponsiveLayout( - desktopBody: HomeWebPage(), - mobileBody:HomeMobilePage() - ); + return ResponsiveLayout( + desktopBody: HomeWebPage(), mobileBody: HomeMobilePage()); } } diff --git a/lib/pages/home/view/home_page_mobile.dart b/lib/pages/home/view/home_page_mobile.dart index 1b868906..dcf35a41 100644 --- a/lib/pages/home/view/home_page_mobile.dart +++ b/lib/pages/home/view/home_page_mobile.dart @@ -44,7 +44,8 @@ class HomeMobilePage extends StatelessWidget { width: size.width * 0.68, child: GridView.builder( itemCount: 8, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, diff --git a/lib/pages/home/view/home_page_web.dart b/lib/pages/home/view/home_page_web.dart index 39a79a57..aa779796 100644 --- a/lib/pages/home/view/home_page_web.dart +++ b/lib/pages/home/view/home_page_web.dart @@ -39,7 +39,8 @@ class HomeWebPage extends StatelessWidget { Text( 'ACCESS YOUR APPS', style: Theme.of(context) - .textTheme.headlineLarge! + .textTheme + .headlineLarge! .copyWith(color: Colors.black, fontSize: 40), ), const SizedBox(height: 30), @@ -63,7 +64,8 @@ class HomeWebPage extends StatelessWidget { active: homeBloc.homeItems[index].active!, name: homeBloc.homeItems[index].title!, img: homeBloc.homeItems[index].icon!, - onTap: () => homeBloc.homeItems[index].onPress(context), + onTap: () => + homeBloc.homeItems[index].onPress(context), ); }, ), diff --git a/lib/pages/home/view/tree_page.dart b/lib/pages/home/view/tree_page.dart index 6038dadf..2166467f 100644 --- a/lib/pages/home/view/tree_page.dart +++ b/lib/pages/home/view/tree_page.dart @@ -32,8 +32,8 @@ class TreeWidget extends StatelessWidget { SizedBox( width: 100, child: TextFormField( - decoration: - const InputDecoration(labelText: "Subtree separation"), + decoration: const InputDecoration( + labelText: "Subtree separation"), onChanged: (text) { firstNodeName = text; }, @@ -73,9 +73,7 @@ class TreeWidget extends StatelessWidget { child: GraphView( graph: state.graph, algorithm: BuchheimWalkerAlgorithm( - state.builder, - TreeEdgeRenderer(state.builder) - ), + state.builder, TreeEdgeRenderer(state.builder)), paint: Paint() ..color = Colors.green ..strokeWidth = 1 @@ -138,7 +136,7 @@ Widget rectangleWidget(String text, Node node, BuildContext blocContext) { ); }, child: Container( - width: MediaQuery.of(blocContext).size.width*0.2, + width: MediaQuery.of(blocContext).size.width * 0.2, margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0), padding: EdgeInsets.all(20.0), decoration: BoxDecoration( @@ -163,7 +161,7 @@ Widget rectangleWidget(String text, Node node, BuildContext blocContext) { ), ), const SizedBox(width: 10.0), - SizedBox( + SizedBox( child: Text( text, style: const TextStyle( diff --git a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart index 2a3e486f..6b1c60ee 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart @@ -220,7 +220,6 @@ class VisitorPasswordBloc } } - Future postOnlineMultipleTimePassword( OnlineMultipleTimePasswordEvent event, Emitter emit) async { @@ -246,11 +245,10 @@ class VisitorPasswordBloc passwordName: event.passwordName); if (res == true) { emit(SuccessState()); - }else { + } else { throw Exception('Failed to create password'); } emit(TableLoaded(data)); - } catch (e) { emit(FailedState(e.toString())); Navigator.pop(event.context!); @@ -267,17 +265,16 @@ class VisitorPasswordBloc try { emit(LoadingInitialState()); await generate7DigitNumber(); - bool res = await AccessMangApi().postOffLineOneTime( + bool res = await AccessMangApi().postOffLineOneTime( email: event.email, devicesUuid: selectedDevices, passwordName: event.passwordName); if (res == true) { emit(SuccessState()); - }else { + } else { throw Exception('Failed to create password'); } emit(TableLoaded(data)); - } catch (e) { emit(FailedState(e.toString())); Navigator.pop(event.context!); @@ -294,7 +291,7 @@ class VisitorPasswordBloc try { emit(LoadingInitialState()); await generate7DigitNumber(); - bool res = await AccessMangApi().postOffLineMultipleTime( + bool res = await AccessMangApi().postOffLineMultipleTime( email: event.email, devicesUuid: selectedDevices, passwordName: event.passwordName, @@ -303,18 +300,18 @@ class VisitorPasswordBloc ); if (res == true) { emit(SuccessState()); - }else { + } 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'); } + title: 'Something Wrong'); + } } void selectDevice( diff --git a/lib/pages/visitor_password/bloc/visitor_password_event.dart b/lib/pages/visitor_password/bloc/visitor_password_event.dart index 9526bf54..6ecae200 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_event.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_event.dart @@ -26,33 +26,32 @@ class SelectUsageFrequency extends VisitorPasswordEvent { @override List get props => [usageType]; } + class SelectTimeVisitorPassword extends VisitorPasswordEvent { final BuildContext context; final bool isStart; final bool isRepeat; - const SelectTimeVisitorPassword({ required this.context,required this.isStart,required this.isRepeat}); + const SelectTimeVisitorPassword( + {required this.context, required this.isStart, required this.isRepeat}); @override - List get props => [context,isStart,isRepeat]; + List get props => [context, isStart, isRepeat]; } - - class ToggleDaySelectionEvent extends VisitorPasswordEvent { - final String key; + final String key; const ToggleDaySelectionEvent({required this.key}); @override List get props => [key]; } - class ToggleRepeatEvent extends VisitorPasswordEvent {} + class GeneratePasswordEvent extends VisitorPasswordEvent {} -class FetchDevice extends VisitorPasswordEvent { -} +class FetchDevice extends VisitorPasswordEvent {} //online password class OnlineOneTimePasswordEvent extends VisitorPasswordEvent { @@ -60,20 +59,31 @@ class OnlineOneTimePasswordEvent extends VisitorPasswordEvent { final String? passwordName; final BuildContext? context; - const OnlineOneTimePasswordEvent({this.email,this.passwordName,this.context}); + const OnlineOneTimePasswordEvent( + {this.email, this.passwordName, this.context}); @override - List get props => [email!,passwordName!,]; + List get props => [ + email!, + passwordName!, + ]; } + class OnlineMultipleTimePasswordEvent extends VisitorPasswordEvent { final String? email; final String? passwordName; final String? invalidTime; final String? effectiveTime; final BuildContext? context; - const OnlineMultipleTimePasswordEvent({this.email,this.passwordName,this.invalidTime,this.effectiveTime,this.context}); + const OnlineMultipleTimePasswordEvent( + {this.email, + this.passwordName, + this.invalidTime, + this.effectiveTime, + this.context}); @override - List get props => [email!,passwordName!,invalidTime!,effectiveTime!,context!]; + List get props => + [email!, passwordName!, invalidTime!, effectiveTime!, context!]; } //offline password @@ -81,9 +91,14 @@ class OfflineOneTimePasswordEvent extends VisitorPasswordEvent { final BuildContext? context; final String? email; final String? passwordName; - const OfflineOneTimePasswordEvent({this.email,this.passwordName,this.context}); + const OfflineOneTimePasswordEvent( + {this.email, this.passwordName, this.context}); @override - List get props => [email!,passwordName!,context!,]; + List get props => [ + email!, + passwordName!, + context!, + ]; } class OfflineMultipleTimePasswordEvent extends VisitorPasswordEvent { @@ -93,13 +108,18 @@ class OfflineMultipleTimePasswordEvent extends VisitorPasswordEvent { final String? effectiveTime; final BuildContext? context; - const OfflineMultipleTimePasswordEvent({this.context,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!,context!]; + List get props => + [email!, passwordName!, invalidTime!, effectiveTime!, context!]; } - class SelectDeviceEvent extends VisitorPasswordEvent { final String deviceId; const SelectDeviceEvent(this.deviceId); @@ -116,22 +136,26 @@ class FilterDataEvent extends VisitorPasswordEvent { this.endTime, }); } + 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 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}); + const ChangeTimeEvent({required this.val, required this.isStartEndTime}); @override - List get props => [val,isStartEndTime]; -} \ No newline at end of file + List get props => [val, isStartEndTime]; +} diff --git a/lib/pages/visitor_password/bloc/visitor_password_state.dart b/lib/pages/visitor_password/bloc/visitor_password_state.dart index 279c9809..273220b8 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_state.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_state.dart @@ -1,5 +1,3 @@ - - import 'package:equatable/equatable.dart'; import 'package:syncrow_web/pages/visitor_password/model/device_model.dart'; @@ -12,8 +10,6 @@ abstract class VisitorPasswordState extends Equatable { class VisitorPasswordInitial extends VisitorPasswordState {} - - class PasswordTypeSelected extends VisitorPasswordState { final String selectedType; const PasswordTypeSelected(this.selectedType); @@ -36,13 +32,16 @@ class IsRepeatState extends VisitorPasswordState { @override List get props => [repeat]; - } class LoadingInitialState extends VisitorPasswordState {} + class ChangeTimeState extends VisitorPasswordState {} + class TimeSelectedState extends VisitorPasswordState {} + class DeviceLoaded extends VisitorPasswordState {} + class SuccessState extends VisitorPasswordState {} class FailedState extends VisitorPasswordState { @@ -62,4 +61,4 @@ class TableLoaded extends VisitorPasswordState { 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/device_model.dart b/lib/pages/visitor_password/model/device_model.dart index 2c3ce8d9..9d4e9290 100644 --- a/lib/pages/visitor_password/model/device_model.dart +++ b/lib/pages/visitor_password/model/device_model.dart @@ -1,28 +1,26 @@ - - import 'package:syncrow_web/utils/constants/const.dart'; class DeviceModel { - dynamic productUuid; - dynamic productType; - dynamic activeTime; - dynamic category; - dynamic categoryName; - dynamic createTime; - dynamic gatewayId; - dynamic icon; - dynamic ip; - dynamic lat; - dynamic localKey; - dynamic lon; - dynamic model; - dynamic name; - DeviseStatus online; - dynamic ownerId; - dynamic sub; - dynamic timeZone; - dynamic updateTime; - dynamic uuid; + dynamic productUuid; + dynamic productType; + dynamic activeTime; + dynamic category; + dynamic categoryName; + dynamic createTime; + dynamic gatewayId; + dynamic icon; + dynamic ip; + dynamic lat; + dynamic localKey; + dynamic lon; + dynamic model; + dynamic name; + DeviseStatus online; + dynamic ownerId; + dynamic sub; + dynamic timeZone; + dynamic updateTime; + dynamic uuid; DeviceModel({ required this.productUuid, @@ -50,25 +48,25 @@ class DeviceModel { // Deserialize from JSON factory DeviceModel.fromJson(Map json) { return DeviceModel( - productUuid: json['productUuid'] , + productUuid: json['productUuid'], productType: json['productType'], activeTime: json['activeTime'], - category: json['category'] , - categoryName: json['categoryName'] , - createTime: json['createTime'] , + category: json['category'], + categoryName: json['categoryName'], + createTime: json['createTime'], gatewayId: json['gatewayId'], icon: json['icon'], - ip: json['ip'] , - lat: json['lat'] , - localKey: json['localKey'] , - lon: json['lon'] , - model: json['model'] , + ip: json['ip'], + lat: json['lat'], + localKey: json['localKey'], + lon: json['lon'], + model: json['model'], name: json['name'], online: OnlineTypeExtension.fromString(json['online']), - ownerId: json['ownerId'] , + ownerId: json['ownerId'], sub: json['sub'], timeZone: json['timeZone'], - updateTime: json['updateTime'] , + updateTime: json['updateTime'], uuid: json['uuid'], ); } diff --git a/lib/pages/visitor_password/model/schedule_model.dart b/lib/pages/visitor_password/model/schedule_model.dart index efeeff35..d34dadbc 100644 --- a/lib/pages/visitor_password/model/schedule_model.dart +++ b/lib/pages/visitor_password/model/schedule_model.dart @@ -24,4 +24,4 @@ class Schedule { 'workingDay': workingDay, }; } -} \ No newline at end of file +} diff --git a/lib/pages/visitor_password/view/repeat_widget.dart b/lib/pages/visitor_password/view/repeat_widget.dart index ae37b1e3..8b40b580 100644 --- a/lib/pages/visitor_password/view/repeat_widget.dart +++ b/lib/pages/visitor_password/view/repeat_widget.dart @@ -17,70 +17,72 @@ class RepeatWidget extends StatelessWidget { Size size = MediaQuery.of(context).size; return BlocBuilder( builder: (context, state) { - final visitorBloc = BlocProvider.of(context); - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Wrap the Row in a SingleChildScrollView to handle overflow - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: visitorBloc.days.map((day) { - return Container( - width: 70, // Adjust width as needed - margin: EdgeInsets.all(5), - child: CheckboxListTile( - contentPadding: EdgeInsets.zero, - title: Text( - day['day']!, - style: TextStyle( - fontSize: 10, - color: visitorBloc.selectedDays.contains(day['key']) - ? Colors.black - : ColorsManager.blackColor, - ), - ), - value: visitorBloc.selectedDays.contains(day['key']), - onChanged: (bool? value) { - if (value != null) { - visitorBloc.add(ToggleDaySelectionEvent(key: day['key']!)); - } - }, + final visitorBloc = BlocProvider.of(context); + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Wrap the Row in a SingleChildScrollView to handle overflow + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: visitorBloc.days.map((day) { + return Container( + width: 70, // Adjust width as needed + margin: EdgeInsets.all(5), + child: CheckboxListTile( + contentPadding: EdgeInsets.zero, + title: Text( + day['day']!, + style: TextStyle( + fontSize: 10, + color: visitorBloc.selectedDays.contains(day['key']) + ? Colors.black + : ColorsManager.blackColor, ), - ); - }).toList(), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: DateTimeWebWidget( - icon: Assets.timeIcon, - isRequired: false, - title: '', - size: size, - endTime: () { - visitorBloc.add(SelectTimeEvent( - context: context, - isEffective: false)); - Future.delayed(const Duration(milliseconds: 500), () { - visitorBloc.add(ChangeTimeEvent(val: visitorBloc.endTime, isStartEndTime: true)); - }); - }, - startTime: () { - Future.delayed(const Duration(milliseconds: 500), () { - visitorBloc.add(ChangeTimeEvent(val: visitorBloc.endTime, isStartEndTime: true)); - }); - visitorBloc.add(SelectTimeEvent(context: context, isEffective: true)); - }, - firstString: visitorBloc.effectiveTime, - secondString: visitorBloc.expirationTime, - ), - ), - const SizedBox(height: 20), - ], - ); - } - ); + ), + value: visitorBloc.selectedDays.contains(day['key']), + onChanged: (bool? value) { + if (value != null) { + visitorBloc + .add(ToggleDaySelectionEvent(key: day['key']!)); + } + }, + ), + ); + }).toList(), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: DateTimeWebWidget( + icon: Assets.timeIcon, + isRequired: false, + title: '', + size: size, + endTime: () { + visitorBloc + .add(SelectTimeEvent(context: context, isEffective: false)); + Future.delayed(const Duration(milliseconds: 500), () { + visitorBloc.add(ChangeTimeEvent( + val: visitorBloc.endTime, isStartEndTime: true)); + }); + }, + startTime: () { + Future.delayed(const Duration(milliseconds: 500), () { + visitorBloc.add(ChangeTimeEvent( + val: visitorBloc.endTime, isStartEndTime: true)); + }); + visitorBloc + .add(SelectTimeEvent(context: context, isEffective: true)); + }, + firstString: visitorBloc.effectiveTime, + secondString: visitorBloc.expirationTime, + ), + ), + const SizedBox(height: 20), + ], + ); + }); } } diff --git a/lib/services/access_mang_api.dart b/lib/services/access_mang_api.dart index 6a2cf40d..84c82586 100644 --- a/lib/services/access_mang_api.dart +++ b/lib/services/access_mang_api.dart @@ -7,8 +7,7 @@ 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'; -class AccessMangApi{ - +class AccessMangApi { Future> fetchVisitorPassword() async { try { final response = await HTTPService().get( @@ -23,7 +22,7 @@ class AccessMangApi{ }, ); return response; - } catch (e) { + } catch (e) { debugPrint('Error fetching visitor passwords: $e'); return []; } @@ -43,19 +42,19 @@ class AccessMangApi{ }, ); return response; - } catch (e) { + } catch (e) { debugPrint('Error fetching $e'); return []; } } - Future postOnlineOneTime({ - String? email, - String? passwordName, - String? password, - String? effectiveTime, - String? invalidTime, - List? devicesUuid}) async { + Future postOnlineOneTime( + {String? email, + String? passwordName, + String? password, + String? effectiveTime, + String? invalidTime, + List? devicesUuid}) async { try { final response = await HTTPService().post( path: ApiEndpoints.sendOnlineOneTime, @@ -64,34 +63,34 @@ class AccessMangApi{ "passwordName": passwordName, "password": password, "devicesUuid": devicesUuid, - "effectiveTime":effectiveTime , + "effectiveTime": effectiveTime, "invalidTime": invalidTime }), showServerMessage: true, expectedResponseModel: (json) { - if(json['statusCode'].toString()=='201'){ + if (json['statusCode'].toString() == '201') { return true; - }else{ + } else { return false; } }, ); return response; - } on DioException catch (e) { + } on DioException catch (e) { debugPrint('Error: ${e.message}'); debugPrint('Error fetching ${e.response!.statusMessage}'); return false; } } - Future postOnlineMultipleTime({ - String? effectiveTime, - String? invalidTime, - String? email, - String? password, - String? passwordName, - List? scheduleList, - List? devicesUuid}) async { + Future postOnlineMultipleTime( + {String? effectiveTime, + String? invalidTime, + String? email, + String? password, + String? passwordName, + List? scheduleList, + List? devicesUuid}) async { try { Map body = { "email": email, @@ -102,22 +101,24 @@ class AccessMangApi{ "invalidTime": invalidTime, }; if (scheduleList != null) { - body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList(); + body["scheduleList"] = + scheduleList.map((schedule) => schedule.toJson()).toList(); } final response = await HTTPService().post( path: ApiEndpoints.sendOnlineMultipleTime, body: jsonEncode(body), showServerMessage: true, expectedResponseModel: (json) { - if(json['data']['successOperations'][0]['success'].toString()=='true'){ + if (json['data']['successOperations'][0]['success'].toString() == + 'true') { return true; - }else{ + } else { return false; } }, ); return response; - } on DioException catch (e){ + } on DioException catch (e) { debugPrint('Error fetching ${e.type.name}'); debugPrint('Error fetching ${e.response!.statusMessage}'); return false; @@ -126,16 +127,17 @@ class AccessMangApi{ // OffLine One Time Password - Future postOffLineOneTime({String? email,String? passwordName,List? devicesUuid}) async { + Future postOffLineOneTime( + {String? email, String? passwordName, List? devicesUuid}) async { try { final response = await HTTPService().post( - path: ApiEndpoints.sendOffLineOneTime, - body: jsonEncode({ - "email": email, - "passwordName": passwordName, - "devicesUuid": devicesUuid - }), - showServerMessage: true, + path: ApiEndpoints.sendOffLineOneTime, + body: jsonEncode({ + "email": email, + "passwordName": passwordName, + "devicesUuid": devicesUuid + }), + showServerMessage: true, expectedResponseModel: (json) { if (json['data']['successOperations'][0]['success'].toString() == 'true') { @@ -143,8 +145,7 @@ class AccessMangApi{ } else { return false; } - } - ); + }); return response; } catch (e) { debugPrint('Error fetching $e'); @@ -152,25 +153,23 @@ class AccessMangApi{ } } - Future postOffLineMultipleTime({ - String? email, - String? passwordName, - String? effectiveTime, - String? invalidTime, - List? devicesUuid - - }) async { + Future postOffLineMultipleTime( + {String? email, + String? passwordName, + String? effectiveTime, + String? invalidTime, + List? devicesUuid}) async { try { final response = await HTTPService().post( - path: ApiEndpoints.sendOffLineOneTime, - body: jsonEncode({ - "email": email, - "devicesUuid":devicesUuid, - "passwordName": passwordName, - "effectiveTime": effectiveTime, - "invalidTime": invalidTime - }), - showServerMessage: true, + path: ApiEndpoints.sendOffLineOneTime, + body: jsonEncode({ + "email": email, + "devicesUuid": devicesUuid, + "passwordName": passwordName, + "effectiveTime": effectiveTime, + "invalidTime": invalidTime + }), + showServerMessage: true, expectedResponseModel: (json) { if (json['data']['successOperations'][0]['success'].toString() == 'true') { @@ -178,12 +177,11 @@ class AccessMangApi{ } else { return false; } - } - ); + }); return response; } catch (e) { debugPrint('Error fetching $e'); return []; } } -} \ No newline at end of file +} diff --git a/lib/services/api/http_interceptor.dart b/lib/services/api/http_interceptor.dart index cece39a7..bef8d804 100644 --- a/lib/services/api/http_interceptor.dart +++ b/lib/services/api/http_interceptor.dart @@ -22,16 +22,19 @@ class HTTPInterceptor extends InterceptorsWrapper { if (await validateResponse(response)) { super.onResponse(response, handler); } else { - handler.reject(DioException(requestOptions: response.requestOptions, response: response)); + handler.reject(DioException( + requestOptions: response.requestOptions, response: response)); } } @override - void onRequest(RequestOptions options, RequestInterceptorHandler handler) async { + void onRequest( + RequestOptions options, RequestInterceptorHandler handler) async { var storage = const FlutterSecureStorage(); var token = await storage.read(key: Token.loginAccessTokenKey); if (checkHeaderExclusionListOfAddedParameters(options.path)) { - options.headers.putIfAbsent(HttpHeaders.authorizationHeader, () => "Bearer $token"); + options.headers + .putIfAbsent(HttpHeaders.authorizationHeader, () => "Bearer $token"); } // options.headers['Authorization'] = 'Bearer ${'${token!}123'}'; super.onRequest(options, handler); diff --git a/lib/services/api/network_exception.dart b/lib/services/api/network_exception.dart index 81f12b3e..d85ef27c 100644 --- a/lib/services/api/network_exception.dart +++ b/lib/services/api/network_exception.dart @@ -31,8 +31,7 @@ class ServerFailure extends Failure { { // var document = parser.parse(dioError.response!.data.toString()); // var message = document.body!.text; - return ServerFailure.fromResponse( - dioError.response!.statusCode!, + return ServerFailure.fromResponse(dioError.response!.statusCode!, dioError.response?.data['message'] ?? "Error"); } case DioExceptionType.cancel: diff --git a/lib/services/auth_api.dart b/lib/services/auth_api.dart index 3bb8f7e7..69dbb799 100644 --- a/lib/services/auth_api.dart +++ b/lib/services/auth_api.dart @@ -1,4 +1,3 @@ - import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:syncrow_web/pages/auth/model/region_model.dart'; @@ -18,48 +17,43 @@ class AuthenticationAPI { return response; } - static Future forgetPassword( - {required var email, required var password,}) async { + static Future forgetPassword({ + required var email, + required var password, + }) async { final response = await HTTPService().post( path: ApiEndpoints.forgetPassword, - body: { - "email": email, - "password": password - }, + body: {"email": email, "password": password}, showServerMessage: true, expectedResponseModel: (json) {}); return response; } - - static Future sendOtp({required String email, required String regionUuid}) async { + static Future sendOtp( + {required String email, required String regionUuid}) async { try { final response = await HTTPService().post( path: ApiEndpoints.sendOtp, - body: { - "email": email, - "type": "PASSWORD", - "regionUuid": regionUuid - }, + body: {"email": email, "type": "PASSWORD", "regionUuid": regionUuid}, showServerMessage: true, expectedResponseModel: (json) { return 30; - } - ); + }); return 30; } on DioException catch (e) { if (e.response != null) { if (e.response!.statusCode == 400) { final errorData = e.response!.data; String errorMessage = errorData['message']; - if(errorMessage=='User not found'){ + if (errorMessage == 'User not found') { return 1; - }else{ + } else { int cooldown = errorData['data']['cooldown'] ?? 1; return cooldown; } } else { - debugPrint('Error: ${e.response!.statusCode} - ${e.response!.statusMessage}'); + debugPrint( + 'Error: ${e.response!.statusCode} - ${e.response!.statusMessage}'); return 1; } } else { @@ -74,7 +68,7 @@ class AuthenticationAPI { static Future verifyOtp( {required String email, required String otpCode}) async { - try{ + try { final response = await HTTPService().post( path: ApiEndpoints.verifyOtp, body: {"email": email, "type": "PASSWORD", "otpCode": otpCode}, @@ -87,12 +81,12 @@ class AuthenticationAPI { } }); return response; - }on DioException catch (e){ + } on DioException catch (e) { if (e.response != null) { if (e.response!.statusCode == 400) { final errorData = e.response!.data; String errorMessage = errorData['message']; - return errorMessage; + return errorMessage; } } else { debugPrint('Error: ${e.message}'); @@ -105,10 +99,10 @@ class AuthenticationAPI { path: ApiEndpoints.getRegion, showServerMessage: true, expectedResponseModel: (json) { - return (json as List).map((zone) => RegionModel.fromJson(zone)).toList(); - } - ); + return (json as List) + .map((zone) => RegionModel.fromJson(zone)) + .toList(); + }); return response as List; } - } diff --git a/lib/services/devices_mang_api.dart b/lib/services/devices_mang_api.dart new file mode 100644 index 00000000..21dbbe97 --- /dev/null +++ b/lib/services/devices_mang_api.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/device_managment/models/devices_model.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + +class DevicesManagementApi { + Future> fetchDevices() async { + try { + final response = await HTTPService().get( + path: ApiEndpoints.getAllDevices, + showServerMessage: true, + expectedResponseModel: (json) { + List jsonData = json; + List devicesList = jsonData.map((jsonItem) { + return AllDevicesModel.fromJson(jsonItem); + }).toList(); + return devicesList; + }, + ); + return response; + } catch (e) { + debugPrint('Error fetching $e'); + return []; + } + } +} diff --git a/lib/services/home_api.dart b/lib/services/home_api.dart index 42e732b4..dfbaf4bf 100644 --- a/lib/services/home_api.dart +++ b/lib/services/home_api.dart @@ -1,16 +1,15 @@ - import 'package:syncrow_web/pages/auth/model/user_model.dart'; +import 'package:syncrow_web/pages/auth/model/user_model.dart'; import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; -class HomeApi{ - Future fetchUserInfo(userId) async { - final response = await HTTPService().get( - path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!), - showServerMessage: true, - expectedResponseModel: (json) { - return UserModel.fromJson(json); - } - ); - return response; - } - } \ No newline at end of file +class HomeApi { + Future fetchUserInfo(userId) async { + final response = await HTTPService().get( + path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!), + showServerMessage: true, + expectedResponseModel: (json) { + return UserModel.fromJson(json); + }); + return response; + } +} diff --git a/lib/services/locator.dart b/lib/services/locator.dart index 4a9263a0..055deb05 100644 --- a/lib/services/locator.dart +++ b/lib/services/locator.dart @@ -4,7 +4,7 @@ import 'package:syncrow_web/services/api/http_interceptor.dart'; import 'package:syncrow_web/services/api/http_service.dart'; final GetIt serviceLocator = GetIt.instance; - //setupLocator() // to search for dependency injection in flutter +//setupLocator() // to search for dependency injection in flutter initialSetup() { serviceLocator.registerSingleton(HTTPInterceptor()); //Base classes diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 17ee82a9..5fbdb782 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -25,6 +25,7 @@ abstract class ColorsManager { static const Color slidingBlueColor = Color(0x99023DFE); static const Color blackColor = Color(0xFF000000); static const Color lightGreen = Color(0xFF00FF0A); + static const Color green = Color(0xFF008905); static const Color grayColor = Color(0xFF999999); static const Color red = Color(0xFFFF0000); static const Color graysColor = Color(0xffEBEBEB); diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index f9581cb6..8248cf43 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -5,21 +5,28 @@ abstract class ApiEndpoints { ////////////////////////////////////// Authentication /////////////////////////////// static const String signUp = '$baseUrl/authentication/user/signup'; static const String login = '$baseUrl/authentication/user/login'; - static const String forgetPassword = '$baseUrl/authentication/user/forget-password'; + static const String forgetPassword = + '$baseUrl/authentication/user/forget-password'; 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 getDevices = '$baseUrl/visitor-password/devices'; - - static const String sendOnlineOneTime = '$baseUrl/visitor-password/temporary-password/online/one-time'; - static const String sendOnlineMultipleTime = '$baseUrl/visitor-password/temporary-password/online/multiple-time'; + static const String sendOnlineOneTime = + '$baseUrl/visitor-password/temporary-password/online/one-time'; + static const String sendOnlineMultipleTime = + '$baseUrl/visitor-password/temporary-password/online/multiple-time'; //offline Password - static const String sendOffLineOneTime = '$baseUrl/visitor-password/temporary-password/offline/one-time'; - static const String sendOffLineMultipleTime = '$baseUrl/visitor-password/temporary-password/offline/multiple-time'; - + static const String sendOffLineOneTime = + '$baseUrl/visitor-password/temporary-password/offline/one-time'; + static const String sendOffLineMultipleTime = + '$baseUrl/visitor-password/temporary-password/offline/multiple-time'; static const String getUser = '$baseUrl/user/{userUuid}'; + + ////// Devices Management //////////////// + + static const String getAllDevices = '$baseUrl/device'; } diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index f4a2859e..9d580630 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -13,10 +13,12 @@ class Assets { static const String rightLine = "assets/images/right_line.png"; static const String google = "assets/images/google.svg"; static const String facebook = "assets/images/facebook.svg"; - static const String invisiblePassword = "assets/images/Password_invisible.svg"; + static const String invisiblePassword = + "assets/images/Password_invisible.svg"; static const String visiblePassword = "assets/images/Password_visible.svg"; static const String accessIcon = "assets/images/access_icon.svg"; - static const String spaseManagementIcon = "assets/images/spase_management_icon.svg"; + static const String spaseManagementIcon = + "assets/images/spase_management_icon.svg"; static const String devicesIcon = "assets/images/devices_icon.svg"; static const String moveinIcon = "assets/images/movein_icon.svg"; static const String constructionIcon = "assets/images/construction_icon.svg"; diff --git a/lib/utils/constants/const.dart b/lib/utils/constants/const.dart index 77beff69..09f44f0f 100644 --- a/lib/utils/constants/const.dart +++ b/lib/utils/constants/const.dart @@ -1,4 +1,3 @@ - enum AccessType { onlineOnetime, onlineMultiple, @@ -36,11 +35,6 @@ extension AccessTypeExtension on AccessType { } } - - - - - enum DeviseStatus { online, offline, @@ -53,7 +47,6 @@ extension OnlineTypeExtension on DeviseStatus { return "Online"; case DeviseStatus.offline: return "Offline"; - } } @@ -69,10 +62,9 @@ extension OnlineTypeExtension on DeviseStatus { } } - enum AccessStatus { - expired , - effective , + expired, + effective, toBeEffective, } @@ -82,28 +74,22 @@ extension AccessStatusExtension on AccessStatus { case AccessStatus.expired: return "Expired"; case AccessStatus.effective: - return "Effective" ; - case AccessStatus.toBeEffective: + return "Effective"; + case AccessStatus.toBeEffective: return "To be effective"; - } } static AccessStatus fromString(String value) { switch (value) { - case "EXPIRED" : + case "EXPIRED": return AccessStatus.expired; - case "EFFECTIVE" : + case "EFFECTIVE": return AccessStatus.effective; - case "TO_BE_EFFECTIVE": + case "TO_BE_EFFECTIVE": return AccessStatus.toBeEffective; default: - throw ArgumentError("Invalid access type: $value"); + throw ArgumentError("Invalid access type: $value"); } } } - - - - - diff --git a/lib/utils/format_date_time.dart b/lib/utils/format_date_time.dart new file mode 100644 index 00000000..5c089a2c --- /dev/null +++ b/lib/utils/format_date_time.dart @@ -0,0 +1,11 @@ +import 'package:intl/intl.dart'; + +String formatDateTime(DateTime? dateTime) { + if (dateTime == null) { + return '-'; + } + final DateFormat dateFormatter = DateFormat('dd/MM/yyyy'); + final DateFormat timeFormatter = DateFormat('HH:mm'); + + return '${dateFormatter.format(dateTime)} ${timeFormatter.format(dateTime)}'; +} diff --git a/lib/utils/helpers/decodeBase64.dart b/lib/utils/helpers/decodeBase64.dart index 9931a8e3..e0473992 100644 --- a/lib/utils/helpers/decodeBase64.dart +++ b/lib/utils/helpers/decodeBase64.dart @@ -4,7 +4,7 @@ String decodeBase64(String str) { //'-', '+' 62nd char of encoding, '_', '/' 63rd char of encoding String output = str.replaceAll('-', '+').replaceAll('_', '/'); switch (output.length % 4) { - // Pad with trailing '=' + // Pad with trailing '=' case 0: // No pad chars in this case break; case 2: // Two pad chars diff --git a/lib/utils/responsive_layout.dart b/lib/utils/responsive_layout.dart index 804b5ddb..efc1600b 100644 --- a/lib/utils/responsive_layout.dart +++ b/lib/utils/responsive_layout.dart @@ -1,19 +1,20 @@ import 'package:flutter/material.dart'; - class ResponsiveLayout extends StatelessWidget { - final Widget desktopBody; + final Widget desktopBody; final Widget mobileBody; - const ResponsiveLayout({super.key,required this.desktopBody,required this.mobileBody}); + const ResponsiveLayout( + {super.key, required this.desktopBody, required this.mobileBody}); @override Widget build(BuildContext context) { - return LayoutBuilder(builder: (context, constraints) { - if(constraints.maxWidth<600){ - return mobileBody; - }else{ - return desktopBody; - } - }, + return LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth < 600) { + return mobileBody; + } else { + return desktopBody; + } + }, ); } } diff --git a/lib/utils/snack_bar.dart b/lib/utils/snack_bar.dart index 11e46828..d50a4250 100644 --- a/lib/utils/snack_bar.dart +++ b/lib/utils/snack_bar.dart @@ -16,7 +16,6 @@ class CustomSnackBar { BuildContext? currentContext = key?.currentContext; if (key != null && currentContext != null) { final snackBar = SnackBar( - padding: const EdgeInsets.all(16), backgroundColor: Colors.green, content: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/utils/style.dart b/lib/utils/style.dart index 24747880..3f31f9d7 100644 --- a/lib/utils/style.dart +++ b/lib/utils/style.dart @@ -1,46 +1,43 @@ import 'package:flutter/material.dart'; import 'color_manager.dart'; -InputDecoration? textBoxDecoration({bool suffixIcon = false}) => InputDecoration( - focusColor: ColorsManager.grayColor, - suffixIcon:suffixIcon? const Icon(Icons.search):null, - hintText: 'Search', - filled: true, // Enable background filling - fillColor: const Color(0xffF5F6F7), // Set the background color - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), // Add border radius - borderSide: BorderSide.none, // Remove the underline - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), // Add border radius - borderSide: BorderSide.none, // Remove the underline - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), // Add border radius - borderSide: BorderSide.none, // Remove the underline - ), - errorBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.red, width: 2), - borderRadius: BorderRadius.circular(8), - ), - focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.red, width: 2), - borderRadius: BorderRadius.circular(8), - ), -); +InputDecoration? textBoxDecoration({bool suffixIcon = false}) => + InputDecoration( + focusColor: ColorsManager.grayColor, + suffixIcon: suffixIcon ? const Icon(Icons.search) : null, + hintText: 'Search', + filled: true, // Enable background filling + fillColor: const Color(0xffF5F6F7), // Set the background color + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), // Add border radius + borderSide: BorderSide.none, // Remove the underline + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), // Add border radius + borderSide: BorderSide.none, // Remove the underline + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), // Add border radius + borderSide: BorderSide.none, // Remove the underline + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.red, width: 2), + borderRadius: BorderRadius.circular(8), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.red, width: 2), + borderRadius: BorderRadius.circular(8), + ), + ); - -BoxDecoration containerDecoration = BoxDecoration( +BoxDecoration containerDecoration = BoxDecoration( boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.5), spreadRadius: 5, blurRadius: 8, - offset: const Offset(0, - 3), // changes position of shadow + offset: const Offset(0, 3), // changes position of shadow ), ], color: ColorsManager.boxColor, borderRadius: const BorderRadius.all(Radius.circular(10))); - - diff --git a/lib/web_layout/menu_sidebar.dart b/lib/web_layout/menu_sidebar.dart index 5cc882a8..c0d7b08f 100644 --- a/lib/web_layout/menu_sidebar.dart +++ b/lib/web_layout/menu_sidebar.dart @@ -21,30 +21,35 @@ class MenuSidebar extends StatelessWidget { color: ColorsManager.whiteColors, ), width: 200, - child: Padding( + child: Padding( padding: const EdgeInsets.all(15.0), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text('Community',style: TextStyle(fontSize: 20),), + const Text( + 'Community', + style: TextStyle(fontSize: 20), + ), CircleAvatar( backgroundColor: Colors.grey.shade200, child: IconButton( - color: ColorsManager.onSecondaryColor, - onPressed: () {}, - icon: const Icon(Icons.add) - ), - ) + color: ColorsManager.onSecondaryColor, + onPressed: () {}, + icon: const Icon(Icons.add)), + ) ], ), - const SizedBox(height: 20,), - TextFormField( - controller: TextEditingController(), - decoration:textBoxDecoration(suffixIcon: true) + const SizedBox( + height: 20, ), - Container(height: 100,) + TextFormField( + controller: TextEditingController(), + decoration: textBoxDecoration(suffixIcon: true)), + Container( + height: 100, + ) ], ), ), diff --git a/lib/web_layout/web_scaffold.dart b/lib/web_layout/web_scaffold.dart index 31b8d958..03610e52 100644 --- a/lib/web_layout/web_scaffold.dart +++ b/lib/web_layout/web_scaffold.dart @@ -3,51 +3,54 @@ 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}); + const WebScaffold( + {super.key, + this.appBarTitle, + this.appBarBody, + this.scaffoldBody, + this.enableMenuSideba = true}); @override Widget build(BuildContext context) { return Scaffold( body: Stack( + children: [ + SizedBox( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + child: SvgPicture.asset( + Assets.webBackground, + fit: BoxFit.cover, + ), + ), + Container( + color: Colors.white.withOpacity(0.7), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - SizedBox( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - child: SvgPicture.asset( - Assets.webBackground, - fit: BoxFit.cover, + Opacity( + opacity: 0.7, + child: WebAppBar( + title: appBarTitle, + body: appBarBody, + )), + Expanded( + child: Row( + children: [ + if (enableMenuSideba) const MenuSidebar(), + Expanded(flex: 5, child: scaffoldBody!) + ], ), - ), - Container(color: Colors.white.withOpacity(0.7),), - Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Opacity( - opacity: 0.7, - child: WebAppBar( - title: appBarTitle, - body: appBarBody, - ) - ), - Expanded( - child: Row( - children: [ - if(enableMenuSideba) - const MenuSidebar(), - Expanded( - flex: 5, - child: scaffoldBody! - ) - ], - ), - ) - ], - ), + ) ], - )); + ), + ], + )); } }