diff --git a/lib/pages/auth/view/login_web_page.dart b/lib/pages/auth/view/login_web_page.dart index 72cad7cc..a6de87cf 100644 --- a/lib/pages/auth/view/login_web_page.dart +++ b/lib/pages/auth/view/login_web_page.dart @@ -55,12 +55,12 @@ class _LoginWebPageState extends State with HelperResponsiveLayout final isSmallScreen = isSmallScreenSize(context); final isMediumScreen = isMediumScreenSize(context); Size size = MediaQuery.of(context).size; - late ScrollController _scrollController; - _scrollController = ScrollController(); + late ScrollController scrollController; + scrollController = ScrollController(); - void _scrollToCenter() { - final double middlePosition = _scrollController.position.maxScrollExtent / 2; - _scrollController.animateTo( + void scrollToCenter() { + final double middlePosition = scrollController.position.maxScrollExtent / 2; + scrollController.animateTo( middlePosition, duration: const Duration(seconds: 1), curve: Curves.easeInOut, @@ -68,7 +68,7 @@ class _LoginWebPageState extends State with HelperResponsiveLayout } WidgetsBinding.instance.addPostFrameCallback((_) { - _scrollToCenter(); + scrollToCenter(); }); return Stack( @@ -76,7 +76,7 @@ class _LoginWebPageState extends State with HelperResponsiveLayout FirstLayer( second: Center( child: ListView( - controller: _scrollController, + controller: scrollController, shrinkWrap: true, children: [ Container( @@ -199,7 +199,7 @@ class _LoginWebPageState extends State with HelperResponsiveLayout width: size.width * 0.9, child: DropdownButtonHideUnderline( child: DropdownButton2( - style: TextStyle(color: Colors.black), + style: const TextStyle(color: Colors.black), isExpanded: true, hint: Text( 'Select your region/country', @@ -336,6 +336,16 @@ class _LoginWebPageState extends State with HelperResponsiveLayout obscureText: loginBloc.obscureText, keyboardType: TextInputType.visiblePassword, controller: loginBloc.loginPasswordController, + onFieldSubmitted: (value) { + if (loginBloc.loginFormKey.currentState!.validate()) { + loginBloc.add(LoginButtonPressed( + username: loginBloc.loginEmailController.text, + password: value, + )); + } else { + loginBloc.add(ChangeValidateEvent()); + } + }, decoration: textBoxDecoration()!.copyWith( hintText: 'At least 8 characters', hintStyle: Theme.of(context) @@ -393,7 +403,7 @@ class _LoginWebPageState extends State with HelperResponsiveLayout Transform.scale( scale: 1.2, child: Checkbox( - fillColor: MaterialStateProperty.all(Colors.white), + fillColor: WidgetStateProperty.all(Colors.white), activeColor: Colors.white, value: loginBloc.isChecked, checkColor: Colors.black, diff --git a/lib/pages/routines/helper/save_routine_helper.dart b/lib/pages/routines/helper/save_routine_helper.dart index 457f74dc..23920ba6 100644 --- a/lib/pages/routines/helper/save_routine_helper.dart +++ b/lib/pages/routines/helper/save_routine_helper.dart @@ -5,7 +5,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart'; -import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -15,13 +14,18 @@ class SaveRoutineHelper { static Future showSaveRoutineDialog(BuildContext context) async { return showDialog( context: context, - builder: (BuildContext context) { + builder: (context) { return BlocBuilder( builder: (context, state) { + final selectedConditionLabel = state.selectedAutomationOperator == 'and' + ? 'All Conditions are met' + : 'Any Condition is met'; + return AlertDialog( contentPadding: EdgeInsets.zero, content: Container( - width: MediaQuery.sizeOf(context).width * 0.5, + width: context.screenWidth * 0.5, + height: 500, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), @@ -29,99 +33,42 @@ class SaveRoutineHelper { child: Column( mainAxisSize: MainAxisSize.min, children: [ - DialogHeader('Create a scene: ${state.routineName ?? ""}'), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Left side - IF - Expanded( - child: ListView( - // crossAxisAlignment: CrossAxisAlignment.start, - shrinkWrap: true, - children: [ - const Text( - 'IF:', - style: TextStyle( - fontSize: 16, - ), - ), - const SizedBox(height: 8), - if (state.isTabToRun) - ListTile( - leading: SvgPicture.asset( - Assets.tabToRun, - width: 24, - height: 24, - ), - title: const Text('Tab to run'), - ), - if (state.isAutomation) - ...state.ifItems.map((item) { - final functions = - state.selectedFunctions[item['uniqueCustomId']] ?? []; - return functionRow(item, context, functions); - }), - ], - ), + const SizedBox(height: 18), + Text( + 'Create a scene: ${state.routineName ?? ""}', + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.headlineMedium!.copyWith( + color: ColorsManager.primaryColorWithOpacity, + fontWeight: FontWeight.bold, ), - const SizedBox(width: 16), - // Right side - THEN items - - Expanded( - child: ListView( - // crossAxisAlignment: CrossAxisAlignment.start, - shrinkWrap: true, - children: [ - const Text( - 'THEN:', - style: TextStyle( - fontSize: 16, - ), - ), - const SizedBox(height: 8), - ...state.thenItems.map((item) { - final functions = - state.selectedFunctions[item['uniqueCustomId']] ?? []; - return functionRow(item, context, functions); - }), - ], + ), + const SizedBox(height: 18), + _buildDivider(), + _buildListsLabelRow(selectedConditionLabel), + Expanded( + child: Padding( + padding: const EdgeInsetsDirectional.symmetric( + horizontal: 16, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + spacing: 24, + children: [ + _buildIfConditions(state, context), + Container( + width: 1, + color: ColorsManager.greyColor.withValues(alpha: 0.8), ), - ), - ], + _buildThenActions(state, context), + ], + ), ), ), - // if (state.errorMessage != null || state.errorMessage!.isNotEmpty) - // Padding( - // padding: const EdgeInsets.all(8.0), - // child: Text( - // state.errorMessage!, - // style: const TextStyle(color: Colors.red), - // ), - // ), - DialogFooter( - onCancel: () => Navigator.pop(context), - onConfirm: () async { - if (state.isAutomation) { - if (state.isUpdate ?? false) { - context.read().add(const UpdateAutomation()); - } else { - context.read().add(const CreateAutomationEvent()); - } - } else { - if (state.isUpdate ?? false) { - context.read().add(const UpdateScene()); - } else { - context.read().add(const CreateSceneEvent()); - } - } - // if (state.errorMessage == null || state.errorMessage!.isEmpty) { - Navigator.pop(context); - // } - }, - isConfirmEnabled: true, - ), + _buildDivider(), + const SizedBox(height: 8), + _buildDialogFooter(context, state), + const SizedBox(height: 8), ], ), ), @@ -132,8 +79,107 @@ class SaveRoutineHelper { ); } + static Container _buildDivider() { + return Container( + height: 1, + width: double.infinity, + color: ColorsManager.greyColor, + ); + } + + static Widget _buildListsLabelRow(String selectedConditionLabel) { + const textStyle = TextStyle( + fontSize: 16, + ); + return Container( + color: ColorsManager.backgroundColor.withValues(alpha: 0.5), + padding: const EdgeInsetsDirectional.all(20), + child: Row( + spacing: 16, + children: [ + Expanded(child: Text('IF: $selectedConditionLabel', style: textStyle)), + const Expanded(child: Text('THEN:', style: textStyle)), + ], + ), + ); + } + + static Widget _buildDialogFooter(BuildContext context, RoutineState state) { + return Row( + spacing: 16, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + DialogFooterButton( + text: 'Cancel', + onTap: () => Navigator.pop(context), + ), + DialogFooterButton( + text: 'Confirm', + onTap: () { + if (state.isAutomation) { + if (state.isUpdate ?? false) { + context.read().add(const UpdateAutomation()); + } else { + context.read().add(const CreateAutomationEvent()); + } + } else { + if (state.isUpdate ?? false) { + context.read().add(const UpdateScene()); + } else { + context.read().add(const CreateSceneEvent()); + } + } + + Navigator.pop(context); + }, + textColor: ColorsManager.primaryColorWithOpacity, + ), + ], + ); + } + + static Widget _buildThenActions(RoutineState state, BuildContext context) { + return Expanded( + child: ListView( + // shrinkWrap: true, + children: state.thenItems.map((item) { + final functions = state.selectedFunctions[item['uniqueCustomId']] ?? []; + return functionRow(item, context, functions); + }).toList(), + ), + ); + } + + static Widget _buildIfConditions(RoutineState state, BuildContext context) { + return Expanded( + child: ListView( + // shrinkWrap: true, + children: [ + if (state.isTabToRun) + ListTile( + leading: SvgPicture.asset( + Assets.tabToRun, + width: 24, + height: 24, + ), + title: const Text('Tab to run'), + ), + if (state.isAutomation) + ...state.ifItems.map((item) { + final functions = + state.selectedFunctions[item['uniqueCustomId']] ?? []; + return functionRow(item, context, functions); + }), + ], + ), + ); + } + static Widget functionRow( - dynamic item, BuildContext context, List functions) { + dynamic item, + BuildContext context, + List functions, + ) { return Padding( padding: const EdgeInsets.only(top: 6), child: Row( @@ -142,19 +188,36 @@ class SaveRoutineHelper { Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.start, - spacing: 8, + spacing: 17, children: [ - item['type'] == 'tap_to_run' || item['type'] == 'scene' - ? Image.memory( - base64Decode(item['icon']), - width: 22, - height: 22, - ) - : SvgPicture.asset( - item['imagePath'], - width: 22, - height: 22, - ), + Container( + width: 22, + height: 22, + padding: const EdgeInsetsDirectional.all(4), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.textFieldGreyColor, + border: Border.all( + color: ColorsManager.neutralGray, + width: 1.5, + ), + ), + child: Center( + child: item['type'] == 'tap_to_run' || item['type'] == 'scene' + ? Image.memory( + base64Decode(item['icon']), + width: 12, + height: 22, + fit: BoxFit.scaleDown, + ) + : SvgPicture.asset( + item['imagePath'], + width: 12, + height: 12, + fit: BoxFit.scaleDown, + ), + ), + ), Flexible( child: Column( mainAxisAlignment: MainAxisAlignment.start, @@ -166,19 +229,25 @@ class SaveRoutineHelper { maxLines: 1, overflow: TextOverflow.ellipsis, style: context.textTheme.bodySmall?.copyWith( - fontSize: 14, + fontSize: 15, color: ColorsManager.textPrimaryColor, ), ), Wrap( + runSpacing: 16, + spacing: 4, children: functions - .map((f) => Text( - '${f.operationName}: ${f.value}', - style: context.textTheme.bodySmall - ?.copyWith(color: ColorsManager.grayColor, fontSize: 8), - overflow: TextOverflow.ellipsis, - maxLines: 3, - )) + .map( + (function) => Text( + '${function.operationName}: ${function.value}', + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.grayColor, + fontSize: 8, + ), + overflow: TextOverflow.ellipsis, + maxLines: 3, + ), + ) .toList(), ), ], @@ -197,7 +266,13 @@ class SaveRoutineHelper { child: Row( spacing: 2, children: [ - SizedBox(width: 8, height: 8, child: SvgPicture.asset(Assets.deviceTagIcon)), + SizedBox( + width: 8, + height: 8, + child: SvgPicture.asset( + Assets.deviceTagIcon, + ), + ), Text( item['tag'] ?? '', textAlign: TextAlign.center, @@ -218,7 +293,12 @@ class SaveRoutineHelper { spacing: 2, children: [ SizedBox( - width: 8, height: 8, child: SvgPicture.asset(Assets.spaceLocationIcon)), + width: 8, + height: 8, + child: SvgPicture.asset( + Assets.spaceLocationIcon, + ), + ), Text( item['subSpace'] ?? '', textAlign: TextAlign.center, diff --git a/lib/pages/routines/widgets/dialog_footer.dart b/lib/pages/routines/widgets/dialog_footer.dart index e5a548f7..38178ee6 100644 --- a/lib/pages/routines/widgets/dialog_footer.dart +++ b/lib/pages/routines/widgets/dialog_footer.dart @@ -28,32 +28,40 @@ class DialogFooter extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _buildFooterButton( - context: context, + DialogFooterButton( text: 'Cancel', onTap: onCancel, ), if (isConfirmEnabled) ...[ Container(width: 1, height: 50, color: ColorsManager.greyColor), - _buildFooterButton( - context: context, + DialogFooterButton( text: 'Confirm', onTap: onConfirm, - textColor: - isConfirmEnabled ? ColorsManager.primaryColorWithOpacity : Colors.red, + textColor: isConfirmEnabled + ? ColorsManager.primaryColorWithOpacity + : Colors.red, ), ], ], ), ); } +} - Widget _buildFooterButton({ - required BuildContext context, - required String text, - required VoidCallback? onTap, - Color? textColor, - }) { +class DialogFooterButton extends StatelessWidget { + const DialogFooterButton({ + required this.text, + required this.onTap, + this.textColor, + super.key, + }); + + final String text; + final VoidCallback? onTap; + final Color? textColor; + + @override + Widget build(BuildContext context) { return Expanded( child: TextButton( style: TextButton.styleFrom( diff --git a/lib/pages/routines/widgets/dialog_header.dart b/lib/pages/routines/widgets/dialog_header.dart index 4fe1f0b1..f1f5686a 100644 --- a/lib/pages/routines/widgets/dialog_header.dart +++ b/lib/pages/routines/widgets/dialog_header.dart @@ -16,6 +16,7 @@ class DialogHeader extends StatelessWidget { ), Text( title, + textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: ColorsManager.primaryColorWithOpacity, fontWeight: FontWeight.bold,