diff --git a/lib/core/theme/app_theme.dart b/lib/core/theme/app_theme.dart deleted file mode 100644 index 8b137891..00000000 --- a/lib/core/theme/app_theme.dart +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/main.dart b/lib/main.dart index 90c643cc..b4fcb72d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,29 +7,35 @@ import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.da import 'package:go_router/go_router.dart'; import 'package:syncrow_web/services/locator.dart'; import 'package:syncrow_web/utils/app_routes.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart'; +import 'package:syncrow_web/utils/theme/theme.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); initialSetup(); - String checkToken = await AuthBloc.getTokenAndValidate(); - GoRouter router = GoRouter( - initialLocation: checkToken == 'Success' ? RoutesConst.home : RoutesConst.auth, - routes: AppRoutes.getRoutes(), - ); - runApp(MyApp( - router: router, - )); + runApp(MyApp()); } class MyApp extends StatelessWidget { - final GoRouter router; - const MyApp({ + MyApp({ super.key, - required this.router, }); + final GoRouter _router = GoRouter( + initialLocation: RoutesConst.auth, + routes: AppRoutes.getRoutes(), + redirect: (context, state) async { + String checkToken = await AuthBloc.getTokenAndValidate(); + final loggedIn = checkToken == 'Success'; + final goingToLogin = state.uri.toString() == RoutesConst.auth; + + if (!loggedIn && !goingToLogin) return RoutesConst.auth; + if (loggedIn && goingToLogin) return RoutesConst.home; + + return null; + }, + ); + @override Widget build(BuildContext context) { return MultiBlocProvider( @@ -40,7 +46,7 @@ class MyApp extends StatelessWidget { ) ], child: MaterialApp.router( - debugShowCheckedModeBanner: false, // Hide debug banner + debugShowCheckedModeBanner: false, scrollBehavior: const MaterialScrollBehavior().copyWith( dragDevices: { PointerDeviceKind.mouse, @@ -49,26 +55,8 @@ class MyApp extends StatelessWidget { PointerDeviceKind.unknown, }, ), - - theme: ThemeData( - fontFamily: 'Aftika', - textTheme: const TextTheme( - bodySmall: TextStyle( - fontSize: 13, color: ColorsManager.whiteColors, fontWeight: FontWeight.bold), - bodyMedium: TextStyle(color: Colors.black87, fontSize: 14), - bodyLarge: TextStyle(fontSize: 16, color: Colors.white), - headlineSmall: TextStyle(color: Colors.black87, fontSize: 18), - headlineMedium: TextStyle(color: Colors.black87, fontSize: 20), - headlineLarge: TextStyle( - color: Colors.white, - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - ), - routeInformationProvider: router.routeInformationProvider, - routerDelegate: router.routerDelegate, - routeInformationParser: router.routeInformationParser, + theme: myTheme, + routerConfig: _router, )); } } diff --git a/lib/pages/access_management/bloc/access_bloc.dart b/lib/pages/access_management/bloc/access_bloc.dart index 473d19bd..fb66408a 100644 --- a/lib/pages/access_management/bloc/access_bloc.dart +++ b/lib/pages/access_management/bloc/access_bloc.dart @@ -4,7 +4,6 @@ import 'package:syncrow_web/pages/access_management/bloc/access_event.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_state.dart'; import 'package:syncrow_web/pages/access_management/model/password_model.dart'; import 'package:syncrow_web/services/access_mang_api.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart'; import 'package:syncrow_web/utils/snack_bar.dart'; diff --git a/lib/pages/access_management/view/access_management.dart b/lib/pages/access_management/view/access_management.dart index a63b02a3..5652e2cd 100644 --- a/lib/pages/access_management/view/access_management.dart +++ b/lib/pages/access_management/view/access_management.dart @@ -1,260 +1,91 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:go_router/go_router.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_bloc.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_event.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_state.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; +import 'package:syncrow_web/pages/common/buttons/search_reset_buttons.dart'; import 'package:syncrow_web/pages/common/custom_table.dart'; import 'package:syncrow_web/pages/common/date_time_widget.dart'; +import 'package:syncrow_web/pages/common/filter/filter_widget.dart'; import 'package:syncrow_web/pages/common/text_field/custom_web_textfield.dart'; +import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart'; -import 'package:syncrow_web/utils/constants/routes_const.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/style.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; -class AccessManagementPage extends StatelessWidget { +class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout { const AccessManagementPage({super.key}); + @override Widget build(BuildContext context) { - Size size = MediaQuery.of(context).size; + final isLargeScreen = isLargeScreenSize(context); + final isSmallScreen = isSmallScreenSize(context); + final isHalfMediumScreen = isHafMediumScreenSize(context); + final padding = + isLargeScreen ? const EdgeInsets.all(30) : const EdgeInsets.all(15); + return WebScaffold( enableMenuSideba: false, - appBarTitle: Row( - children: [ - Text( - 'Access Management', - style: Theme.of(context).textTheme.headlineLarge, - ) - ], - ), - appBarBody: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Physical Access', - style: Theme.of(context).textTheme.headlineMedium!.copyWith(color: Colors.white), - ), - Row( - children: [ - InkWell( - onTap: () { - context.go(RoutesConst.home); - }, - child: SvgPicture.asset( - height: 20, - width: 20, - Assets.grid, - ), - ), - const SizedBox( - width: 10, - ) - ], - ), - ], + appBarTitle: FittedBox( + child: Text( + 'Access Management', + style: Theme.of(context).textTheme.headlineLarge, ), - ], + ), + centerBody: Text( + 'Physical Access', + style: Theme.of(context) + .textTheme + .headlineMedium! + .copyWith(color: Colors.white), + ), + rightBody: const NavigateHomeGridView(), scaffoldBody: BlocProvider( - create: (BuildContext context) => AccessBloc()..add(FetchTableData()), + create: (BuildContext context) => + AccessBloc()..add(FetchTableData()), child: BlocConsumer( listener: (context, state) {}, builder: (context, state) { final accessBloc = BlocProvider.of(context); final filteredData = accessBloc.filteredData; + return state is AccessLoaded ? const Center(child: CircularProgressIndicator()) : Container( - padding: EdgeInsets.all(30), - height: size.height, - width: size.width, + padding: padding, + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - decoration: containerDecoration, - height: size.height * 0.05, - child: Flexible( - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: BlocProvider.of(context).tabs.length, - shrinkWrap: true, - itemBuilder: (context, index) { - final isSelected = index == - BlocProvider.of(context).selectedIndex; - return InkWell( - onTap: () { - BlocProvider.of(context) - .add(TabChangedEvent(index)); - }, - child: Container( - decoration: BoxDecoration( - color: ColorsManager.boxColor, - border: Border.all( - color: isSelected ? Colors.blue : Colors.transparent, - width: 2.0, - ), - borderRadius: index == 0 - ? const BorderRadius.only( - topLeft: Radius.circular(10), - bottomLeft: Radius.circular(10)) - : index == 3 - ? const BorderRadius.only( - topRight: Radius.circular(10), - bottomRight: Radius.circular(10)) - : null, - ), - padding: const EdgeInsets.only(left: 10, right: 10), - child: Center( - child: Text( - BlocProvider.of(context).tabs[index], - style: TextStyle( - color: isSelected ? Colors.blue : Colors.black, - ), - ), - ), - ), - ); - }, - ), - ), - ), - const SizedBox( - height: 20, - ), - Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - textBaseline: TextBaseline.ideographic, - children: [ - Container( - width: size.width * 0.15, - child: CustomWebTextField( - controller: accessBloc.passwordName, - isRequired: true, - textFieldName: 'Name', - description: '', - ), - ), - const SizedBox( - width: 15, - ), - DateTimeWebWidget( - icon: Assets.calendarIcon, - isRequired: false, - title: 'Access Time', - size: size, - endTime: () { - accessBloc.add(SelectTime(context: context, isStart: false)); - }, - startTime: () { - accessBloc.add(SelectTime(context: context, isStart: true)); - }, - firstString: BlocProvider.of(context).startTime, - secondString: BlocProvider.of(context).endTime, - ), - const SizedBox( - width: 15, - ), - SizedBox( - width: size.width * 0.06, - child: Container( - decoration: containerDecoration, - child: DefaultButton( - onPressed: () { - accessBloc.add(FilterDataEvent( - selectedTabIndex: BlocProvider.of( - context) - .selectedIndex, // Pass the selected tab index - passwordName: - accessBloc.passwordName.text.toLowerCase(), - startTime: accessBloc.effectiveTimeTimeStamp, - endTime: accessBloc.expirationTimeTimeStamp)); - }, - borderRadius: 9, - child: const Text('Search'))), - ), - const SizedBox( - width: 10, - ), - SizedBox( - width: size.width * 0.06, - child: Container( - decoration: containerDecoration, - child: DefaultButton( - onPressed: () { - accessBloc.add(ResetSearch()); - }, - backgroundColor: ColorsManager.whiteColors, - borderRadius: 9, - child: Text( - 'Reset', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: Colors.black), - ), - ), - ), - ), - ], - ), - const SizedBox( - height: 20, - ), - Wrap( - children: [ - Container( - width: size.width * 0.15, - decoration: containerDecoration, - child: DefaultButton( - onPressed: () { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return const VisitorPasswordDialog(); - }, - ).then((v) { - if (v != null) { - accessBloc.add(FetchTableData()); - } - }); - }, - borderRadius: 8, - child: const Text('+ Create Visitor Password ')), - ), - const SizedBox( - width: 10, - ), - Container( - width: size.width * 0.12, - decoration: containerDecoration, - child: DefaultButton( - borderRadius: 8, - backgroundColor: ColorsManager.whiteColors, - child: Text( - 'Admin Password', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: Colors.black), - ))) - ], - ), - const SizedBox( - height: 20, + FilterWidget( + size: MediaQuery.of(context).size, + tabs: accessBloc.tabs, + selectedIndex: accessBloc.selectedIndex, + onTabChanged: (index) { + accessBloc.add(TabChangedEvent(index)); + }, ), + const SizedBox(height: 20), + if (isSmallScreen || isHalfMediumScreen) + _buildSmallSearchFilters(context, accessBloc) + else + _buildNormalSearchWidgets(context, accessBloc), + const SizedBox(height: 20), + _buildVisitorAdminPasswords(context, accessBloc), + const SizedBox(height: 20), Expanded( child: DynamicTable( + uuidIndex: 1, isEmpty: filteredData.isEmpty, withCheckBox: false, - size: size, + size: MediaQuery.of(context).size, cellDecoration: containerDecoration, headers: const [ 'Name', @@ -276,12 +107,156 @@ class AccessManagementPage extends StatelessWidget { item.passwordStatus.value, ]; }).toList(), - ) - // : const Center(child: CircularProgressIndicator()), - ) + )), ], ), ); }))); } + + Wrap _buildVisitorAdminPasswords( + BuildContext context, AccessBloc accessBloc) { + return Wrap( + spacing: 10, + runSpacing: 10, + children: [ + Container( + width: 205, + height: 42, + decoration: containerDecoration, + child: DefaultButton( + onPressed: () { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return const VisitorPasswordDialog(); + }, + ).then((v) { + if (v != null) { + accessBloc.add(FetchTableData()); + } + }); + }, + borderRadius: 8, + child: Text( + '+ Create Visitor Password ', + style: context.textTheme.titleSmall! + .copyWith(color: Colors.white, fontSize: 12), + )), + ), + Container( + width: 133, + height: 42, + decoration: containerDecoration, + child: DefaultButton( + borderRadius: 8, + backgroundColor: ColorsManager.whiteColors, + child: Text( + 'Admin Password', + style: context.textTheme.titleSmall! + .copyWith(color: Colors.black, fontSize: 12), + )), + ), + ], + ); + } + + Row _buildNormalSearchWidgets(BuildContext context, AccessBloc accessBloc) { + return Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + textBaseline: TextBaseline.ideographic, + children: [ + SizedBox( + width: 250, + child: CustomWebTextField( + controller: accessBloc.passwordName, + height: 38, + isRequired: true, + textFieldName: 'Name', + description: '', + ), + ), + const SizedBox(width: 15), + SizedBox( + height: 70, + child: DateTimeWebWidget( + icon: Assets.calendarIcon, + isRequired: false, + title: 'Access Time', + size: MediaQuery.of(context).size, + endTime: () { + accessBloc.add(SelectTime(context: context, isStart: false)); + }, + startTime: () { + accessBloc.add(SelectTime(context: context, isStart: true)); + }, + firstString: BlocProvider.of(context).startTime, + secondString: BlocProvider.of(context).endTime, + ), + ), + const SizedBox(width: 15), + SearchResetButtons( + onSearch: () { + accessBloc.add(FilterDataEvent( + selectedTabIndex: + BlocProvider.of(context).selectedIndex, + passwordName: accessBloc.passwordName.text.toLowerCase(), + startTime: accessBloc.effectiveTimeTimeStamp, + endTime: accessBloc.expirationTimeTimeStamp)); + }, + onReset: () { + accessBloc.add(ResetSearch()); + }, + ), + ], + ); + } + + Widget _buildSmallSearchFilters(BuildContext context, AccessBloc accessBloc) { + return Wrap( + spacing: 20, + runSpacing: 10, + children: [ + SizedBox( + width: 300, + child: CustomWebTextField( + controller: accessBloc.passwordName, + isRequired: true, + height: 40, + textFieldName: 'Name', + description: '', + ), + ), + DateTimeWebWidget( + icon: Assets.calendarIcon, + isRequired: false, + title: 'Access Time', + size: MediaQuery.of(context).size, + endTime: () { + accessBloc.add(SelectTime(context: context, isStart: false)); + }, + startTime: () { + accessBloc.add(SelectTime(context: context, isStart: true)); + }, + firstString: BlocProvider.of(context).startTime, + secondString: BlocProvider.of(context).endTime, + ), + SearchResetButtons( + onSearch: () { + accessBloc.add(FilterDataEvent( + selectedTabIndex: + BlocProvider.of(context).selectedIndex, + passwordName: accessBloc.passwordName.text.toLowerCase(), + startTime: accessBloc.effectiveTimeTimeStamp, + endTime: accessBloc.expirationTimeTimeStamp)); + }, + onReset: () { + accessBloc.add(ResetSearch()); + }, + ), + ], + ); + } } diff --git a/lib/pages/auth/bloc/auth_bloc.dart b/lib/pages/auth/bloc/auth_bloc.dart index ea53fdd3..ef433028 100644 --- a/lib/pages/auth/bloc/auth_bloc.dart +++ b/lib/pages/auth/bloc/auth_bloc.dart @@ -135,6 +135,7 @@ class AuthBloc extends Bloc { emit(const LoginFailure(error: 'Something went wrong')); return; } + token = await AuthenticationAPI.loginWithEmail( model: LoginWithEmailModel( email: event.username, @@ -143,10 +144,10 @@ class AuthBloc extends Bloc { ); } catch (failure) { validate = 'Invalid Credentials!'; - emit(AuthInitialState()); - // emit(const LoginFailure(error: 'Something went wrong')); + emit(const LoginFailure(error: 'Invalid Credentials!')); return; } + if (token.accessTokenIsNotEmpty) { FlutterSecureStorage storage = const FlutterSecureStorage(); await storage.write( @@ -155,9 +156,9 @@ class AuthBloc extends Bloc { key: UserModel.userUuidKey, value: Token.decodeToken(token.accessToken)['uuid'].toString()); user = UserModel.fromToken(token); - debugPrint(token.accessToken); loginEmailController.clear(); loginPasswordController.clear(); + debugPrint("token " + token.accessToken); emit(LoginSuccess()); } else { emit(const LoginFailure(error: 'Something went wrong')); diff --git a/lib/pages/auth/bloc/auth_event.dart b/lib/pages/auth/bloc/auth_event.dart index a8786cbc..0026554c 100644 --- a/lib/pages/auth/bloc/auth_event.dart +++ b/lib/pages/auth/bloc/auth_event.dart @@ -1,5 +1,4 @@ import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; abstract class AuthEvent extends Equatable { const AuthEvent(); @@ -47,8 +46,7 @@ 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 {} diff --git a/lib/pages/auth/bloc/auth_state.dart b/lib/pages/auth/bloc/auth_state.dart index 8f2f6bd5..f3a95157 100644 --- a/lib/pages/auth/bloc/auth_state.dart +++ b/lib/pages/auth/bloc/auth_state.dart @@ -56,8 +56,7 @@ 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]; @@ -65,12 +64,12 @@ class TimerState extends AuthState { class AuthError extends AuthState { final String message; - String? code; - AuthError({required this.message, this.code}); + final String? code; + const AuthError({required this.message, this.code}); } class AuthTokenError extends AuthError { - AuthTokenError({required super.message, super.code}); + const AuthTokenError({required super.message, super.code}); } class AuthSuccess extends AuthState {} diff --git a/lib/pages/auth/view/forget_password_page.dart b/lib/pages/auth/view/forget_password_page.dart index 63b4d0f9..0ab2c2df 100644 --- a/lib/pages/auth/view/forget_password_page.dart +++ b/lib/pages/auth/view/forget_password_page.dart @@ -1,5 +1,4 @@ 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'; @@ -9,7 +8,6 @@ class ForgetPasswordPage extends StatelessWidget { @override Widget build(BuildContext context) { return const ResponsiveLayout( - desktopBody: ForgetPasswordWebPage(), - mobileBody: ForgetPasswordWebPage()); + desktopBody: ForgetPasswordWebPage(), mobileBody: ForgetPasswordWebPage()); } } diff --git a/lib/pages/auth/view/login_mobile_page.dart b/lib/pages/auth/view/login_mobile_page.dart index 1413db5d..1a5c8358 100644 --- a/lib/pages/auth/view/login_mobile_page.dart +++ b/lib/pages/auth/view/login_mobile_page.dart @@ -1,8 +1,8 @@ -import 'dart:ui'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_event.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_state.dart'; @@ -11,7 +11,7 @@ import 'package:syncrow_web/pages/auth/view/forget_password_page.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -import 'package:syncrow_web/pages/home/view/home_page.dart'; +import 'package:syncrow_web/utils/constants/routes_const.dart'; import 'package:syncrow_web/utils/style.dart'; class LoginMobilePage extends StatelessWidget { @@ -25,10 +25,7 @@ class LoginMobilePage extends StatelessWidget { listener: (context, state) { if (state is LoginSuccess) { // Navigate to home screen after successful login - Navigator.pushReplacement( - context, - MaterialPageRoute(builder: (context) => HomePage()), - ); + context.go(RoutesConst.home, extra: {'clearHistory': true}); } else if (state is LoginFailure) { // Show error message ScaffoldMessenger.of(context).showSnackBar( @@ -52,8 +49,6 @@ class LoginMobilePage extends StatelessWidget { Widget _buildLoginForm(BuildContext context) { final loginBloc = BlocProvider.of(context); - final TextEditingController _usernameController = TextEditingController(); - final TextEditingController _passwordController = TextEditingController(); return Center( child: Stack( children: [ @@ -102,11 +97,8 @@ class LoginMobilePage extends StatelessWidget { padding: EdgeInsets.all(20), 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: Column( @@ -117,9 +109,7 @@ class LoginMobilePage extends StatelessWidget { const Text( 'Login', style: TextStyle( - color: Colors.white, - fontSize: 24, - fontWeight: FontWeight.bold), + color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), ), const SizedBox(height: 30), Column( @@ -148,8 +138,7 @@ 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), @@ -173,8 +162,8 @@ class LoginMobilePage extends StatelessWidget { child: TextFormField( validator: loginBloc.validateEmail, controller: loginBloc.loginEmailController, - decoration: textBoxDecoration()! - .copyWith(hintText: 'Enter your email'), + decoration: + textBoxDecoration()!.copyWith(hintText: 'Enter your email'), style: const TextStyle(color: Colors.black), ), ), @@ -194,8 +183,7 @@ 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', ), @@ -213,16 +201,13 @@ class LoginMobilePage extends StatelessWidget { 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, + style: Theme.of(context).textTheme.bodySmall, ), ), ], @@ -233,15 +218,13 @@ class LoginMobilePage extends StatelessWidget { Transform.scale( 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, checkColor: Colors.black, shape: const CircleBorder(), onChanged: (bool? newValue) { - loginBloc.add( - CheckBoxEvent(newValue: newValue)); + loginBloc.add(CheckBoxEvent(newValue: newValue)); }, ), ), @@ -250,37 +233,30 @@ class LoginMobilePage extends StatelessWidget { 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)', - style: const TextStyle( - color: Colors.black), + style: const TextStyle(color: Colors.black), recognizer: TapGestureRecognizer() ..onTap = () { - loginBloc.launchURL( - 'https://example.com/terms'); + loginBloc.launchURL('https://example.com/terms'); }, ), TextSpan( text: ' (Legal Statement)', - style: const TextStyle( - color: Colors.black), + style: const TextStyle(color: Colors.black), recognizer: TapGestureRecognizer() ..onTap = () { - loginBloc.launchURL( - 'https://example.com/legal'); + loginBloc.launchURL('https://example.com/legal'); }, ), TextSpan( text: ' (Privacy Statement)', - style: const TextStyle( - color: Colors.black), + style: const TextStyle(color: Colors.black), recognizer: TapGestureRecognizer() ..onTap = () { - loginBloc.launchURL( - 'https://example.com/privacy'); + loginBloc.launchURL('https://example.com/privacy'); }, ), ], @@ -297,15 +273,12 @@ 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, + username: loginBloc.loginEmailController.text, + password: loginBloc.loginPasswordController.text, ), ); } @@ -320,8 +293,7 @@ class LoginMobilePage extends StatelessWidget { Flexible( child: Text( "Don't you have an account? ", - style: TextStyle( - color: Colors.white, fontSize: 13), + style: TextStyle(color: Colors.white, fontSize: 13), )), Text( "Sign up", diff --git a/lib/pages/auth/view/login_web_page.dart b/lib/pages/auth/view/login_web_page.dart index 7e9123ee..1c638287 100644 --- a/lib/pages/auth/view/login_web_page.dart +++ b/lib/pages/auth/view/login_web_page.dart @@ -13,6 +13,7 @@ import 'package:syncrow_web/pages/common/first_layer.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/style.dart'; class LoginWebPage extends StatefulWidget { @@ -22,7 +23,7 @@ class LoginWebPage extends StatefulWidget { State createState() => _LoginWebPageState(); } -class _LoginWebPageState extends State { +class _LoginWebPageState extends State with HelperResponsiveLayout { @override Widget build(BuildContext context) { return Scaffold( @@ -31,7 +32,7 @@ class _LoginWebPageState extends State { child: BlocConsumer( listener: (context, state) { if (state is LoginSuccess) { - context.go(RoutesConst.home); + GoRouter.of(context).go(RoutesConst.home); } else if (state is LoginFailure) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -50,9 +51,12 @@ class _LoginWebPageState extends State { Widget _buildLoginForm(BuildContext context, AuthState state) { final loginBloc = BlocProvider.of(context); + final isSmallScreen = isSmallScreenSize(context); + final isMediumScreen = isMediumScreenSize(context); Size size = MediaQuery.of(context).size; late ScrollController _scrollController; _scrollController = ScrollController(); + void _scrollToCenter() { final double middlePosition = _scrollController.position.maxScrollExtent / 2; _scrollController.animateTo( @@ -65,6 +69,7 @@ class _LoginWebPageState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { _scrollToCenter(); }); + return Stack( children: [ FirstLayer( @@ -74,356 +79,52 @@ class _LoginWebPageState extends State { shrinkWrap: true, children: [ Container( + width: 400, padding: EdgeInsets.all(size.width * 0.02), - margin: EdgeInsets.all(size.width * 0.09), + margin: EdgeInsets.all(size.width * 0.05), decoration: BoxDecoration( color: Colors.black.withOpacity(0.3), borderRadius: const BorderRadius.all(Radius.circular(20)), ), child: Center( - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Spacer(), - Expanded( - flex: 3, - child: SvgPicture.asset( - Assets.loginLogo, - ), - ), - const Spacer(), - Expanded( - flex: 3, - 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))), - child: Form( - key: loginBloc.loginFormKey, - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: size.width * 0.02, - vertical: size.width * 0.003), - child: Column( - 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), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - "Country/Region", - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith( - fontSize: 14, fontWeight: FontWeight.w400), - ), - const SizedBox( - height: 10, - ), - SizedBox( - child: DropdownButtonFormField( - padding: EdgeInsets.zero, - value: loginBloc.regionList!.any((region) => - region.id == loginBloc.regionUuid) - ? loginBloc.regionUuid - : null, - validator: loginBloc.validateRegion, - icon: const Icon( - Icons.keyboard_arrow_down_outlined, - ), - decoration: textBoxDecoration()!.copyWith( - errorStyle: const TextStyle(height: 0), - hintText: null, - ), - hint: SizedBox( - width: size.width * 0.12, - 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, - ), - ), - ), - isDense: true, - style: const TextStyle(color: Colors.black), - items: - loginBloc.regionList!.map((RegionModel region) { - return DropdownMenuItem( - value: region.id, - child: SizedBox( - width: size.width * 0.08, - child: Text(region.name)), - ); - }).toList(), - onChanged: (String? value) { - loginBloc.add(CheckEnableEvent()); - loginBloc.add(SelectRegionEvent(val: value!)); - }, - ), - ) - ], - ), - const SizedBox(height: 20.0), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - "Email", - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith( - fontSize: 14, fontWeight: FontWeight.w400), - ), - const SizedBox( - height: 10, - ), - SizedBox( - child: TextFormField( - onChanged: (value) { - 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), - ), - ), - ], - ), - const SizedBox(height: 20.0), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - "Password", - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith( - fontSize: 14, fontWeight: FontWeight.w400), - ), - const SizedBox( - height: 10, - ), - 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)); - }, - 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 - ), - style: const TextStyle(color: Colors.black), - ), - ), - ], - ), - const SizedBox( - height: 20, - ), - SizedBox( - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - InkWell( - onTap: () { - 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), - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - Row( - children: [ - Transform.scale( - scale: 1.2, // Adjust the scale as needed - child: Checkbox( - fillColor: - MaterialStateProperty.all(Colors.white), - activeColor: Colors.white, - value: loginBloc.isChecked, - checkColor: Colors.black, - shape: const CircleBorder(), - onChanged: (bool? newValue) { - loginBloc.add(CheckBoxEvent(newValue: newValue)); - }, - ), - ), - SizedBox( - width: size.width * 0.14, - child: RichText( - text: TextSpan( - text: 'Agree to ', - style: const TextStyle(color: Colors.white), - children: [ - TextSpan( - text: '(Terms of Service)', - style: const TextStyle( - 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)', - style: const TextStyle(color: Colors.black), - recognizer: TapGestureRecognizer() - ..onTap = () { - loginBloc.launchURL( - 'https://example.com/privacy'); - }, - ), - ], - ), - ), - ), - ], - ), - const SizedBox(height: 20.0), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - 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), - )), - onPressed: () { - if (loginBloc.loginFormKey.currentState! - .validate()) { - loginBloc.add(LoginButtonPressed( - regionUuid: loginBloc.regionUuid, - username: loginBloc.loginEmailController.text, - password: - loginBloc.loginPasswordController.text, - )); - } 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), - ), - ) - ], - ) - ], - ), + child: isSmallScreen || isMediumScreen + ? SizedBox( + width: 400, + child: Column( + // For small screens + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: 300, + child: SvgPicture.asset( + Assets.loginLogo, ), - ))), - const Spacer(), - ], - ), + ), + const SizedBox(height: 20), + _buildLoginFormFields(context, loginBloc, size), + ], + ), + ) + : Row( + // For larger screens + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Spacer(), + Expanded( + flex: 2, + child: SvgPicture.asset( + Assets.loginLogo, + ), + ), + const Spacer(), + Expanded( + flex: 2, + child: _buildLoginFormFields(context, loginBloc, size), + ), + const Spacer(), + ], + ), ), ), ], @@ -434,4 +135,335 @@ class _LoginWebPageState extends State { ], ); } + + Widget _buildLoginFormFields(BuildContext context, AuthBloc loginBloc, Size size) { + return 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)), + ), + child: Form( + key: loginBloc.loginFormKey, + child: Padding( + padding: + EdgeInsets.symmetric(horizontal: size.width * 0.02, vertical: size.width * 0.003), + child: Column( + 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), + _buildDropdownField(context, loginBloc, size), + const SizedBox(height: 20.0), + _buildEmailField(context, loginBloc), + const SizedBox(height: 20.0), + _buildPasswordField(context, loginBloc), + const SizedBox(height: 20), + _buildForgotPassword(context), + const SizedBox(height: 20), + _buildCheckbox(context, loginBloc, size), + const SizedBox(height: 20.0), + _buildSignInButton(context, loginBloc, size), + const SizedBox(height: 15.0), + _buildValidationMessage(loginBloc), + ], + ), + ), + ), + ); + } + + Widget _buildDropdownField(BuildContext context, AuthBloc loginBloc, Size size) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + "Country/Region", + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(fontSize: 14, fontWeight: FontWeight.w400), + ), + const SizedBox(height: 10), + SizedBox( + width: size.width * 0.8, + child: LayoutBuilder( + builder: (context, constraints) { + return DropdownButtonFormField( + value: loginBloc.regionList!.any((region) => region.id == loginBloc.regionUuid) + ? loginBloc.regionUuid + : null, + validator: loginBloc.validateRegion, + icon: const Icon( + Icons.keyboard_arrow_down_outlined, + size: 20, + ), + decoration: textBoxDecoration()!.copyWith( + errorStyle: const TextStyle(height: 0), + contentPadding: const EdgeInsets.symmetric( + vertical: 12, + horizontal: 10, + ), + ), + hint: Text( + 'Select your region/country', + 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( + value: region.id, + child: Container( + constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40), + child: Text( + region.name, + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + ); + }).toList(), + onChanged: (String? value) { + loginBloc.add(CheckEnableEvent()); + loginBloc.add(SelectRegionEvent(val: value!)); + }, + dropdownColor: Colors.white, + menuMaxHeight: size.height * 0.45, + selectedItemBuilder: (context) { + return loginBloc.regionList!.map((region) { + return Container( + constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40), + child: Text( + region.name, + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ); + }).toList(); + }, + ); + }, + ), + ), + ], + ); + } + + Widget _buildEmailField(BuildContext context, AuthBloc loginBloc) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + "Email", + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(fontSize: 14, fontWeight: FontWeight.w400), + ), + const SizedBox(height: 10), + SizedBox( + child: TextFormField( + onChanged: (value) { + loginBloc.add(CheckEnableEvent()); + }, + validator: loginBloc.loginValidateEmail, + controller: loginBloc.loginEmailController, + decoration: textBoxDecoration()!.copyWith( + errorStyle: const TextStyle(height: 0), + hintText: 'Enter your email address', + hintStyle: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400)), + style: const TextStyle(color: Colors.black), + ), + ), + ], + ); + } + + Widget _buildPasswordField(BuildContext context, AuthBloc loginBloc) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + "Password", + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(fontSize: 14, fontWeight: FontWeight.w400), + ), + const SizedBox(height: 10), + 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)); + }, + icon: SizedBox( + child: SvgPicture.asset( + loginBloc.obscureText ? Assets.visiblePassword : Assets.invisiblePassword, + height: 15, + width: 15, + ), + ), + ), + errorStyle: const TextStyle(height: 0), + ), + style: const TextStyle(color: Colors.black), + ), + ), + ], + ); + } + + Widget _buildForgotPassword(BuildContext context) { + return SizedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + InkWell( + onTap: () { + 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), + ), + ), + ], + ), + ); + } + + Widget _buildCheckbox(BuildContext context, AuthBloc loginBloc, Size size) { + return Row( + children: [ + Transform.scale( + scale: 1.2, + child: Checkbox( + fillColor: MaterialStateProperty.all(Colors.white), + activeColor: Colors.white, + value: loginBloc.isChecked, + checkColor: Colors.black, + shape: const CircleBorder(), + onChanged: (bool? newValue) { + loginBloc.add(CheckBoxEvent(newValue: newValue)); + }, + ), + ), + SizedBox( + width: 220, + child: RichText( + text: TextSpan( + text: 'Agree to ', + style: const TextStyle(color: Colors.white), + children: [ + TextSpan( + text: '(Terms of Service)', + style: const TextStyle(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)', + style: const TextStyle(color: Colors.black), + recognizer: TapGestureRecognizer() + ..onTap = () { + loginBloc.launchURL('https://example.com/privacy'); + }, + ), + ], + ), + ), + ), + ], + ); + } + + Widget _buildSignInButton(BuildContext context, AuthBloc loginBloc, Size size) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + 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), + )), + onPressed: () { + if (loginBloc.loginFormKey.currentState!.validate()) { + loginBloc.add(LoginButtonPressed( + regionUuid: loginBloc.regionUuid, + username: loginBloc.loginEmailController.text, + password: loginBloc.loginPasswordController.text, + )); + } else { + loginBloc.add(ChangeValidateEvent()); + } + }, + ), + ), + ], + ); + } + + Widget _buildValidationMessage(AuthBloc loginBloc) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + child: Text( + loginBloc.validate, + style: const TextStyle(fontWeight: FontWeight.w700, color: ColorsManager.red), + ), + ) + ], + ); + } } diff --git a/lib/pages/common/buttons/search_reset_buttons.dart b/lib/pages/common/buttons/search_reset_buttons.dart index a03b889a..7b63a485 100644 --- a/lib/pages/common/buttons/search_reset_buttons.dart +++ b/lib/pages/common/buttons/search_reset_buttons.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -23,14 +24,18 @@ class SearchResetButtons extends StatelessWidget { const SizedBox(height: 25), Center( child: Container( - height: 43, + height: 42, width: 100, decoration: containerDecoration, child: Center( child: DefaultButton( onPressed: onSearch, borderRadius: 9, - child: const Text('Search'), + child: Text( + 'Search', + style: context.textTheme.titleSmall! + .copyWith(color: Colors.white, fontSize: 12), + ), ), ), ), @@ -44,21 +49,19 @@ class SearchResetButtons extends StatelessWidget { const SizedBox(height: 25), Center( child: Container( - height: 43, + height: 42, width: 100, decoration: containerDecoration, child: Center( child: DefaultButton( backgroundColor: ColorsManager.whiteColors, borderRadius: 9, + onPressed: onReset, child: Text( 'Reset', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: Colors.black), + style: context.textTheme.titleSmall! + .copyWith(color: Colors.black, fontSize: 12), ), - onPressed: onReset, ), ), ), diff --git a/lib/pages/common/custom_table.dart b/lib/pages/common/custom_table.dart index fdeff6b6..a1532b68 100644 --- a/lib/pages/common/custom_table.dart +++ b/lib/pages/common/custom_table.dart @@ -14,6 +14,7 @@ class DynamicTable extends StatefulWidget { final void Function(bool?)? selectAll; final void Function(int, bool, dynamic)? onRowSelected; final List? initialSelectedIds; + final int uuidIndex; const DynamicTable({ super.key, required this.headers, @@ -26,6 +27,7 @@ class DynamicTable extends StatefulWidget { this.selectAll, this.onRowSelected, this.initialSelectedIds, + required this.uuidIndex, }); @override @@ -38,9 +40,24 @@ class _DynamicTableState extends State { @override void initState() { super.initState(); + _initializeSelection(); + } + + @override + void didUpdateWidget(DynamicTable oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.data != widget.data) { + _initializeSelection(); + } + } + + void _initializeSelection() { _selected = List.generate(widget.data.length, (index) { + // Check if the initialSelectedIds contains the deviceUuid + // uuidIndex is the index of the column containing the deviceUuid + final deviceUuid = widget.data[index][widget.uuidIndex]; return widget.initialSelectedIds != null && - widget.initialSelectedIds!.contains(widget.data[index][1]); + widget.initialSelectedIds!.contains(deviceUuid); }); } @@ -65,11 +82,13 @@ class _DynamicTableState extends State { child: Column( children: [ Container( - decoration: widget.headerDecoration ?? BoxDecoration(color: Colors.grey[200]), + decoration: widget.headerDecoration ?? + BoxDecoration(color: Colors.grey[200]), child: Row( children: [ if (widget.withCheckBox) _buildSelectAllCheckbox(), - ...widget.headers.map((header) => _buildTableHeaderCell(header)).toList(), + ...widget.headers + .map((header) => _buildTableHeaderCell(header)), ], ), ), @@ -93,7 +112,8 @@ class _DynamicTableState extends State { style: Theme.of(context) .textTheme .bodySmall! - .copyWith(color: ColorsManager.grayColor), + .copyWith( + color: ColorsManager.grayColor), ) ], ), @@ -113,11 +133,11 @@ class _DynamicTableState extends State { return Row( children: [ if (widget.withCheckBox) - _buildRowCheckbox(index, widget.size.height * 0.10), - ...row - .map((cell) => - _buildTableCell(cell.toString(), widget.size.height * 0.10)) - .toList(), + _buildRowCheckbox( + index, widget.size.height * 0.10), + ...row.map((cell) => _buildTableCell( + cell.toString(), + widget.size.height * 0.10)), ], ); }, @@ -190,6 +210,7 @@ class _DynamicTableState extends State { fontSize: 13, color: Color(0xFF999999), ), + maxLines: 2, ), ), ), @@ -222,7 +243,7 @@ class _DynamicTableState extends State { statusColor = ColorsManager.red; break; default: - statusColor = Colors.black; // Default color + statusColor = Colors.black; } return Expanded( @@ -241,11 +262,14 @@ class _DynamicTableState extends State { child: Text( content, style: TextStyle( - color: batteryLevel != null && batteryLevel < 20 + color: (batteryLevel != null && batteryLevel < 20) ? ColorsManager.red - : statusColor, // Use the passed color or default to black + : (batteryLevel != null && batteryLevel > 20) + ? ColorsManager.green + : statusColor, fontSize: 10, fontWeight: FontWeight.w400), + maxLines: 2, ), ), ); diff --git a/lib/pages/common/date_time_widget.dart b/lib/pages/common/date_time_widget.dart index 0b8fa0da..0965377e 100644 --- a/lib/pages/common/date_time_widget.dart +++ b/lib/pages/common/date_time_widget.dart @@ -36,7 +36,10 @@ class DateTimeWebWidget extends StatelessWidget { if (isRequired) Text( '* ', - style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: Colors.red), + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), ), Text( title, @@ -51,8 +54,9 @@ class DateTimeWebWidget extends StatelessWidget { height: 8, ), Container( - height: size.height * 0.055, - padding: const EdgeInsets.only(top: 10, bottom: 10, right: 30, left: 10), + // height: size.height * 0.056, + padding: + const EdgeInsets.only(top: 10, bottom: 10, right: 30, left: 10), decoration: containerDecoration, child: FittedBox( child: Column( @@ -65,10 +69,13 @@ class DateTimeWebWidget extends StatelessWidget { child: FittedBox( child: Text( firstString, - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: ColorsManager.grayColor, - fontSize: 12, - fontWeight: FontWeight.w400), + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: ColorsManager.grayColor, + fontSize: 12, + fontWeight: FontWeight.w400), ), )), const SizedBox( @@ -83,10 +90,13 @@ class DateTimeWebWidget extends StatelessWidget { child: FittedBox( child: Text( secondString, - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: ColorsManager.grayColor, - fontSize: 12, - fontWeight: FontWeight.w400), + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: ColorsManager.grayColor, + fontSize: 12, + fontWeight: FontWeight.w400), ), )), const SizedBox( diff --git a/lib/pages/common/filter/filter_widget.dart b/lib/pages/common/filter/filter_widget.dart index d6cfcc7e..1af23045 100644 --- a/lib/pages/common/filter/filter_widget.dart +++ b/lib/pages/common/filter/filter_widget.dart @@ -20,7 +20,7 @@ class FilterWidget extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: containerDecoration, - height: size.height * 0.05, + height: 40, child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: tabs.length, diff --git a/lib/pages/common/text_field/custom_text_field.dart b/lib/pages/common/text_field/custom_text_field.dart index 587a1f4f..f54d3991 100644 --- a/lib/pages/common/text_field/custom_text_field.dart +++ b/lib/pages/common/text_field/custom_text_field.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_web/core/extension/build_context_x.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; class StatefulTextField extends StatefulWidget { const StatefulTextField({ @@ -8,14 +8,14 @@ class StatefulTextField extends StatefulWidget { this.hintText = 'Please enter', required this.width, this.elevation = 0, - required this.controller, // Add the controller + required this.controller, }); final String title; final String hintText; final double width; final double elevation; - final TextEditingController controller; + final TextEditingController controller; @override State createState() => _StatefulTextFieldState(); @@ -26,7 +26,7 @@ class _StatefulTextFieldState extends State { Widget build(BuildContext context) { return CustomTextField( title: widget.title, - controller: widget.controller, + controller: widget.controller, hintText: widget.hintText, width: widget.width, elevation: widget.elevation, @@ -59,6 +59,7 @@ class CustomTextField extends StatelessWidget { Text( title, style: context.textTheme.bodyMedium!.copyWith( + fontSize: 13, fontWeight: FontWeight.w600, color: const Color(0xff000000), ), @@ -69,6 +70,7 @@ class CustomTextField extends StatelessWidget { borderRadius: BorderRadius.circular(8), child: Container( width: width, + height: 45, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), @@ -78,6 +80,7 @@ class CustomTextField extends StatelessWidget { style: const TextStyle(color: Colors.black), decoration: InputDecoration( hintText: hintText, + hintStyle: const TextStyle(fontSize: 12), contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), border: InputBorder.none, diff --git a/lib/pages/common/text_field/custom_web_textfield.dart b/lib/pages/common/text_field/custom_web_textfield.dart index 6fe0dc49..926a20f5 100644 --- a/lib/pages/common/text_field/custom_web_textfield.dart +++ b/lib/pages/common/text_field/custom_web_textfield.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/style.dart'; class CustomWebTextField extends StatelessWidget { @@ -11,6 +12,7 @@ class CustomWebTextField extends StatelessWidget { this.description, this.validator, this.hintText, + this.height, }); final bool isRequired; @@ -19,6 +21,7 @@ class CustomWebTextField extends StatelessWidget { final TextEditingController? controller; final String? Function(String?)? validator; final String? hintText; + final double? height; @override Widget build(BuildContext context) { @@ -66,6 +69,7 @@ class CustomWebTextField extends StatelessWidget { height: 7, ), Container( + height: height ?? 35, decoration: containerDecoration .copyWith(color: const Color(0xFFF5F6F7), boxShadow: [ BoxShadow( @@ -80,9 +84,9 @@ class CustomWebTextField extends StatelessWidget { controller: controller, style: const TextStyle(color: Colors.black), decoration: textBoxDecoration()!.copyWith( - errorStyle: - const TextStyle(height: 0), // Hide the error text space - + errorStyle: const TextStyle(height: 0), + hintStyle: context.textTheme.titleSmall! + .copyWith(color: Colors.grey, fontSize: 12), hintText: hintText ?? 'Please enter'), ), ), diff --git a/lib/pages/device_managment/ac/view/ac_device_control.dart b/lib/pages/device_managment/ac/view/ac_device_control.dart index 5c49ecfa..63d1799d 100644 --- a/lib/pages/device_managment/ac/view/ac_device_control.dart +++ b/lib/pages/device_managment/ac/view/ac_device_control.dart @@ -18,6 +18,7 @@ class AcDeviceControl extends StatelessWidget with HelperResponsiveLayout { @override Widget build(BuildContext context) { + final isExtraLarge = isExtraLargeScreenSize(context); final isLarge = isLargeScreenSize(context); final isMedium = isMediumScreenSize(context); return BlocProvider( @@ -31,7 +32,7 @@ class AcDeviceControl extends StatelessWidget with HelperResponsiveLayout { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: isLarge + crossAxisCount: isLarge || isExtraLarge ? 3 : isMedium ? 2 @@ -74,7 +75,7 @@ class AcDeviceControl extends StatelessWidget with HelperResponsiveLayout { } else if (state is AcsLoadingState) { return const Center(child: CircularProgressIndicator()); } else { - return const Center(child: Text('Error fetching status')); + return const Center(child: Text('Error fetching status')); } }, ), diff --git a/lib/pages/device_managment/ac/view/control_list/current_temp.dart b/lib/pages/device_managment/ac/view/control_list/current_temp.dart index fc00479b..2cf35228 100644 --- a/lib/pages/device_managment/ac/view/control_list/current_temp.dart +++ b/lib/pages/device_managment/ac/view/control_list/current_temp.dart @@ -116,16 +116,20 @@ class _CurrentTempState extends State { description: '°C', descriptionColor: ColorsManager.dialogBlueTitle, onIncrement: () { - setState(() { - _adjustedValue++; - }); - _onValueChanged(_adjustedValue); + if (_adjustedValue < 30) { + setState(() { + _adjustedValue++; + }); + _onValueChanged(_adjustedValue); + } }, onDecrement: () { - setState(() { - _adjustedValue--; - }); - _onValueChanged(_adjustedValue); + if (_adjustedValue > 20) { + setState(() { + _adjustedValue--; + }); + _onValueChanged(_adjustedValue); + } }), ], ), diff --git a/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart b/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart index edb02cb9..d64599f1 100644 --- a/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart +++ b/lib/pages/device_managment/all_devices/bloc/device_managment_bloc.dart @@ -28,14 +28,16 @@ class DeviceManagementBloc emit(DeviceManagementLoading()); try { final devices = await DevicesManagementApi().fetchDevices(); + _selectedDevices.clear(); _devices = devices; _calculateDeviceCounts(); emit(DeviceManagementLoaded( devices: devices, - selectedIndex: _selectedIndex, + selectedIndex: 0, onlineCount: _onlineCount, offlineCount: _offlineCount, lowBatteryCount: _lowBatteryCount, + selectedDevice: null, )); } catch (e) { emit(DeviceManagementInitial()); @@ -63,6 +65,8 @@ class DeviceManagementBloc onlineCount: _onlineCount, offlineCount: _offlineCount, lowBatteryCount: _lowBatteryCount, + selectedDevice: + _selectedDevices.isNotEmpty ? _selectedDevices.first : null, )); } } @@ -75,8 +79,10 @@ class DeviceManagementBloc void _onSelectDevice( SelectDevice event, Emitter emit) { - if (_selectedDevices.contains(event.selectedDevice)) { - _selectedDevices.remove(event.selectedDevice); + final selectedUuid = event.selectedDevice.uuid; + + if (_selectedDevices.any((device) => device.uuid == selectedUuid)) { + _selectedDevices.removeWhere((device) => device.uuid == selectedUuid); } else { _selectedDevices.add(event.selectedDevice); } @@ -129,6 +135,9 @@ class DeviceManagementBloc void _onSearchDevices( SearchDevices event, Emitter emit) { if (_devices.isNotEmpty) { + _selectedDevices.clear(); + _selectedIndex = 0; + final filteredDevices = _devices.where((device) { final matchesCommunity = event.community == null || event.community!.isEmpty || @@ -150,13 +159,13 @@ class DeviceManagementBloc false); return matchesCommunity && matchesUnit && matchesProductName; }).toList(); - emit(DeviceManagementFiltered( filteredDevices: filteredDevices, - selectedIndex: _selectedIndex, + selectedIndex: 0, onlineCount: _onlineCount, offlineCount: _offlineCount, lowBatteryCount: _lowBatteryCount, + selectedDevice: null, )); } } diff --git a/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart b/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart index fbe1f198..14ac7c5a 100644 --- a/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart +++ b/lib/pages/device_managment/all_devices/helper/route_controls_based_code.dart @@ -5,7 +5,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo import 'package:syncrow_web/pages/device_managment/ceiling_sensor/view/ceiling_sensor_controls.dart'; import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_status_view.dart'; import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_view.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/view/living_room_device_control.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/view/living_room_device_control.dart'; import 'package:syncrow_web/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart'; mixin RouteControlsBasedCode { diff --git a/lib/pages/device_managment/all_devices/view/device_managment_page.dart b/lib/pages/device_managment/all_devices/view/device_managment_page.dart index c56a661f..93480ae5 100644 --- a/lib/pages/device_managment/all_devices/view/device_managment_page.dart +++ b/lib/pages/device_managment/all_devices/view/device_managment_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_managment_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_managment_body.dart'; +import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; @@ -13,10 +14,13 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout { return BlocProvider( create: (context) => DeviceManagementBloc()..add(FetchDevices()), child: WebScaffold( - appBarTitle: Text( - 'Device Management', - style: Theme.of(context).textTheme.headlineLarge, + appBarTitle: FittedBox( + child: Text( + 'Device Management', + style: Theme.of(context).textTheme.headlineLarge, + ), ), + rightBody: const NavigateHomeGridView(), enableMenuSideba: isLargeScreenSize(context), scaffoldBody: BlocBuilder( builder: (context, state) { @@ -30,7 +34,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout { return DeviceManagementBody(devices: devices); } else { - return const Center(child: Text('No Devices Found')); + return const Center(child: Text('Error fetching Devices')); } }, ), diff --git a/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart b/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart index 3fb8b300..7f40bf37 100644 --- a/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart +++ b/lib/pages/device_managment/all_devices/widgets/device_managment_body.dart @@ -1,6 +1,6 @@ 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/utils/extension/build_context_x.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'; @@ -42,10 +42,14 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { offlineCount = state.offlineCount; lowBatteryCount = state.lowBatteryCount; isControlButtonEnabled = state.selectedDevice != null; + } else if (state is DeviceManagementInitial) { + devicesToShow = []; + selectedIndex = 0; + isControlButtonEnabled = false; } final tabs = [ - 'All (${devices.length})', + 'All', 'Online ($onlineCount)', 'Offline ($offlineCount)', 'Low Battery ($lowBatteryCount)', @@ -75,8 +79,8 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { const DeviceSearchFilters(), const SizedBox(height: 12), Container( - height: 43, - width: isSmallScreenSize(context) ? double.infinity : 100, + height: 45, + width: 100, decoration: containerDecoration, child: Center( child: DefaultButton( @@ -97,6 +101,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { child: Text( 'Control', style: TextStyle( + fontSize: 12, color: isControlButtonEnabled ? Colors.white : Colors.grey, @@ -124,6 +129,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { }, withCheckBox: true, size: context.screenSize, + uuidIndex: 2, headers: const [ 'Device Name', 'Product Name', @@ -144,7 +150,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { device.room?.name ?? '', device.batteryLevel != null ? '${device.batteryLevel}%' - : '', + : '-', formatDateTime(DateTime.fromMillisecondsSinceEpoch( (device.createTime ?? 0) * 1000)), device.online == true ? 'Online' : 'Offline', @@ -152,10 +158,15 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout { (device.updateTime ?? 0) * 1000)), ]; }).toList(), + initialSelectedIds: context + .read() + .selectedDevices + .map((device) => device.uuid!) + .toList(), isEmpty: devicesToShow.isEmpty, ), ), - ), + ) ], ); }, diff --git a/lib/pages/device_managment/all_devices/widgets/device_search_filters.dart b/lib/pages/device_managment/all_devices/widgets/device_search_filters.dart index 7af6293e..e2d43b1a 100644 --- a/lib/pages/device_managment/all_devices/widgets/device_search_filters.dart +++ b/lib/pages/device_managment/all_devices/widgets/device_search_filters.dart @@ -28,7 +28,7 @@ class _DeviceSearchFiltersState extends State @override Widget build(BuildContext context) { - return isLargeScreenSize(context) + return isExtraLargeScreenSize(context) ? Row( children: [ _buildSearchField("Community", communityController, 200), diff --git a/lib/pages/device_managment/ceiling_sensor/bloc/bloc.dart b/lib/pages/device_managment/ceiling_sensor/bloc/bloc.dart index fe928c94..570c1e9c 100644 --- a/lib/pages/device_managment/ceiling_sensor/bloc/bloc.dart +++ b/lib/pages/device_managment/ceiling_sensor/bloc/bloc.dart @@ -62,16 +62,19 @@ class CeilingSensorBloc extends Bloc { deviceStatus.noBodyTime = event.value; } else if (event.code == 'moving_max_dis') { deviceStatus.maxDistance = event.value; + } else if (event.code == 'scene') { + deviceStatus.spaceType = getSpaceType(event.value); } emit(CeilingUpdateState(ceilingSensorModel: deviceStatus)); await _runDeBouncer( - deviceId: deviceId, code: event.code, value: event.value); + deviceId: deviceId, code: event.code, value: event.value, emit: emit); } _runDeBouncer({ required String deviceId, required String code, required dynamic value, + required Emitter emit, }) { if (_timer != null) { _timer!.cancel(); @@ -84,6 +87,11 @@ class CeilingSensorBloc extends Bloc { if (!response) { add(CeilingInitialEvent()); } + if (response == true && code == 'scene') { + emit(CeilingLoadingInitialState()); + await Future.delayed(const Duration(seconds: 1)); + add(CeilingInitialEvent()); + } } catch (_) { await Future.delayed(const Duration(milliseconds: 500)); add(CeilingInitialEvent()); diff --git a/lib/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart b/lib/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart index e8118168..a79cbc19 100644 --- a/lib/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart +++ b/lib/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart @@ -10,6 +10,7 @@ class CeilingSensorModel { String bodyMovement; String noBodyTime; int maxDistance; + SpaceTypes spaceType; CeilingSensorModel({ required this.presenceState, @@ -20,6 +21,7 @@ class CeilingSensorModel { required this.bodyMovement, required this.noBodyTime, required this.maxDistance, + required this.spaceType, }); factory CeilingSensorModel.fromJson(List jsonList) { @@ -31,6 +33,7 @@ class CeilingSensorModel { String _bodyMovement = 'none'; String _noBodyTime = 'none'; int _maxDis = 0; + SpaceTypes _spaceType = SpaceTypes.none; try { for (var status in jsonList) { @@ -38,17 +41,26 @@ class CeilingSensorModel { case 'presence_state': _presenceState = status.value ?? 'none'; break; + case 'scene': + _spaceType = getSpaceType(status.value ?? 'none'); + break; case 'sensitivity': - _sensitivity = status.value is int ? status.value : int.tryParse(status.value ?? '1') ?? 1; + _sensitivity = status.value is int + ? status.value + : int.tryParse(status.value ?? '1') ?? 1; break; case 'checking_result': _checkingResult = status.value ?? ''; break; case 'presence_range': - _presenceRange = status.value is int ? status.value : int.tryParse(status.value ?? '0') ?? 0; + _presenceRange = status.value is int + ? status.value + : int.tryParse(status.value ?? '0') ?? 0; break; case 'sports_para': - _sportsPara = status.value is int ? status.value : int.tryParse(status.value ?? '0') ?? 0; + _sportsPara = status.value is int + ? status.value + : int.tryParse(status.value ?? '0') ?? 0; break; case 'body_movement': _bodyMovement = status.value ?? ''; @@ -57,7 +69,9 @@ class CeilingSensorModel { _noBodyTime = status.value ?? 'none'; break; case 'moving_max_dis': - _maxDis = status.value is int ? status.value : int.tryParse(status.value ?? '0') ?? 0; + _maxDis = status.value is int + ? status.value + : int.tryParse(status.value ?? '0') ?? 0; break; } } @@ -74,6 +88,31 @@ class CeilingSensorModel { bodyMovement: _bodyMovement, noBodyTime: _noBodyTime, maxDistance: _maxDis, + spaceType: _spaceType, ); } } + +enum SpaceTypes { + none, + parlour, + area, + toilet, + bedroom, +} + +SpaceTypes getSpaceType(String value) { + switch (value) { + case 'parlour': + return SpaceTypes.parlour; + case 'area': + return SpaceTypes.area; + case 'toilet': + return SpaceTypes.toilet; + case 'bedroom': + return SpaceTypes.bedroom; + case 'none': + default: + return SpaceTypes.none; + } +} diff --git a/lib/pages/device_managment/ceiling_sensor/view/ceiling_sensor_controls.dart b/lib/pages/device_managment/ceiling_sensor/view/ceiling_sensor_controls.dart index 2bb7eb76..bf24ae0f 100644 --- a/lib/pages/device_managment/ceiling_sensor/view/ceiling_sensor_controls.dart +++ b/lib/pages/device_managment/ceiling_sensor/view/ceiling_sensor_controls.dart @@ -24,6 +24,7 @@ class CeilingSensorControls extends StatelessWidget @override Widget build(BuildContext context) { + final isExtraLarge = isExtraLargeScreenSize(context); final isLarge = isLargeScreenSize(context); final isMedium = isMediumScreenSize(context); return BlocProvider( @@ -35,8 +36,8 @@ class CeilingSensorControls extends StatelessWidget state is CeilingReportsLoadingState) { return const Center(child: CircularProgressIndicator()); } else if (state is CeilingUpdateState) { - return _buildGridView( - context, state.ceilingSensorModel, isLarge, isMedium); + return _buildGridView(context, state.ceilingSensorModel, + isExtraLarge, isLarge, isMedium); } else if (state is CeilingReportsState) { return ReportsTable( report: state.deviceReport, @@ -58,7 +59,8 @@ class CeilingSensorControls extends StatelessWidget ); } else if (state is CeilingReportsFailedState) { final model = context.read().deviceStatus; - return _buildGridView(context, model, isLarge, isMedium); + return _buildGridView( + context, model, isExtraLarge, isLarge, isMedium); } return const Center(child: Text('Error fetching status')); }, @@ -67,13 +69,13 @@ class CeilingSensorControls extends StatelessWidget } Widget _buildGridView(BuildContext context, CeilingSensorModel model, - bool isLarge, bool isMedium) { + bool isExtraLarge, bool isLarge, bool isMedium) { return GridView( padding: const EdgeInsets.symmetric(horizontal: 50), shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: isLarge + crossAxisCount: isLarge || isExtraLarge ? 3 : isMedium ? 2 @@ -96,15 +98,15 @@ class CeilingSensorControls extends StatelessWidget postfix: 'm', description: 'Detection Range', ), - const PresenceSpaceType( - listOfIcons: [ - Assets.office, - Assets.parlour, - Assets.dyi, - Assets.bathroom, - Assets.bedroom, - ], + PresenceSpaceType( description: 'Space Type', + value: model.spaceType, + action: (String value) => context.read().add( + CeilingChangeValueEvent( + code: 'scene', + value: value, + ), + ), ), PresenceUpdateData( value: model.sensitivity.toDouble(), @@ -138,7 +140,7 @@ class CeilingSensorControls extends StatelessWidget PresenceNoBodyTime( value: model.noBodyTime, title: 'Nobody Time:', - // description: 'hr', + description: '', action: (String value) => context.read().add( CeilingChangeValueEvent( code: 'nobody_time', diff --git a/lib/pages/device_managment/door_lock/view/door_lock_status_view.dart b/lib/pages/device_managment/door_lock/view/door_lock_status_view.dart index 12db871f..536b0d97 100644 --- a/lib/pages/device_managment/door_lock/view/door_lock_status_view.dart +++ b/lib/pages/device_managment/door_lock/view/door_lock_status_view.dart @@ -34,9 +34,9 @@ class DoorLockView extends StatelessWidget { } else if (state is UpdateState) { return _buildStatusControls(context, state.smartDoorModel); } else if (state is DoorLockControlError) { - return Center(child: Text(state.message)); + return const SizedBox(); } else { - return const Center(child: CircularProgressIndicator()); + return const Center(child: Text('Error fetching status')); } }, ), diff --git a/lib/pages/device_managment/gateway/view/gateway_view.dart b/lib/pages/device_managment/gateway/view/gateway_view.dart index 4f14161e..9fbe8cb9 100644 --- a/lib/pages/device_managment/gateway/view/gateway_view.dart +++ b/lib/pages/device_managment/gateway/view/gateway_view.dart @@ -14,6 +14,7 @@ class GateWayControls extends StatelessWidget with HelperResponsiveLayout { @override Widget build(BuildContext context) { + final isExtraLarge = isExtraLargeScreenSize(context); final isLarge = isLargeScreenSize(context); final isMedium = isMediumScreenSize(context); @@ -29,7 +30,7 @@ class GateWayControls extends StatelessWidget with HelperResponsiveLayout { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: isLarge + crossAxisCount: isLarge || isExtraLarge ? 3 : isMedium ? 2 @@ -45,7 +46,7 @@ class GateWayControls extends StatelessWidget with HelperResponsiveLayout { }, ); } else { - return const Center(child: Text('Error fetching devices')); + return const Center(child: Text('Error fetching status')); } }, ), diff --git a/lib/pages/device_managment/shared/device_control_dialog.dart b/lib/pages/device_managment/shared/device_control_dialog.dart index d115f57e..cde54047 100644 --- a/lib/pages/device_managment/shared/device_control_dialog.dart +++ b/lib/pages/device_managment/shared/device_control_dialog.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:syncrow_web/core/extension/build_context_x.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/helper/route_controls_based_code.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; diff --git a/lib/pages/device_managment/shared/navigate_home_grid_view.dart b/lib/pages/device_managment/shared/navigate_home_grid_view.dart new file mode 100644 index 00000000..7969e2b0 --- /dev/null +++ b/lib/pages/device_managment/shared/navigate_home_grid_view.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/constants/routes_const.dart'; + +class NavigateHomeGridView extends StatelessWidget { + const NavigateHomeGridView({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + InkWell( + onTap: () { + context.go(RoutesConst.home); + }, + child: SvgPicture.asset( + height: 20, + width: 20, + Assets.grid, + ), + ), + const SizedBox( + width: 10, + ) + ], + ); + } +} diff --git a/lib/pages/device_managment/shared/sensors_widgets/presence_space_type.dart b/lib/pages/device_managment/shared/sensors_widgets/presence_space_type.dart index 185144e1..2f593abe 100644 --- a/lib/pages/device_managment/shared/sensors_widgets/presence_space_type.dart +++ b/lib/pages/device_managment/shared/sensors_widgets/presence_space_type.dart @@ -1,21 +1,33 @@ -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_web/core/extension/build_context_x.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart'; class PresenceSpaceType extends StatelessWidget { const PresenceSpaceType({ super.key, - required this.listOfIcons, required this.description, + required this.value, + required this.action, }); - final List listOfIcons; final String description; + final SpaceTypes value; + final void Function(String value) action; @override Widget build(BuildContext context) { + final Map spaceTypeIcons = { + SpaceTypes.none: Assets.office, + SpaceTypes.parlour: Assets.parlour, + SpaceTypes.area: Assets.dyi, + SpaceTypes.toilet: Assets.bathroom, + SpaceTypes.bedroom: Assets.bedroom, + }; + return DeviceControlsContainer( child: Column( mainAxisSize: MainAxisSize.min, @@ -35,13 +47,28 @@ class PresenceSpaceType extends StatelessWidget { Wrap( runSpacing: 8, spacing: 16, - children: [ - ...listOfIcons.map((icon) => SvgPicture.asset( + children: spaceTypeIcons.entries.map((entry) { + final icon = entry.value; + final spaceType = entry.key; + return GestureDetector( + onTap: () => action(spaceType.name), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(100), + border: Border.all( + color: value == spaceType + ? ColorsManager.blueColor + : Colors.transparent, + ), + ), + child: SvgPicture.asset( icon, width: 40, height: 40, - )), - ], + ), + ), + ); + }).toList(), ), ], ), diff --git a/lib/pages/device_managment/shared/sensors_widgets/presense_nobody_time.dart b/lib/pages/device_managment/shared/sensors_widgets/presense_nobody_time.dart index 6d37ec3a..4e64ee1e 100644 --- a/lib/pages/device_managment/shared/sensors_widgets/presense_nobody_time.dart +++ b/lib/pages/device_managment/shared/sensors_widgets/presense_nobody_time.dart @@ -23,6 +23,8 @@ class PresenceNoBodyTime extends StatefulWidget { class _PresenceUpdateDataState extends State { late String _currentValue; + late String _numericValue; + late String _unit; final List nobodyTimeRange = [ 'none', @@ -40,29 +42,45 @@ class _PresenceUpdateDataState extends State { void initState() { super.initState(); _currentValue = widget.value; + _numericValue = _extractNumericValue(_currentValue); + _unit = _extractUnit(_currentValue); + } + + String _extractNumericValue(String value) { + if (value == 'none') return '0'; + return value.replaceAll(RegExp(r'[a-zA-Z]'), '').trim(); + } + + String _extractUnit(String value) { + if (value == 'none') return ''; + if (value.endsWith('s')) return 's'; + if (value.endsWith('min')) return 'min'; + if (value.endsWith('hour')) return 'hr'; + return ''; } void _onValueChanged(String newValue) { + setState(() { + _currentValue = newValue; + _numericValue = _extractNumericValue(newValue); + _unit = _extractUnit(newValue); + }); widget.action(newValue); } void _incrementValue() { int currentIndex = nobodyTimeRange.indexOf(_currentValue); if (currentIndex < nobodyTimeRange.length - 1) { - setState(() { - _currentValue = nobodyTimeRange[currentIndex + 1]; - }); - _onValueChanged(_currentValue); + String newValue = nobodyTimeRange[currentIndex + 1]; + _onValueChanged(newValue); } } void _decrementValue() { int currentIndex = nobodyTimeRange.indexOf(_currentValue); if (currentIndex > 0) { - setState(() { - _currentValue = nobodyTimeRange[currentIndex - 1]; - }); - _onValueChanged(_currentValue); + String newValue = nobodyTimeRange[currentIndex - 1]; + _onValueChanged(newValue); } } @@ -81,11 +99,12 @@ class _PresenceUpdateDataState extends State { fontSize: 10), ), IncrementDecrementWidget( - value: _currentValue, - description: widget.description ?? '', - descriptionColor: ColorsManager.blackColor, - onIncrement: _incrementValue, - onDecrement: _decrementValue), + value: _numericValue, + description: _unit, + descriptionColor: ColorsManager.blackColor, + onIncrement: _incrementValue, + onDecrement: _decrementValue, + ), ], ), ); diff --git a/lib/pages/device_managment/shared/table/report_table.dart b/lib/pages/device_managment/shared/table/report_table.dart index 619df168..ef189f3a 100644 --- a/lib/pages/device_managment/shared/table/report_table.dart +++ b/lib/pages/device_managment/shared/table/report_table.dart @@ -6,6 +6,8 @@ import 'package:syncrow_web/pages/device_managment/shared/table/table_header.dar class ReportsTable extends StatelessWidget { final DeviceReport report; + final String? thirdColumnTitle; + final String? thirdColumnDescription; final Function(int index) onRowTap; final VoidCallback onClose; @@ -14,6 +16,8 @@ class ReportsTable extends StatelessWidget { required this.report, required this.onRowTap, required this.onClose, + this.thirdColumnTitle, + this.thirdColumnDescription, }); @override @@ -32,33 +36,34 @@ class ReportsTable extends StatelessWidget { children: [ TableRow( decoration: BoxDecoration(color: Colors.grey.shade200), - children: const [ - TableHeader(title: 'Date'), - TableHeader(title: 'Time'), - TableHeader(title: 'Status'), + children: [ + const TableHeader(title: 'Date'), + const TableHeader(title: 'Time'), + TableHeader(title: thirdColumnTitle ?? 'Status'), ], ), - ...report.data!.asMap().entries.map((entry) { - int index = entry.key; - DeviceEvent data = entry.value; + if (report.data != null) + ...report.data!.asMap().entries.map((entry) { + int index = entry.key; + DeviceEvent data = entry.value; - // Parse eventTime into Date and Time - DateTime eventDateTime = - DateTime.fromMillisecondsSinceEpoch(data.eventTime!); - String date = DateFormat('dd/MM/yyyy').format(eventDateTime); - String time = DateFormat('HH:mm').format(eventDateTime); + // Parse eventTime into Date and Time + DateTime eventDateTime = + DateTime.fromMillisecondsSinceEpoch(data.eventTime!); + String date = DateFormat('dd/MM/yyyy').format(eventDateTime); + String time = DateFormat('HH:mm').format(eventDateTime); - return TableRow( - children: [ - TableCellWidget(value: date), - TableCellWidget(value: time), - TableCellWidget( - value: data.value!, - onTap: () => onRowTap(index), - ), - ], - ); - }).toList(), + return TableRow( + children: [ + TableCellWidget(value: date), + TableCellWidget(value: time), + TableCellWidget( + value: '${data.value!} $thirdColumnDescription', + onTap: () => onRowTap(index), + ), + ], + ); + }), ], ), ), diff --git a/lib/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart b/lib/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart similarity index 82% rename from lib/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart rename to lib/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart index 8adc11cb..de06248b 100644 --- a/lib/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart +++ b/lib/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart @@ -4,7 +4,7 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/models/living_room_model.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart'; import 'package:syncrow_web/services/devices_mang_api.dart'; part 'living_room_event.dart'; @@ -24,18 +24,15 @@ class LivingRoomBloc extends Bloc { LivingRoomFetchDeviceStatus event, Emitter emit) async { emit(LivingRoomDeviceStatusLoading()); try { - final status = - await DevicesManagementApi().getDeviceStatus(event.deviceId); - deviceStatus = - LivingRoomStatusModel.fromJson(event.deviceId, status.status); + final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); + deviceStatus = LivingRoomStatusModel.fromJson(event.deviceId, status.status); emit(LivingRoomDeviceStatusLoaded(deviceStatus)); } catch (e) { emit(LivingRoomDeviceManagementError(e.toString())); } } - FutureOr _livingRoomControl( - LivingRoomControl event, Emitter emit) async { + FutureOr _livingRoomControl(LivingRoomControl event, Emitter emit) async { final oldValue = _getValueByCode(event.code); _updateLocalValue(event.code, event.value); @@ -63,8 +60,8 @@ class LivingRoomBloc extends Bloc { } _timer = Timer(const Duration(seconds: 1), () async { try { - final response = await DevicesManagementApi() - .deviceControl(deviceId, Status(code: code, value: value)); + final response = + await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value)); if (!response) { _revertValueAndEmit(deviceId, code, oldValue, emit); } @@ -74,8 +71,8 @@ class LivingRoomBloc extends Bloc { }); } - void _revertValueAndEmit(String deviceId, String code, dynamic oldValue, - Emitter emit) { + void _revertValueAndEmit( + String deviceId, String code, dynamic oldValue, Emitter emit) { _updateLocalValue(code, oldValue); emit(LivingRoomDeviceStatusLoaded(deviceStatus)); emit(const LivingRoomControlError('Failed to control the device.')); diff --git a/lib/pages/device_managment/living_room_switch/bloc/living_room_event.dart b/lib/pages/device_managment/three_gang_switch/bloc/living_room_event.dart similarity index 100% rename from lib/pages/device_managment/living_room_switch/bloc/living_room_event.dart rename to lib/pages/device_managment/three_gang_switch/bloc/living_room_event.dart diff --git a/lib/pages/device_managment/living_room_switch/bloc/living_room_state.dart b/lib/pages/device_managment/three_gang_switch/bloc/living_room_state.dart similarity index 100% rename from lib/pages/device_managment/living_room_switch/bloc/living_room_state.dart rename to lib/pages/device_managment/three_gang_switch/bloc/living_room_state.dart diff --git a/lib/pages/device_managment/living_room_switch/helper/living_room_helper.dart b/lib/pages/device_managment/three_gang_switch/helper/living_room_helper.dart similarity index 65% rename from lib/pages/device_managment/living_room_switch/helper/living_room_helper.dart rename to lib/pages/device_managment/three_gang_switch/helper/living_room_helper.dart index 8e3f4985..255204b2 100644 --- a/lib/pages/device_managment/living_room_switch/helper/living_room_helper.dart +++ b/lib/pages/device_managment/three_gang_switch/helper/living_room_helper.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/widgets/cieling_light.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/widgets/spot_light.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/widgets/wall_light.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/widgets/cieling_light.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/widgets/spot_light.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/widgets/wall_light.dart'; mixin LivingRoomHelper { Widget livingRoomControlWidgets( @@ -18,4 +18,3 @@ mixin LivingRoomHelper { } } } - diff --git a/lib/pages/device_managment/living_room_switch/models/living_room_model.dart b/lib/pages/device_managment/three_gang_switch/models/living_room_model.dart similarity index 100% rename from lib/pages/device_managment/living_room_switch/models/living_room_model.dart rename to lib/pages/device_managment/three_gang_switch/models/living_room_model.dart diff --git a/lib/pages/device_managment/living_room_switch/view/living_room_device_control.dart b/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart similarity index 70% rename from lib/pages/device_managment/living_room_switch/view/living_room_device_control.dart rename to lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart index 60e656ad..e3a42a7a 100644 --- a/lib/pages/device_managment/living_room_switch/view/living_room_device_control.dart +++ b/lib/pages/device_managment/three_gang_switch/view/living_room_device_control.dart @@ -1,12 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/models/living_room_model.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/widgets/living_toggle_widget.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/widgets/living_toggle_widget.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -class LivingRoomDeviceControl extends StatelessWidget - with HelperResponsiveLayout { +class LivingRoomDeviceControl extends StatelessWidget with HelperResponsiveLayout { final String deviceId; const LivingRoomDeviceControl({super.key, required this.deviceId}); @@ -14,17 +13,16 @@ class LivingRoomDeviceControl extends StatelessWidget @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => LivingRoomBloc(deviceId: deviceId) - ..add(LivingRoomFetchDeviceStatus(deviceId)), + create: (context) => + LivingRoomBloc(deviceId: deviceId)..add(LivingRoomFetchDeviceStatus(deviceId)), child: BlocBuilder( builder: (context, state) { if (state is LivingRoomDeviceStatusLoading) { return const Center(child: CircularProgressIndicator()); } else if (state is LivingRoomDeviceStatusLoaded) { return _buildStatusControls(context, state.status); - } else if (state is LivingRoomDeviceManagementError || - state is LivingRoomControlError) { - return Center(child: Text(state.toString())); + } else if (state is LivingRoomDeviceManagementError || state is LivingRoomControlError) { + return const Center(child: Text('Error fetching status')); } else { return const Center(child: CircularProgressIndicator()); } @@ -33,8 +31,8 @@ class LivingRoomDeviceControl extends StatelessWidget ); } - Widget _buildStatusControls( - BuildContext context, LivingRoomStatusModel status) { + Widget _buildStatusControls(BuildContext context, LivingRoomStatusModel status) { + final isExtraLarge = isExtraLargeScreenSize(context); final isLarge = isLargeScreenSize(context); final isMedium = isMediumScreenSize(context); return GridView( @@ -42,7 +40,7 @@ class LivingRoomDeviceControl extends StatelessWidget shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: isLarge + crossAxisCount: isLarge || isExtraLarge ? 3 : isMedium ? 2 diff --git a/lib/pages/device_managment/living_room_switch/widgets/cieling_light.dart b/lib/pages/device_managment/three_gang_switch/widgets/cieling_light.dart similarity index 89% rename from lib/pages/device_managment/living_room_switch/widgets/cieling_light.dart rename to lib/pages/device_managment/three_gang_switch/widgets/cieling_light.dart index b529e6e6..629c131b 100644 --- a/lib/pages/device_managment/living_room_switch/widgets/cieling_light.dart +++ b/lib/pages/device_managment/three_gang_switch/widgets/cieling_light.dart @@ -2,16 +2,12 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class CeilingLight extends StatelessWidget { - const CeilingLight( - {super.key, - required this.value, - required this.code, - required this.deviceId}); + const CeilingLight({super.key, required this.value, required this.code, required this.deviceId}); final bool value; final String code; diff --git a/lib/pages/device_managment/living_room_switch/widgets/living_toggle_widget.dart b/lib/pages/device_managment/three_gang_switch/widgets/living_toggle_widget.dart similarity index 95% rename from lib/pages/device_managment/living_room_switch/widgets/living_toggle_widget.dart rename to lib/pages/device_managment/three_gang_switch/widgets/living_toggle_widget.dart index 701d412b..abc75e66 100644 --- a/lib/pages/device_managment/living_room_switch/widgets/living_toggle_widget.dart +++ b/lib/pages/device_managment/three_gang_switch/widgets/living_toggle_widget.dart @@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; diff --git a/lib/pages/device_managment/living_room_switch/widgets/spot_light.dart b/lib/pages/device_managment/three_gang_switch/widgets/spot_light.dart similarity index 89% rename from lib/pages/device_managment/living_room_switch/widgets/spot_light.dart rename to lib/pages/device_managment/three_gang_switch/widgets/spot_light.dart index 14c4fe5e..6ac71a38 100644 --- a/lib/pages/device_managment/living_room_switch/widgets/spot_light.dart +++ b/lib/pages/device_managment/three_gang_switch/widgets/spot_light.dart @@ -2,16 +2,12 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class SpotLight extends StatelessWidget { - const SpotLight( - {super.key, - required this.value, - required this.code, - required this.deviceId}); + const SpotLight({super.key, required this.value, required this.code, required this.deviceId}); final bool value; final String code; diff --git a/lib/pages/device_managment/living_room_switch/widgets/wall_light.dart b/lib/pages/device_managment/three_gang_switch/widgets/wall_light.dart similarity index 89% rename from lib/pages/device_managment/living_room_switch/widgets/wall_light.dart rename to lib/pages/device_managment/three_gang_switch/widgets/wall_light.dart index 8e168ec0..12c814ac 100644 --- a/lib/pages/device_managment/living_room_switch/widgets/wall_light.dart +++ b/lib/pages/device_managment/three_gang_switch/widgets/wall_light.dart @@ -2,16 +2,12 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class WallLight extends StatelessWidget { - const WallLight( - {super.key, - required this.value, - required this.code, - required this.deviceId}); + const WallLight({super.key, required this.value, required this.code, required this.deviceId}); final bool value; final String code; diff --git a/lib/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart b/lib/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart index 068cb27d..8448ef67 100644 --- a/lib/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart +++ b/lib/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart @@ -21,6 +21,7 @@ class WallSensorControls extends StatelessWidget with HelperResponsiveLayout { @override Widget build(BuildContext context) { + final isExtraLarge = isExtraLargeScreenSize(context); final isLarge = isLargeScreenSize(context); final isMedium = isMediumScreenSize(context); return BlocProvider( @@ -32,11 +33,13 @@ class WallSensorControls extends StatelessWidget with HelperResponsiveLayout { state is DeviceReportsLoadingState) { return const Center(child: CircularProgressIndicator()); } else if (state is WallSensorUpdateState) { - return _buildGridView( - context, state.wallSensorModel, isLarge, isMedium); + return _buildGridView(context, state.wallSensorModel, isExtraLarge, + isLarge, isMedium); } else if (state is DeviceReportsState) { return ReportsTable( report: state.deviceReport, + thirdColumnTitle: "Value", + thirdColumnDescription: "Lux", onRowTap: (index) {}, onClose: () { context.read().add(BackToGridViewEvent()); @@ -51,7 +54,8 @@ class WallSensorControls extends StatelessWidget with HelperResponsiveLayout { ); } else if (state is DeviceReportsFailedState) { final model = context.read().deviceStatus; - return _buildGridView(context, model, isLarge, isMedium); + return _buildGridView( + context, model, isExtraLarge, isLarge, isMedium); } return const Center(child: Text('Error fetching status')); }, @@ -60,13 +64,13 @@ class WallSensorControls extends StatelessWidget with HelperResponsiveLayout { } Widget _buildGridView(BuildContext context, WallSensorModel model, - bool isLarge, bool isMedium) { + bool isExtraLarge, bool isLarge, bool isMedium) { return GridView( padding: const EdgeInsets.symmetric(horizontal: 50), shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: isLarge + crossAxisCount: isLarge || isExtraLarge ? 3 : isMedium ? 2 diff --git a/lib/pages/home/bloc/home_bloc.dart b/lib/pages/home/bloc/home_bloc.dart index 42a63592..cc40b8fe 100644 --- a/lib/pages/home/bloc/home_bloc.dart +++ b/lib/pages/home/bloc/home_bloc.dart @@ -16,11 +16,11 @@ class HomeBloc extends Bloc { final BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration(); List sourcesList = []; List destinationsList = []; - static UserModel? user; + UserModel? user; HomeBloc() : super((HomeInitial())) { on(_createNode); - fetchUserInfo(); + on(_fetchUserInfo); } void _createNode(CreateNewNode event, Emitter emit) async { @@ -39,11 +39,12 @@ class HomeBloc extends Bloc { emit(HomeUpdateTree(graph: graph, builder: builder)); } - Future fetchUserInfo() async { + Future _fetchUserInfo(FetchUserInfo event, Emitter emit) async { try { - var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); + var uuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey); user = await HomeApi().fetchUserInfo(uuid); - emit(HomeUserInfoLoaded(user!)); // Emit state after fetching user info + emit(HomeInitial()); } catch (e) { return; } diff --git a/lib/pages/home/bloc/home_event.dart b/lib/pages/home/bloc/home_event.dart index da517943..963202b9 100644 --- a/lib/pages/home/bloc/home_event.dart +++ b/lib/pages/home/bloc/home_event.dart @@ -17,3 +17,7 @@ class CreateNewNode extends HomeEvent { @override List get props => [sourceNode, destinationNode]; } + +class FetchUserInfo extends HomeEvent { + const FetchUserInfo(); +} \ No newline at end of file diff --git a/lib/pages/home/bloc/home_state.dart b/lib/pages/home/bloc/home_state.dart index dda3fa50..10c50486 100644 --- a/lib/pages/home/bloc/home_state.dart +++ b/lib/pages/home/bloc/home_state.dart @@ -1,6 +1,5 @@ import 'package:equatable/equatable.dart'; import 'package:graphview/GraphView.dart'; -import 'package:syncrow_web/pages/auth/model/user_model.dart'; abstract class HomeState extends Equatable { const HomeState(); @@ -25,9 +24,3 @@ class HomeUpdateTree extends HomeState { @override List get props => [graph, builder]; } - -class HomeUserInfoLoaded extends HomeState { - final UserModel user; - - HomeUserInfoLoaded(this.user); -} diff --git a/lib/pages/home/view/home_page.dart b/lib/pages/home/view/home_page.dart index c1e36729..75107e84 100644 --- a/lib/pages/home/view/home_page.dart +++ b/lib/pages/home/view/home_page.dart @@ -1,13 +1,31 @@ import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; +import 'package:syncrow_web/pages/home/bloc/home_event.dart'; import 'package:syncrow_web/pages/home/view/home_page_mobile.dart'; import 'package:syncrow_web/pages/home/view/home_page_web.dart'; -import 'package:syncrow_web/utils/responsive_layout.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -class HomePage extends StatelessWidget { +class HomePage extends StatefulWidget { const HomePage({super.key}); + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State with HelperResponsiveLayout { + @override + void initState() { + super.initState(); + context.read().add(const FetchUserInfo()); + } + @override Widget build(BuildContext context) { - return ResponsiveLayout(desktopBody: HomeWebPage(), mobileBody: HomeMobilePage()); + final isSmallScreen = isSmallScreenSize(context); + final isMediumScreen = isMediumScreenSize(context); + return isSmallScreen || isMediumScreen + ? HomeMobilePage() + : const HomeWebPage(); } } diff --git a/lib/pages/home/view/home_page_mobile.dart b/lib/pages/home/view/home_page_mobile.dart index dcf35a41..e8371cf2 100644 --- a/lib/pages/home/view/home_page_mobile.dart +++ b/lib/pages/home/view/home_page_mobile.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; +import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/pages/home/view/home_card.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -9,68 +10,77 @@ import 'package:syncrow_web/web_layout/web_scaffold.dart'; class HomeMobilePage extends StatelessWidget { HomeMobilePage({super.key}); + @override Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; - return WebScaffold( - enableMenuSideba: false, - appBarTitle: Row( - children: [ - SvgPicture.asset( - Assets.loginLogo, - width: 150, - ), - ], - ), - scaffoldBody: BlocProvider( - create: (context) => HomeBloc(), - child: SizedBox( - height: size.height, - width: size.width, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(height: size.height * 0.05), - const Text( - 'ACCESS YOUR APPS', - style: TextStyle(fontSize: 20, fontWeight: FontWeight.w700), - ), - const SizedBox(height: 30), - Expanded( - flex: 4, - child: SizedBox( - height: size.height * 0.6, - width: size.width * 0.68, - child: GridView.builder( - itemCount: 8, - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - crossAxisSpacing: 20.0, - mainAxisSpacing: 20.0, - childAspectRatio: 1.5, - ), - itemBuilder: (context, index) { - return HomeCard( - index: index, - active: homeItems[index]['active'], - name: homeItems[index]['title'], - img: homeItems[index]['icon'], - onTap: () {}, - ); - }, - ), - ), - ), - ], - ), + return PopScope( + canPop: false, + onPopInvoked: (didPop) => false, + child: WebScaffold( + enableMenuSideba: false, + appBarTitle: Row( + children: [ + SvgPicture.asset( + Assets.loginLogo, + width: 150, + ), + ], ), + scaffoldBody: BlocConsumer( + listener: (context, state) {}, + builder: (context, state) { + final homeBloc = BlocProvider.of(context); + return SizedBox( + height: size.height, + width: size.width, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: size.height * 0.05), + const Text( + 'ACCESS YOUR APPS', + style: + TextStyle(fontSize: 20, fontWeight: FontWeight.w700), + ), + const SizedBox(height: 30), + Expanded( + flex: 4, + child: SizedBox( + height: size.height * 0.6, + width: size.width * 0.68, + child: GridView.builder( + itemCount: 8, + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 20.0, + mainAxisSpacing: 20.0, + childAspectRatio: 1.5, + ), + itemBuilder: (context, index) { + return HomeCard( + index: index, + active: homeItems[index]['active'], + name: homeItems[index]['title'], + img: homeItems[index]['icon'], + onTap: () => + homeBloc.homeItems[index].onPress(context), + ); + }, + ), + ), + ), + ], + ), + ); + }), ), ); } - dynamic homeItems = [ + final dynamic homeItems = [ { 'title': 'Access', 'icon': Assets.accessIcon, diff --git a/lib/pages/home/view/home_page_web.dart b/lib/pages/home/view/home_page_web.dart index 2a578fc9..c1fc54f9 100644 --- a/lib/pages/home/view/home_page_web.dart +++ b/lib/pages/home/view/home_page_web.dart @@ -8,31 +8,28 @@ import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; class HomeWebPage extends StatelessWidget { - HomeWebPage({super.key}); + const HomeWebPage({super.key}); @override Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; return PopScope( canPop: false, onPopInvoked: (didPop) => false, - child: - WebScaffold( - enableMenuSideba: false, - appBarTitle: Row( - children: [ - SvgPicture.asset( - Assets.loginLogo, - width: 150, - ), - ], - ), - scaffoldBody: BlocProvider( - create: (context) => HomeBloc(), - child: BlocConsumer( - listener: (BuildContext context, state) {}, - builder: (context, state) { - final homeBloc = BlocProvider.of(context); - return SizedBox( + child: BlocConsumer( + listener: (BuildContext context, state) {}, + builder: (context, state) { + final homeBloc = BlocProvider.of(context); + return WebScaffold( + enableMenuSideba: false, + appBarTitle: Row( + children: [ + SvgPicture.asset( + Assets.loginLogo, + width: 150, + ), + ], + ), + scaffoldBody: SizedBox( height: size.height, width: size.width, child: Column( @@ -55,8 +52,7 @@ class HomeWebPage extends StatelessWidget { width: size.width * 0.68, child: GridView.builder( itemCount: 8, - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, @@ -68,8 +64,7 @@ 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), ); }, ), @@ -77,9 +72,9 @@ class HomeWebPage extends StatelessWidget { ), ], ), - ); - }, - ), - ))); + ), + ); + }, + )); } } diff --git a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart index 2b64d606..f1dbe47c 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart @@ -205,7 +205,6 @@ class VisitorPasswordBloc extends Bloc mapEventToState(VisitorPasswordEvent event) async* { if (event is FetchDevice) { } else if (event is UpdateFilteredDevicesEvent) { @@ -417,7 +412,6 @@ class VisitorPasswordBloc extends Bloc? devicesUuid}) async { - print(jsonEncode({ - "email": email, - "devicesUuid": devicesUuid, - "passwordName": passwordName, - "effectiveTime": effectiveTime, - "invalidTime": invalidTime, - })); final response = await HTTPService().post( path: ApiEndpoints.sendOffLineMultipleTime, body: jsonEncode({ diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 49cefd2d..85a06ea3 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -14,7 +14,7 @@ class Assets { 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 visiblePassword = "assets/images/Password_visible.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 devicesIcon = "assets/images/devices_icon.svg"; diff --git a/lib/utils/constants/routes_const.dart b/lib/utils/constants/routes_const.dart index 093a61dc..9f3b2d2b 100644 --- a/lib/utils/constants/routes_const.dart +++ b/lib/utils/constants/routes_const.dart @@ -1,5 +1,5 @@ class RoutesConst { - static const String auth = '/'; + static const String auth = '/auth'; static const String home = '/home'; static const String visitorPassword = '/visitor-password'; static const String accessManagementPage = '/access-management-page'; diff --git a/lib/core/extension/build_context_x.dart b/lib/utils/extension/build_context_x.dart similarity index 100% rename from lib/core/extension/build_context_x.dart rename to lib/utils/extension/build_context_x.dart diff --git a/lib/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart b/lib/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart index d64a5144..0d793aa6 100644 --- a/lib/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart +++ b/lib/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart @@ -2,15 +2,25 @@ import 'package:flutter/material.dart'; mixin HelperResponsiveLayout { bool isSmallScreenSize(BuildContext context) { - return MediaQuery.of(context).size.width < 700; + return MediaQuery.of(context).size.width < 600; } bool isMediumScreenSize(BuildContext context) { - return MediaQuery.of(context).size.width >= 700 && - MediaQuery.of(context).size.width < 1400; + return MediaQuery.of(context).size.width >= 600 && + MediaQuery.of(context).size.width < 1024; + } + + bool isHafMediumScreenSize(BuildContext context) { + return MediaQuery.of(context).size.width >= 600 / 1.3 && + MediaQuery.of(context).size.width < 1024 / 1.3; } bool isLargeScreenSize(BuildContext context) { - return MediaQuery.of(context).size.width >= 1400; + return MediaQuery.of(context).size.width >= 1024 && + MediaQuery.of(context).size.width < 1440; + } + + bool isExtraLargeScreenSize(BuildContext context) { + return MediaQuery.of(context).size.width >= 1440; } } diff --git a/lib/utils/theme/theme.dart b/lib/utils/theme/theme.dart new file mode 100644 index 00000000..ee868c8d --- /dev/null +++ b/lib/utils/theme/theme.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +final myTheme = ThemeData( + fontFamily: 'Aftika', + textTheme: const TextTheme( + bodySmall: TextStyle( + fontSize: 13, + color: ColorsManager.whiteColors, + fontWeight: FontWeight.bold), + bodyMedium: TextStyle(color: Colors.black87, fontSize: 14), + bodyLarge: TextStyle(fontSize: 16, color: Colors.white), + headlineSmall: TextStyle(color: Colors.black87, fontSize: 18), + headlineMedium: TextStyle(color: Colors.black87, fontSize: 20), + headlineLarge: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + colorScheme: ColorScheme.fromSeed( + seedColor: ColorsManager.blueColor, + primary: ColorsManager.blueColor, + onSurface: Colors.grey.shade400, + ), + switchTheme: SwitchThemeData( + thumbColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return ColorsManager.blueColor; + } + return ColorsManager.whiteColors; + }), + trackColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return ColorsManager.blueColor.withOpacity(0.5); + } + return ColorsManager.whiteColors; + }), + ), + checkboxTheme: CheckboxThemeData( + fillColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return ColorsManager.blueColor; + } + return Colors.grey.shade200; + }), + checkColor: WidgetStateProperty.all(Colors.white), + side: const BorderSide(color: ColorsManager.whiteColors), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + ), +); diff --git a/lib/web_layout/web_app_bar.dart b/lib/web_layout/web_app_bar.dart index 6ad54d0b..69f88f09 100644 --- a/lib/web_layout/web_app_bar.dart +++ b/lib/web_layout/web_app_bar.dart @@ -3,63 +3,125 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -class WebAppBar extends StatelessWidget { +class WebAppBar extends StatelessWidget with HelperResponsiveLayout { final Widget? title; - final List? body; - const WebAppBar({super.key, this.title, this.body}); + final Widget? centerBody; + final Widget? rightBody; + + const WebAppBar({super.key, this.title, this.centerBody, this.rightBody}); @override Widget build(BuildContext context) { + bool isSmallScreen = isSmallScreenSize(context); + bool isHalfMediumScreen = isHafMediumScreenSize(context); return BlocBuilder(builder: (context, state) { + final user = context.read().user; return Container( - height: 100, + height: (isSmallScreen || isHalfMediumScreen) ? 130 : 100, decoration: const BoxDecoration(color: ColorsManager.secondaryColor), padding: const EdgeInsets.all(10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: title!, - ), - if (body != null) - Expanded( - flex: 2, - child: Wrap( - spacing: 15, // Adjust the spacing as needed - children: body!, - ), - ), - Row( - children: [ - const SizedBox( - width: 10, - ), - const SizedBox.square( - dimension: 40, - child: CircleAvatar( - backgroundColor: Colors.white, - child: SizedBox.square( - dimension: 35, - child: CircleAvatar( - backgroundColor: Colors.grey, - child: FlutterLogo(), - ), + child: isSmallScreen || isHalfMediumScreen + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (title != null) + Align( + alignment: Alignment.centerLeft, + child: title!, + ), + if (centerBody != null) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: centerBody, + ), + if (rightBody != null || user != null) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (rightBody != null) rightBody!, + Row( + children: [ + const SizedBox.square( + dimension: 40, + child: CircleAvatar( + backgroundColor: Colors.white, + child: SizedBox.square( + dimension: 35, + child: CircleAvatar( + backgroundColor: Colors.grey, + child: FlutterLogo(), + ), + ), + ), + ), + const SizedBox( + width: 10, + ), + if (user != null) + Text( + '${user.firstName} ${user.lastName}', + style: Theme.of(context).textTheme.bodyLarge, + ), + ], + ), + ], + ), + ], + ) + : Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Row( + children: [ + title!, + if (centerBody != null) + Padding( + padding: const EdgeInsets.only(left: 80), + child: centerBody!, + ), + ], ), ), - ), - const SizedBox( - width: 10, - ), - if (HomeBloc.user != null) - Text( - '${HomeBloc.user!.firstName.toString()} ${HomeBloc.user!.lastName.toString()} ', - style: Theme.of(context).textTheme.bodyLarge, + Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (rightBody != null) + Align( + alignment: Alignment.centerRight, + child: rightBody, + ), + const SizedBox( + width: 10, + ), + const SizedBox.square( + dimension: 40, + child: CircleAvatar( + backgroundColor: Colors.white, + child: SizedBox.square( + dimension: 35, + child: CircleAvatar( + backgroundColor: Colors.grey, + child: FlutterLogo(), + ), + ), + ), + ), + const SizedBox( + width: 10, + ), + if (user != null) + Text( + '${user.firstName} ${user.lastName}', + style: Theme.of(context).textTheme.bodyLarge, + ), + ], ), - ], - ) - ], - ), + ], + ), ); }); } diff --git a/lib/web_layout/web_scaffold.dart b/lib/web_layout/web_scaffold.dart index 72bcb777..b4edd764 100644 --- a/lib/web_layout/web_scaffold.dart +++ b/lib/web_layout/web_scaffold.dart @@ -8,14 +8,17 @@ import 'menu_sidebar.dart'; class WebScaffold extends StatelessWidget with HelperResponsiveLayout { final bool enableMenuSideba; final Widget? appBarTitle; - final List? appBarBody; + final Widget? centerBody; + final Widget? rightBody; final Widget? scaffoldBody; - const WebScaffold( - {super.key, - this.appBarTitle, - this.appBarBody, - this.scaffoldBody, - this.enableMenuSideba = true}); + const WebScaffold({ + super.key, + this.appBarTitle, + this.centerBody, + this.rightBody, + this.scaffoldBody, + this.enableMenuSideba = true, + }); @override Widget build(BuildContext context) { final isSmall = isSmallScreenSize(context); @@ -40,7 +43,8 @@ class WebScaffold extends StatelessWidget with HelperResponsiveLayout { opacity: 0.7, child: WebAppBar( title: appBarTitle, - body: appBarBody, + centerBody: centerBody, + rightBody: rightBody, )), Expanded( child: Row(