From e2d4e48875c5f2e2eb85a80b60cd8137eb78e98a Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 7 Jul 2025 15:36:35 +0300 Subject: [PATCH 01/71] build main screens with its widgets for bookableScreen --- assets/icons/add_button_Icon.svg | 10 ++ assets/icons/back_button_icon.svg | 4 + assets/icons/clock_icon.svg | 4 + assets/icons/no_data_table.svg | 24 +++ .../booking_system/view/booking_page.dart | 9 +- .../screens/setup_bookable_spaces_dialog.dart | 140 ++++++++++++++++++ .../buttons_divider_bottom_dialog_widget.dart | 88 +++++++++++ .../widgets/column_title_widget.dart | 37 +++++ .../widgets/custom_data_table.dart | 55 +++++++ 9 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 assets/icons/add_button_Icon.svg create mode 100644 assets/icons/back_button_icon.svg create mode 100644 assets/icons/clock_icon.svg create mode 100644 assets/icons/no_data_table.svg create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/column_title_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_data_table.dart diff --git a/assets/icons/add_button_Icon.svg b/assets/icons/add_button_Icon.svg new file mode 100644 index 00000000..f9b8eae7 --- /dev/null +++ b/assets/icons/add_button_Icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/back_button_icon.svg b/assets/icons/back_button_icon.svg new file mode 100644 index 00000000..5cc7b637 --- /dev/null +++ b/assets/icons/back_button_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/clock_icon.svg b/assets/icons/clock_icon.svg new file mode 100644 index 00000000..296aa862 --- /dev/null +++ b/assets/icons/clock_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/no_data_table.svg b/assets/icons/no_data_table.svg new file mode 100644 index 00000000..c97946a2 --- /dev/null +++ b/assets/icons/no_data_table.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/pages/access_management/booking_system/view/booking_page.dart b/lib/pages/access_management/booking_system/view/booking_page.dart index 6fdb53bd..d35f258d 100644 --- a/lib/pages/access_management/booking_system/view/booking_page.dart +++ b/lib/pages/access_management/booking_system/view/booking_page.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:syncrow_web/pages/access_management/booking_system/view/widgets/icon_text_button.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/constants/routes_const.dart'; class BookingPage extends StatelessWidget { const BookingPage({super.key}); @@ -33,8 +35,11 @@ class BookingPage extends StatelessWidget { SvgTextButton( svgAsset: Assets.homeIcon, label: 'Manage Bookable Spaces', - onPressed: () {}), - SizedBox(width: 20), + onPressed: () { + context + .go(RoutesConst.manageBookableSapcesPage); + }), + const SizedBox(width: 20), SvgTextButton( svgAsset: Assets.groupIcon, label: 'Manage Users', diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart new file mode 100644 index 00000000..f073b6ba --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class SetupBookableSpacesDialog extends StatelessWidget { + final TextEditingController pointsController = TextEditingController(); + SetupBookableSpacesDialog({super.key}); + @override + Widget build(BuildContext context) { + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => StepsCubit()..initDialogValue(), + ), + BlocProvider( + create: (context) => NonBookableSpacesBloc( + DummyNonNookableSpaces(), + )..add( + LoadUnBookableSpacesEvent( + nonBookableSpacesParams: + NonBookableSpacesParams(currentPage: 1), + ), + ), + ), + ], + child: AlertDialog( + contentPadding: EdgeInsets.zero, + title: Text( + 'Set Up a Bookable Spaces', + style: TextStyle( + fontWeight: FontWeight.w700, + color: ColorsManager.dialogBlueTitle, + fontSize: 15, + ), + ), + content: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Divider(), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Expanded( + flex: 3, + child: StepperPartWidget(), + ), + const SizedBox( + height: 588, + child: VerticalDivider( + thickness: 0.5, + width: 1, + ), + ), + Expanded( + flex: 7, + child: DetailsStepsWidget( + pointsController: pointsController, + ), + ) + ], + ), + Builder(builder: (context) { + return ButtonsDividerBottomDialogWidget( + onNextPressed: () { + final stepsState = context.read().state; + final selectedSpaces = context + .read() + .selectedBookableSpaces; + if (stepsState is StepOneState) { + if (selectedSpaces.isNotEmpty) { + context.read().goToNextStep(); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Please select at least one space.'), + ), + ); + } + } else if (stepsState is StepTwoState) { + selectedSpaces.forEach( + (e) => + e.spaceConfig.cost = int.parse(pointsController.text), + ); + if (selectedSpaces.any( + (element) => !element.isValid, + )) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Please fill the required fields.'), + ), + ); + } else {} + } + }, + onCancelPressed: () => context.pop(), + ); + }), + ], + ), + ), + ); + } +} + +class DetailsStepsWidget extends StatelessWidget { + final TextEditingController pointsController; + const DetailsStepsWidget({ + super.key, + required this.pointsController, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20), + child: BlocBuilder( + builder: (context, state) { + if (state is StepOneState) { + return SpacesStepDetailsWidget(); + } else if (state is StepTwoState) { + return StepTwoDetailsWidget( + pointsController: pointsController, + ); + } else { + return const SizedBox(); + } + }, + ), + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart new file mode 100644 index 00000000..e177b9b1 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class ButtonsDividerBottomDialogWidget extends StatelessWidget { + final void Function() onNextPressed; + final void Function() onCancelPressed; + const ButtonsDividerBottomDialogWidget({ + super.key, + required this.onNextPressed, + required this.onCancelPressed, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + const Divider( + thickness: 0.5, + height: 1, + ), + Row( + children: [ + Expanded( + child: InkWell( + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(26), + ), + onTap: onCancelPressed, + child: Container( + height: 40, + alignment: Alignment.center, + decoration: const BoxDecoration( + border: Border( + right: BorderSide( + color: ColorsManager.grayBorder, + ), + ), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(26), + ), + ), + child: const Text( + 'Cancel', + style: TextStyle(color: ColorsManager.grayBorder), + ), + ), + ), + ), + Expanded( + child: BlocBuilder( + builder: (context, state) { + return InkWell( + borderRadius: const BorderRadius.only( + bottomRight: Radius.circular(26), + ), + onTap: onNextPressed, + child: Container( + height: 40, + alignment: Alignment.center, + decoration: const BoxDecoration( + border: Border( + right: BorderSide( + color: ColorsManager.grayBorder, + ), + ), + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(26), + ), + ), + child: Text( + state is StepOneState ? 'Next' : 'Save', + style: const TextStyle( + color: ColorsManager.blueColor, + ), + ), + ), + ); + }, + ), + ) + ], + ) + ], + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/column_title_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/column_title_widget.dart new file mode 100644 index 00000000..4ff11ec6 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/column_title_widget.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class ColumnTitleWidget extends StatelessWidget { + final bool isFirst; + final bool isLast; + final String title; + const ColumnTitleWidget({ + super.key, + required this.title, + required this.isFirst, + required this.isLast, + }); + + @override + Widget build(BuildContext context) { + return Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.only(left: 10), + decoration: BoxDecoration( + color: ColorsManager.graysColor, + borderRadius: isFirst + ? const BorderRadius.only( + topLeft: Radius.circular(12), + ) + : isLast + ? const BorderRadius.only( + topRight: Radius.circular(12), + ) + : null, + ), + child: Text( + title, + style: const TextStyle(color: ColorsManager.grayColor), + )); + } +} \ No newline at end of file diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_data_table.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_data_table.dart new file mode 100644 index 00000000..a72104fe --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_data_table.dart @@ -0,0 +1,55 @@ +import 'package:data_table_2/data_table_2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/column_title_widget.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class CustomDataTable extends StatelessWidget { + final List columnsTitles; + final List Function(T item) cellsWidgets; + final List items; + + const CustomDataTable({ + super.key, + required this.items, + required this.cellsWidgets, + required this.columnsTitles, + }); + + @override + Widget build(BuildContext context) { + return DataTable2( + dividerThickness: 0.5, + columnSpacing: 2, + horizontalMargin: 0, + empty: SvgPicture.asset(Assets.emptyDataTable), + decoration: BoxDecoration( + color: ColorsManager.circleRolesBackground, + borderRadius: BorderRadius.circular(12), + boxShadow: const [ + BoxShadow( + color: ColorsManager.textGray, + blurRadius: 12, + offset: Offset(0, 4), + ), + ], + ), + columns: columnsTitles.asMap().entries.map((entry) { + final index = entry.key; + final title = entry.value; + + return DataColumn( + label: ColumnTitleWidget( + title: title, + isFirst: index == 0, + isLast: index == columnsTitles.length - 1, + ), + ); + }).toList(), + rows: items.map((item) { + return DataRow(cells: cellsWidgets(item)); + }).toList(), + ); + } +} From 201348a9bf5877c1178fabbd8d573f85b77f812b Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 7 Jul 2025 15:37:38 +0300 Subject: [PATCH 02/71] build dialog with steps view --- .../manage_bookable_spaces_screen.dart | 191 ++++++++++++++++++ .../search_unbookable_spaces_widget.dart | 59 ++++++ .../widgets/space_step_part_widget.dart | 166 +++++++++++++++ .../widgets/step_two_details_widget.dart | 119 +++++++++++ .../widgets/stepper_part_widget.dart | 117 +++++++++++ .../widgets/time_picker_widget.dart | 82 ++++++++ .../widgets/week_checkbox_title_widget.dart | 62 ++++++ 7 files changed, 796 insertions(+) create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart new file mode 100644 index 00000000..f3a21be1 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart @@ -0,0 +1,191 @@ +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/access_management/booking_system/view/widgets/icon_text_button.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_data_table.dart'; +import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; +import 'package:syncrow_web/utils/theme/responsive_text_theme.dart'; +import 'package:syncrow_web/web_layout/web_scaffold.dart'; + +class ManageBookableSpacesPage extends StatefulWidget { + const ManageBookableSpacesPage({super.key}); + + @override + State createState() => + _ManageBookableSpacesPageState(); +} + +class _ManageBookableSpacesPageState extends State { + final PageController _pageController = PageController(initialPage: 1); + int _currentPageIndex = 1; + @override + Widget build(BuildContext context) { + return WebScaffold( + enableMenuSidebar: false, + appBarTitle: Text( + 'Access Management', + style: ResponsiveTextTheme.of(context).deviceManagementTitle, + ), + centerBody: Row( + mainAxisSize: MainAxisSize.min, + children: [ + TextButton( + onPressed: () => _switchPage(0), + child: Text( + 'Access Overview', + style: context.textTheme.titleMedium?.copyWith( + color: _currentPageIndex == 0 ? Colors.white : Colors.grey, + fontWeight: + _currentPageIndex == 0 ? FontWeight.w700 : FontWeight.w400, + ), + ), + ), + TextButton( + onPressed: () => _switchPage(1), + child: Text( + 'Booking System', + style: context.textTheme.titleMedium?.copyWith( + color: _currentPageIndex == 1 ? Colors.white : Colors.grey, + fontWeight: + _currentPageIndex == 1 ? FontWeight.w700 : FontWeight.w400, + ), + ), + ), + ], + ), + rightBody: const NavigateHomeGridView(), + scaffoldBody: BlocProvider( + create: (context) => BookableSpacesBloc( + DummyBookableSpacesService(), + // RemoteBookableSpacesService(HTTPService()), + )..add(LoadBookableSpacesEvent( + BookableSpacesParams(currentPage: 1), + )), + child: const ManageBookableSpacesWidget(), + ), + ); + } + + void _switchPage(int index) { + setState(() => _currentPageIndex = index); + _pageController.jumpToPage(index); + } +} + +class ManageBookableSpacesWidget extends StatelessWidget { + const ManageBookableSpacesWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(20), + child: Column( + children: [ + Expanded( + flex: 1, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SvgTextButton( + svgAsset: Assets.backButtonIcon, + label: 'Booking Home', + onPressed: () { + context.pop(); + }), + SvgTextButton( + svgAsset: Assets.backButtonIcon, + label: 'Set Up a Bookable Spaces', + onPressed: () async => showDialog( + context: context, + builder: (context) => SetupBookableSpacesDialog(), + ), + ) + ], + )), + Expanded( + flex: 9, + child: BlocBuilder( + builder: (context, state) { + if (state is BookableSpacesLoading) { + return const CircularProgressIndicator(); + } else if (state is BookableSpacesError) { + return Text(state.error); + } else if (state is BookableSpacesLoaded) { + return CustomDataTable( + items: state.bookableSpacesList.data, + cellsWidgets: (space) => [ + DataCell( + Padding( + padding: const EdgeInsetsGeometry.only(left: 10), + child: Text(space.spaceName)), + ), + DataCell(Padding( + padding: const EdgeInsetsGeometry.only(left: 10), + child: Text(space.spaceVirtualAddress))), + DataCell(SizedBox( + width: 200, + child: Wrap( + spacing: 4, + children: space.spaceConfig.bookableDays + .map((day) => Text( + day, + style: const TextStyle(fontSize: 12), + )) + .toList(), + ), + )), + DataCell(Padding( + padding: const EdgeInsetsGeometry.only(left: 10), + child: Text(space.spaceConfig.bookingStartTime))), + DataCell(Padding( + padding: const EdgeInsetsGeometry.only(left: 10), + child: Text(space.spaceConfig.bookingEndTime))), + DataCell(Padding( + padding: const EdgeInsetsGeometry.only(left: 10), + child: Text('${space.spaceConfig.cost} Points'))), + DataCell(Center( + child: Transform.scale( + scale: 0.7, + child: Switch( + value: space.spaceConfig.availability, + onChanged: (value) {}, + ), + ), + )), + DataCell(Center( + child: ElevatedButton( + onPressed: () {}, + child: SvgPicture.asset(Assets.settings), + ), + )), + ], + columnsTitles: const [ + 'space', + 'space Virtual Address', + 'Bookable Days', + 'Booking Start Time', + 'Booking End Time', + 'Cost', + 'Availability', + 'Settings', + ], + ); + } else { + return const SizedBox(); + } + }, + ), + ) + ], + ), + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart new file mode 100644 index 00000000..be9931e5 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class SearchUnbookableSpacesWidget extends StatelessWidget { + final String title; + final Widget? suffix; + final double? height; + final double? width; + final TextEditingController? controller; + final List? inputFormatters; + final void Function(String)? onChanged; + const SearchUnbookableSpacesWidget({ + required this.title, + this.controller, + this.onChanged, + this.suffix, + this.height, + this.width, + this.inputFormatters, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + width: width ?? 480, + height: height ?? 30, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: const [ + BoxShadow( + color: Color(0x26000000), + offset: Offset(0, 4), + blurRadius: 5, + ), + ], + ), + child: TextField( + controller: controller, + inputFormatters: inputFormatters, + onChanged: onChanged, + decoration: InputDecoration( + contentPadding: + const EdgeInsets.symmetric(vertical: 5, horizontal: 15), + hintText: title, + hintStyle: const TextStyle(color: Colors.grey), + border: InputBorder.none, + suffixIcon: + suffix ?? const Icon(Icons.search, size: 20, color: Colors.grey), + ), + style: const TextStyle( + fontSize: 14, + color: Colors.grey, + ), + ), + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart new file mode 100644 index 00000000..d58d3689 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart @@ -0,0 +1,166 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class SpacesStepDetailsWidget extends StatelessWidget { + SpacesStepDetailsWidget({ + super.key, + }); + Timer? _debounce; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is NonBookableSpacesLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is NonBookableSpacesError) { + return Text(state.error); + } else if (state is NonBookableSpacesLoaded) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Select Space', + style: TextStyle( + fontWeight: FontWeight.w700, + color: ColorsManager.blackColor, + ), + ), + const SizedBox( + height: 20, + ), + Container( + width: 450, + height: 480, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: const [ + BoxShadow( + color: Color(0x40000000), + offset: Offset(0, 4), + blurRadius: 5, + ), + ], + ), + child: Column( + children: [ + Container( + width: 520, + height: 70, + padding: const EdgeInsets.symmetric( + vertical: 15, horizontal: 20), + decoration: const BoxDecoration( + color: Color(0xFFF8F8F8), + borderRadius: BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + child: SearchUnbookableSpacesWidget( + title: 'Search', + onChanged: (p0) { + if (_debounce?.isActive ?? false) _debounce!.cancel(); + _debounce = + Timer(const Duration(milliseconds: 500), () { + context.read().add( + LoadUnBookableSpacesEvent( + nonBookableSpacesParams: + NonBookableSpacesParams( + currentPage: 1, + searchedWords: p0, + ), + ), + ); + }); + }, + ), + ), + Expanded( + child: Container( + width: 490, + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(20), + ), + ), + padding: + const EdgeInsets.only(top: 10, left: 20, bottom: 5), + child: ListView.separated( + separatorBuilder: (context, index) => const SizedBox( + height: 5, + ), + itemCount: state.nonBookableSpaces.data.length, + itemBuilder: (context, index) => CheckBoxSpaceWidget( + nonBookableSpace: + state.nonBookableSpaces.data[index], + ), + ), + ), + ) + ], + ), + ) + ], + ); + } else { + return const SizedBox(); + } + }, + ); + } +} + +class CheckBoxSpaceWidget extends StatefulWidget { + final BookableSpacemodel nonBookableSpace; + + const CheckBoxSpaceWidget({ + super.key, + required this.nonBookableSpace, + }); + + @override + State createState() => _CheckBoxSpaceWidgetState(); +} + +class _CheckBoxSpaceWidgetState extends State { + bool isChecked = false; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Checkbox( + value: isChecked, + onChanged: (value) => setState(() { + isChecked = value ?? false; + if (isChecked) { + context.read().add( + AddToBookableSpaceEvent( + nonBookableSpace: widget.nonBookableSpace, + ), + ); + } else { + context.read().add( + RemoveFromBookableSpaceEvent( + bookableSpace: widget.nonBookableSpace, + ), + ); + } + }), + ), + const SizedBox( + width: 5, + ), + Text(widget.nonBookableSpace.spaceName), + ], + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart new file mode 100644 index 00000000..8825fb6a --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart @@ -0,0 +1,119 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart'; + +class StepTwoDetailsWidget extends StatelessWidget { + final TextEditingController pointsController; + const StepTwoDetailsWidget({ + super.key, + required this.pointsController, + }); + @override + Widget build(BuildContext context) { + return SizedBox( + width: 450, + height: 480, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const WeekDaysCheckboxRow(), + const SizedBox( + height: 20, + ), + Row( + children: [ + TitleAndTimePickerWidget( + title: 'Booking Start Time', + onTimePicked: (timePicked) { + final nonBookableBloc = context.read(); + nonBookableBloc.selectedBookableSpaces.forEach( + (e) => e.spaceConfig.bookingStartTime = + timePicked!.format(context), + ); + }, + ), + SizedBox(width: 20,), + TitleAndTimePickerWidget( + title: 'Booking End Time', + onTimePicked: (timePicked) { + final nonBookableBloc = context.read(); + nonBookableBloc.selectedBookableSpaces.forEach( + (e) => e.spaceConfig.bookingEndTime = + timePicked!.format(context), + ); + }, + ) + ], + ), + const SizedBox( + height: 20, + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + const Text('Points/hrs'), + ], + ), + const SizedBox( + height: 5, + ), + SearchUnbookableSpacesWidget( + title: 'Ex: 0', + controller: pointsController, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + suffix: const SizedBox(), + ), + ], + ), + ); + } +} + +class TitleAndTimePickerWidget extends StatelessWidget { + final void Function(TimeOfDay? timePicked) onTimePicked; + final String title; + const TitleAndTimePickerWidget({ + super.key, + required this.onTimePicked, + required this.title, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + Text(title), + ], + ), + const SizedBox( + height: 5, + ), + TimePickerWidget( + onTimePicked: onTimePicked, + ), + ], + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart new file mode 100644 index 00000000..e7e3d43d --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class StepperPartWidget extends StatelessWidget { + const StepperPartWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.all(20), + padding: const EdgeInsetsGeometry.only(left: 20), + child: BlocBuilder( + builder: (context, state) { + if (state is StepOneState) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const SizedBox( + height: 10, + ), + const CircleTitleStepperWidget( + title: 'Space', + ), + Container( + alignment: Alignment.centerLeft, + height: 50, + child: const VerticalDivider( + width: 8, + )), + const CircleTitleStepperWidget( + title: 'Settings', + titleColor: ColorsManager.softGray, + circleColor: ColorsManager.softGray, + ) + ], + ); + } else if (state is StepTwoState) { + return Column( + children: [ + const SizedBox( + height: 10, + ), + const CircleTitleStepperWidget( + title: 'Space', + titleColor: ColorsManager.softGray, + cicleIcon: Icon( + Icons.check, + color: ColorsManager.whiteColors, + size: 12, + ), + circleColor: ColorsManager.trueIconGreen, + radius: 3, + ), + Container( + alignment: Alignment.centerLeft, + height: 50, + child: const VerticalDivider( + width: 8, + )), + const CircleTitleStepperWidget( + title: 'Settings', + ) + ], + ); + } else if (state is StepEditMode) { + return const CircleTitleStepperWidget( + title: 'Settings', + ); + } else { + return const SizedBox(); + } + }, + ), + ); + } +} + +class CircleTitleStepperWidget extends StatelessWidget { + final double? radius; + final Widget? cicleIcon; + final Color? circleColor; + final Color? titleColor; + final String title; + const CircleTitleStepperWidget({ + super.key, + required this.title, + this.circleColor, + this.cicleIcon, + this.titleColor, + this.radius, + }); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + CircleAvatar( + minRadius: radius ?? 5, + backgroundColor: circleColor ?? ColorsManager.blue1, + child: cicleIcon, + ), + const SizedBox( + width: 10, + ), + Text( + title, + style: TextStyle( + fontWeight: FontWeight.w700, + color: titleColor ?? ColorsManager.blackColor, + ), + ), + ], + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart new file mode 100644 index 00000000..608fc06a --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_svg/flutter_svg.dart'; + +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class TimePickerWidget extends StatefulWidget { + const TimePickerWidget({ + super.key, + required this.onTimePicked, + }); + + final void Function(TimeOfDay? timePicked) onTimePicked; + @override + State createState() => _TimePickerWidgetState(); +} + +class _TimePickerWidgetState extends State { + TimeOfDay? timePicked; + @override + Widget build(BuildContext context) { + return InkWell( + borderRadius: BorderRadius.circular(10), + onTap: () async { + timePicked = await showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + builder: (context, child) { + return Theme( + data: ThemeData.light().copyWith( + colorScheme: const ColorScheme.light( + primary: ColorsManager.primaryColor, + onSurface: Colors.black, + ), + ), + child: child!, + ); + }, + ); + widget.onTimePicked(timePicked); + setState(() {}); + }, + child: Row( + children: [ + Container( + width: 56, + height: 32, + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + bottomLeft: Radius.circular(10), + ), + ), + alignment: Alignment.center, + child: SvgPicture.asset(Assets.clockIcon), + ), + Container( + width: 120, + height: 32, + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topRight: Radius.circular(10), + bottomRight: Radius.circular(10), + ), + ), + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text( + timePicked == null + ? TimeOfDay.now().format(context) + : timePicked!.format(context), + style: const TextStyle(color: Color(0xB2D5D5D5)), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart new file mode 100644 index 00000000..6fc142b6 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; + +class WeekDaysCheckboxRow extends StatefulWidget { + const WeekDaysCheckboxRow({super.key}); + + @override + State createState() => _WeekDaysCheckboxRowState(); +} + +class _WeekDaysCheckboxRowState extends State { + final Map _daysChecked = { + 'Mon': false, + 'Tue': false, + 'Wed': false, + 'Thu': false, + 'Fri': false, + 'Sat': false, + 'Sun': false, + }; + + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: _daysChecked.entries.map((entry) { + return Expanded( + child: Row( + children: [ + Expanded( + child: Checkbox( + value: entry.value, + onChanged: (newValue) { + setState(() { + _daysChecked[entry.key] = newValue ?? false; + final selectedDays = _daysChecked.entries + .where((e) => e.value) + .map((e) => e.key) + .toList(); + + for (var space in context + .read() + .selectedBookableSpaces) { + space.spaceConfig.bookableDays = selectedDays; + } + }); + }, + ), + ), + Expanded( + child: Text( + entry.key, + style: const TextStyle(fontSize: 10), + )), + ], + ), + ); + }).toList(), + ); + } +} From 1a3dc60bd211d347f1355fc693fcadcc29384534 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 7 Jul 2025 15:38:18 +0300 Subject: [PATCH 03/71] build blocs for bookable and nonBookable spaces --- .../bookable_spaces_bloc.dart | 34 +++++++ .../bookable_spaces_event.dart | 13 +++ .../bookable_spaces_state.dart | 26 ++++++ .../non_bookaable_spaces_bloc.dart | 91 +++++++++++++++++++ .../non_bookaable_spaces_event.dart | 31 +++++++ .../non_bookaable_spaces_state.dart | 26 ++++++ .../blocs/steps_cubit/cubit/steps_cubit.dart | 18 ++++ .../blocs/steps_cubit/cubit/steps_state.dart | 16 ++++ 8 files changed, 255 insertions(+) create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_state.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_state.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_state.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart new file mode 100644 index 00000000..14288a25 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart @@ -0,0 +1,34 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; + +part 'bookable_spaces_event.dart'; +part 'bookable_spaces_state.dart'; + +class BookableSpacesBloc + extends Bloc { + final BookableSpacesService bookableSpacesService; + BookableSpacesBloc(this.bookableSpacesService) + : super(BookableSpacesInitial()) { + on(_onLoadBookableSpaces); + } + + Future _onLoadBookableSpaces( + LoadBookableSpacesEvent event, Emitter emit) async { + emit(BookableSpacesLoading()); + try { + final bookableSpaces = await bookableSpacesService.load(event.params); + emit(BookableSpacesLoaded(bookableSpacesList: bookableSpaces)); + } on APIException catch (e) { + emit(BookableSpacesError(error: e.message)); + } catch (e) { + emit( + BookableSpacesError(error: e.toString()), + ); + } + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart new file mode 100644 index 00000000..47a1b396 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart @@ -0,0 +1,13 @@ +part of 'bookable_spaces_bloc.dart'; + +sealed class BookableSpacesEvent extends Equatable { + const BookableSpacesEvent(); + + @override + List get props => []; +} + +class LoadBookableSpacesEvent extends BookableSpacesEvent { + final BookableSpacesParams params; + const LoadBookableSpacesEvent(this.params); +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_state.dart new file mode 100644 index 00000000..d722ddef --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_state.dart @@ -0,0 +1,26 @@ +part of 'bookable_spaces_bloc.dart'; + +sealed class BookableSpacesState extends Equatable { + const BookableSpacesState(); + + @override + List get props => []; +} + +final class BookableSpacesInitial extends BookableSpacesState {} + +final class BookableSpacesLoading extends BookableSpacesState {} + +final class BookableSpacesLoaded extends BookableSpacesState { + final PaginatedDataModel bookableSpacesList; + const BookableSpacesLoaded({ + required this.bookableSpacesList, + }); +} + +final class BookableSpacesError extends BookableSpacesState { + final String error; + const BookableSpacesError({ + required this.error, + }); +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart new file mode 100644 index 00000000..24ac31d7 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart @@ -0,0 +1,91 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; + +part 'non_bookaable_spaces_event.dart'; +part 'non_bookaable_spaces_state.dart'; + +class NonBookableSpacesBloc + extends Bloc { + NonBookableSpacesService nonBookableSpacesService; + List selectedBookableSpaces = []; + NonBookableSpacesBloc(this.nonBookableSpacesService) + : super(NonBookableSpacesInitial()) { + on(_onLoadUnBookableSpacesEvent); + on(_onAddToBookableSpaceEvent); + on(_onRemoveFromBookableSpaceEvent); + on(_onSendBookableSpacesToApi); + } + Future _onLoadUnBookableSpacesEvent(LoadUnBookableSpacesEvent event, + Emitter emit) async { + emit(NonBookableSpacesLoading()); + try { + final nonBookableSpacesList = await nonBookableSpacesService.load( + event.nonBookableSpacesParams, + ); + emit( + NonBookableSpacesLoaded(nonBookableSpaces: nonBookableSpacesList), + ); + } catch (e) { + emit( + NonBookableSpacesError(e.toString()), + ); + } + } + + void _onAddToBookableSpaceEvent( + AddToBookableSpaceEvent event, + Emitter emit, + ) { + if (state is NonBookableSpacesLoaded) { + final currentState = state as NonBookableSpacesLoaded; + + final updatedSelectedSpaces = + List.from(currentState.selectedBookableSpaces) + ..add(event.nonBookableSpace); + + selectedBookableSpaces.add(event.nonBookableSpace); + + emit( + NonBookableSpacesLoaded( + nonBookableSpaces: currentState.nonBookableSpaces, + selectedBookableSpaces: updatedSelectedSpaces, + ), + ); + } + } + + void _onRemoveFromBookableSpaceEvent(RemoveFromBookableSpaceEvent event, + Emitter emit) { + if (state is NonBookableSpacesLoaded) { + final currentState = state as NonBookableSpacesLoaded; + currentState.selectedBookableSpaces.remove(event.bookableSpace); + selectedBookableSpaces.remove(event.bookableSpace); + emit( + NonBookableSpacesLoaded( + nonBookableSpaces: currentState.nonBookableSpaces, + selectedBookableSpaces: currentState.selectedBookableSpaces, + ), + ); + } + } + + Future _onSendBookableSpacesToApi(SendBookableSpacesToApi event, + Emitter emit) async { + emit(NonBookableSpacesLoading()); + try { + await nonBookableSpacesService.sendBookableSpacesToApi( + SendBookableSpacesToApiParams.fromBookableSpacesModel( + selectedBookableSpaces), + ); + } catch (e) { + emit( + NonBookableSpacesError(e.toString()), + ); + } + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart new file mode 100644 index 00000000..392cf4d4 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart @@ -0,0 +1,31 @@ +part of 'non_bookaable_spaces_bloc.dart'; + +sealed class NonBookableSpacesEvent extends Equatable { + const NonBookableSpacesEvent(); + + @override + List get props => []; +} + +class LoadUnBookableSpacesEvent extends NonBookableSpacesEvent { + final NonBookableSpacesParams nonBookableSpacesParams; + const LoadUnBookableSpacesEvent({ + required this.nonBookableSpacesParams, + }); +} + +class AddToBookableSpaceEvent extends NonBookableSpacesEvent { + final BookableSpacemodel nonBookableSpace; + const AddToBookableSpaceEvent({ + required this.nonBookableSpace, + }); +} + +class RemoveFromBookableSpaceEvent extends NonBookableSpacesEvent { + final BookableSpacemodel bookableSpace; + const RemoveFromBookableSpaceEvent({ + required this.bookableSpace, + }); +} + +class SendBookableSpacesToApi extends NonBookableSpacesEvent {} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_state.dart new file mode 100644 index 00000000..850b114d --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_state.dart @@ -0,0 +1,26 @@ +part of 'non_bookaable_spaces_bloc.dart'; + +sealed class NonBookableSpacesState extends Equatable { + const NonBookableSpacesState(); + + @override + List get props => []; +} + +final class NonBookableSpacesInitial extends NonBookableSpacesState {} + +class NonBookableSpacesLoading extends NonBookableSpacesState {} + +class NonBookableSpacesLoaded extends NonBookableSpacesState { + final PaginatedDataModel nonBookableSpaces; + final List selectedBookableSpaces; + const NonBookableSpacesLoaded({ + required this.nonBookableSpaces, + this.selectedBookableSpaces = const [], + }); +} + +class NonBookableSpacesError extends NonBookableSpacesState { + final String error; + const NonBookableSpacesError(this.error); +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart new file mode 100644 index 00000000..07dba931 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart @@ -0,0 +1,18 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; + +part 'steps_state.dart'; + +class StepsCubit extends Cubit { + StepsCubit() : super(StepsInitial()); + + void initDialogValue() { + emit(StepOneState()); + } + + void goToNextStep() { + if (state is StepOneState) { + emit(StepTwoState()); + } + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_state.dart new file mode 100644 index 00000000..d6b8dd37 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_state.dart @@ -0,0 +1,16 @@ +part of 'steps_cubit.dart'; + +sealed class StepsState extends Equatable { + const StepsState(); + + @override + List get props => []; +} + +final class StepsInitial extends StepsState {} + +final class StepOneState extends StepsState {} + +final class StepTwoState extends StepsState {} + +final class StepEditMode extends StepsState {} From f89660a9ff6ec5cdfa59936a5c98061c42daf146 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 7 Jul 2025 15:38:52 +0300 Subject: [PATCH 04/71] build modeling and params --- .../domain/models/bookable_space_config.dart | 39 +++++++++++++++++ .../domain/models/bookable_space_model.dart | 42 +++++++++++++++++++ .../domain/params/bookable_spaces_params.dart | 10 +++++ .../params/non_bookable_spaces_params.dart | 12 ++++++ 4 files changed, 103 insertions(+) create mode 100644 lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart new file mode 100644 index 00000000..68e7bfd8 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart @@ -0,0 +1,39 @@ +class BookableSpaceConfig { + String configUuid; + List bookableDays; + String bookingStartTime; + String bookingEndTime; + int cost; + bool availability; + BookableSpaceConfig({ + required this.configUuid, + required this.availability, + required this.bookableDays, + required this.bookingEndTime, + required this.bookingStartTime, + required this.cost, + }); + factory BookableSpaceConfig.zero() => BookableSpaceConfig( + configUuid: '', + bookableDays: [], + availability: false, + bookingEndTime: '', + bookingStartTime: '', + cost: -1, + ); + factory BookableSpaceConfig.fromJson(Map json) => + BookableSpaceConfig( + configUuid: json['uuid'] as String, + bookableDays: json['daysAvailable'] as List, + availability: (json['active'] as bool?) ?? false, + bookingEndTime: json['startTime'] as String, + bookingStartTime: json['endTime'] as String, + cost: json['points'] as int, + ); + bool get isValid => + configUuid.isNotEmpty && + bookableDays.isNotEmpty && + bookingStartTime.isNotEmpty && + bookingEndTime.isNotEmpty && + cost > 0; +} diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart new file mode 100644 index 00000000..f108ab08 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart @@ -0,0 +1,42 @@ +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart'; + +class BookableSpacemodel { + String spaceUuid; + String spaceName; + BookableSpaceConfig spaceConfig; + String spaceVirtualAddress; + + BookableSpacemodel({ + required this.spaceUuid, + required this.spaceName, + required this.spaceConfig, + required this.spaceVirtualAddress, + }); + factory BookableSpacemodel.zero() => BookableSpacemodel( + spaceUuid: '', + spaceName: '', + spaceConfig: BookableSpaceConfig.zero(), + spaceVirtualAddress: '', + ); + factory BookableSpacemodel.fromJson(Map json) => + BookableSpacemodel( + spaceUuid: json['uuid'] as String, + spaceName: json['spaceName'] as String, + spaceConfig: BookableSpaceConfig.fromJson( + json['bookableConfig'] as Map), + spaceVirtualAddress: json['spaceVirtualAddress'] as String, + ); + + static List fromJsonList(List jsonList) => + jsonList + .map( + (e) => BookableSpacemodel.fromJson(e as Map), + ) + .toList(); + + bool get isValid => + spaceUuid.isNotEmpty && + spaceName.isNotEmpty && + spaceVirtualAddress.isNotEmpty && + spaceConfig.isValid; +} diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart b/lib/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart new file mode 100644 index 00000000..02e60733 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart @@ -0,0 +1,10 @@ +class BookableSpacesParams { + int currentPage; + BookableSpacesParams({ + required this.currentPage, + }); + + Map toJson() => { + 'page': currentPage, + }; +} diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart b/lib/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart new file mode 100644 index 00000000..b688e6b1 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart @@ -0,0 +1,12 @@ +class NonBookableSpacesParams { + int currentPage; + String? searchedWords; + NonBookableSpacesParams({ + required this.currentPage, + this.searchedWords, + }); + + Map toJson() => { + 'page': currentPage, + }; +} From e4a27b565141d4e464a977c3a173397b1ff72db1 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 7 Jul 2025 15:39:19 +0300 Subject: [PATCH 05/71] build services for them --- .../data/dummy_bookable_spaces_service.dart | 60 ++++++++++++++++ .../data/dummy_non_nookable_spaces.dart | 72 +++++++++++++++++++ .../data/remote_bookable_spaces_service.dart | 45 ++++++++++++ .../data/remote_non_bookable_spaces.dart | 71 ++++++++++++++++++ .../service/bookable_spaces_service.dart | 8 +++ .../service/non_bookable_spaces_service.dart | 10 +++ 6 files changed, 266 insertions(+) create mode 100644 lib/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart b/lib/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart new file mode 100644 index 00000000..c3687119 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart @@ -0,0 +1,60 @@ +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; + +class DummyBookableSpacesService implements BookableSpacesService { + @override + Future> load( + BookableSpacesParams param) async { + return PaginatedDataModel( + data: [ + BookableSpacemodel( + spaceName: 'space1', + spaceConfig: BookableSpaceConfig( + configUuid: 'uuid', + bookableDays: ['wed', 'saturday'], + availability: true, + bookingEndTime: '08:00 PM', + bookingStartTime: '5:00 PM', + cost: 6, + ), + spaceUuid: 'uuiiddd', + spaceVirtualAddress: 'idvirtual1', + ), + BookableSpacemodel( + spaceName: 'space2', + spaceConfig: BookableSpaceConfig( + configUuid: 'uuid', + bookableDays: ['wed', 'thur'], + availability: true, + bookingEndTime: '08:00 PM', + bookingStartTime: '5:00 PM', + cost: 6, + ), + spaceUuid: 'uuiiddd', + spaceVirtualAddress: 'idvirtual1', + ), + BookableSpacemodel( + spaceName: 'space3', + spaceConfig: BookableSpaceConfig( + configUuid: 'uuid', + bookableDays: ['wed', 'fri', 'tues'], + availability: true, + bookingEndTime: '08:00 PM', + bookingStartTime: '5:00 PM', + cost: 6, + ), + spaceUuid: 'uuiiddd', + spaceVirtualAddress: 'idvirtual1', + ) + ], + page: 1, + size: 1, + hasNext: false, + totalItems: 3, + totalPages: 1, + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart b/lib/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart new file mode 100644 index 00000000..6f59e368 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart @@ -0,0 +1,72 @@ +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; + +class DummyNonNookableSpaces implements NonBookableSpacesService { + @override + Future> load( + NonBookableSpacesParams params) { + return Future.value(PaginatedDataModel( + data: [ + BookableSpacemodel( + spaceName: 'space3', + spaceConfig: BookableSpaceConfig( + configUuid: 'uuid', + bookableDays: ['wed', 'saturday'], + availability: true, + bookingEndTime: '08:00 PM', + bookingStartTime: '5:00 PM', + cost: 6, + ), + spaceUuid: 'uuiiddd', + spaceVirtualAddress: 'idvirtual1', + ), + BookableSpacemodel( + spaceName: 'space3', + spaceConfig: BookableSpaceConfig( + configUuid: 'uuid', + bookableDays: ['wed', 'saturday', 'thuresday'], + availability: true, + bookingEndTime: '08:00 PM', + bookingStartTime: '5:00 PM', + cost: 5, + ), + spaceUuid: 'uuiiddd', + spaceVirtualAddress: 'idvirtual2', + ), + BookableSpacemodel( + spaceName: 'space3', + spaceConfig: BookableSpaceConfig( + configUuid: 'uuid', + bookableDays: [ + 'saturday', + 'sunday', + 'Monday', + 'tuesday', + 'wed', + 'thuresday' + ], + availability: true, + bookingEndTime: '08:00 PM', + bookingStartTime: '5:00 PM', + cost: 2, + ), + spaceUuid: 'uuiiddd', + spaceVirtualAddress: 'idvirtual3', + ) + ], + page: 1, + size: 1, + hasNext: false, + totalPages: 0, + totalItems: 0, + )); + } + + @override + Future sendBookableSpacesToApi( + SendBookableSpacesToApiParams params) async {} +} diff --git a/lib/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart b/lib/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart new file mode 100644 index 00000000..49563e45 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart @@ -0,0 +1,45 @@ +import 'package:dio/dio.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + +class RemoteBookableSpacesService implements BookableSpacesService { + final HTTPService _httpService; + RemoteBookableSpacesService(this._httpService); + static const _defaultErrorMessage = 'Failed to load tags'; + @override + Future> load( + BookableSpacesParams param) async { + try { + final response = await _httpService.get( + //TODO: you have to Chage this API call Path + path: ApiEndpoints.listTags, + //*************|********** */ + expectedResponseModel: (json) { + final result = json as Map; + return PaginatedDataModel.fromJson( + result, + BookableSpacemodel.fromJsonList, + ); + }, + ); + return response; + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [ + _defaultErrorMessage, + errorMessage, + ].join(': '); + throw APIException(formattedErrorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart new file mode 100644 index 00000000..5224eb2b --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart @@ -0,0 +1,71 @@ +import 'package:dio/dio.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + +class RemoteNonBookableSpaces implements NonBookableSpacesService { + final HTTPService _httpService; + RemoteNonBookableSpaces(this._httpService); + static const _defaultErrorMessage = 'Failed to load Spaces'; + + @override + Future> load( + NonBookableSpacesParams params) async { + try { + final response = await _httpService.get( + //TODO: you have to Chage this API call Path + path: ApiEndpoints.listTags, + //*************|********** */ + expectedResponseModel: (json) { + final result = json as Map; + return PaginatedDataModel.fromJson( + result, + BookableSpacemodel.fromJsonList, + ); + }, + ); + return response; + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [ + _defaultErrorMessage, + errorMessage, + ].join(': '); + throw APIException(formattedErrorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } + + @override + Future sendBookableSpacesToApi( + SendBookableSpacesToApiParams params) async { + try { + await _httpService.post( + path: ApiEndpoints.addBookableSpaces, + body: params.toJson(), + expectedResponseModel: (p0) {}, + ); + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [ + _defaultErrorMessage, + errorMessage, + ].join(': '); + throw APIException(formattedErrorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart b/lib/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart new file mode 100644 index 00000000..5929a935 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart @@ -0,0 +1,8 @@ +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; + +abstract class BookableSpacesService { + Future> load( + BookableSpacesParams param); +} diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart b/lib/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart new file mode 100644 index 00000000..cbcb24fa --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart @@ -0,0 +1,10 @@ +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; + +abstract class NonBookableSpacesService { + Future> load( + NonBookableSpacesParams params); + Future sendBookableSpacesToApi(SendBookableSpacesToApiParams params); +} From 7cf4d0b5a9ea9059f8679a756aa73d26e65e5953 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 7 Jul 2025 15:40:04 +0300 Subject: [PATCH 06/71] add to route and related endpoints and color and assets --- lib/utils/app_routes.dart | 5 +++++ lib/utils/color_manager.dart | 1 + lib/utils/constants/api_const.dart | 6 +++++- lib/utils/constants/assets.dart | 4 ++++ lib/utils/constants/routes_const.dart | 1 + 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/utils/app_routes.dart b/lib/utils/app_routes.dart index 263bdbd6..b93965ce 100644 --- a/lib/utils/app_routes.dart +++ b/lib/utils/app_routes.dart @@ -1,4 +1,5 @@ import 'package:go_router/go_router.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart'; import 'package:syncrow_web/pages/access_management/view/access_management.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/views/analytics_page.dart'; import 'package:syncrow_web/pages/auth/view/login_page.dart'; @@ -28,6 +29,10 @@ class AppRoutes { path: RoutesConst.accessManagementPage, builder: (context, state) => const AccessManagementPage(), ), + GoRoute( + path: RoutesConst.manageBookableSapcesPage, + builder: (context, state) => const ManageBookableSpacesPage(), + ), GoRoute( path: RoutesConst.deviceManagementPage, builder: (context, state) => const DeviceManagementPage(), diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 40fca1fa..ee69f397 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -64,6 +64,7 @@ abstract class ColorsManager { static const Color circleRolesBackground = Color(0xFFF8F8F8); static const Color activeGreen = Color(0xFF99FF93); static const Color activeGreenText = Color(0xFF008905); + static const Color trueIconGreen = Color(0xFFBBEC6C); static const Color disabledPink = Color(0xFFFF9395); static const Color disabledRedText = Color(0xFF890002); static const Color invitedOrange = Color(0xFFFFE193); diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index eb7b6a3e..24175a37 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -46,7 +46,8 @@ abstract class ApiEndpoints { // Community Module static const String createCommunity = '/projects/{projectId}/communities'; static const String getCommunityList = '/projects/{projectId}/communities'; - static const String getCommunityListv2 = '/projects/{projectId}/communities/v2'; + static const String getCommunityListv2 = + '/projects/{projectId}/communities/v2'; static const String getCommunityById = '/projects/{projectId}/communities/{communityId}'; static const String updateCommunity = @@ -138,4 +139,7 @@ abstract class ApiEndpoints { static const String assignDeviceToRoom = '/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/subspaces/{subSpaceUuid}/devices/{deviceUuid}'; static const String saveSchedule = '/schedule/{deviceUuid}'; + + ////booking System + static const String addBookableSpaces = '/bookable-spaces'; } diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index f92975f3..0150ec36 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -18,6 +18,9 @@ class Assets { 'assets/images/Password_invisible.svg'; static const String visiblePassword = 'assets/images/password_visible.svg'; static const String accessIcon = 'assets/images/access_icon.svg'; + static const String addButtonIcon = 'assets/icons/add_button_Icon.svg'; + static const String backButtonIcon = 'assets/icons/back_button_icon.svg'; + static const String emptyDataTable = 'assets/icons/no_data_table.svg'; static const String spaseManagementIcon = 'assets/images/spase_management_icon.svg'; static const String devicesIcon = 'assets/images/devices_icon.svg'; @@ -205,6 +208,7 @@ class Assets { //assets/icons/ac_lock.svg static const String acLock = 'assets/icons/ac_lock.svg'; + static const String clockIcon = 'assets/icons/clock_icon.svg'; //assets/icons/ac_schedule.svg static const String acSchedule = 'assets/icons/ac_schedule.svg'; diff --git a/lib/utils/constants/routes_const.dart b/lib/utils/constants/routes_const.dart index 83b1896f..19116f17 100644 --- a/lib/utils/constants/routes_const.dart +++ b/lib/utils/constants/routes_const.dart @@ -7,4 +7,5 @@ class RoutesConst { static const String spacesManagementPage = '/spaces_management-page'; static const String rolesAndPermissions = '/roles_and_Permissions-page'; static const String analytics = '/syncrow_analytics'; + static const String manageBookableSapcesPage = '/manage_bookable_spaces'; } From 387586f6f71204d7e793c6fdf015b3cd8404b351 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 7 Jul 2025 15:40:20 +0300 Subject: [PATCH 07/71] unused commnet --- lib/pages/visitor_password/bloc/visitor_password_bloc.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart index c5ee3259..b963f6b4 100644 --- a/lib/pages/visitor_password/bloc/visitor_password_bloc.dart +++ b/lib/pages/visitor_password/bloc/visitor_password_bloc.dart @@ -181,7 +181,7 @@ class VisitorPasswordBloc effectiveTimeTimeStamp = selectedTimestamp; startTimeAccess = selectedDateTime.toString().split('.').first; } else { - // END TIME VALIDATION + if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) { await showDialog( From 35e9b606b24813af94f6a5ecab679a65c89fcf07 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 7 Jul 2025 15:40:49 +0300 Subject: [PATCH 08/71] use param to send Update Api for unbookable to be bookable --- .../send_bookable_spaces_to_api_params.dart | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart b/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart new file mode 100644 index 00000000..caa489c0 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart @@ -0,0 +1,38 @@ +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; + +class SendBookableSpacesToApiParams { + List spaceUuids; + List daysAvailable; + String startTime; + String endTime; + int points; + SendBookableSpacesToApiParams({ + required this.spaceUuids, + required this.daysAvailable, + required this.startTime, + required this.endTime, + required this.points, + }); + + static SendBookableSpacesToApiParams fromBookableSpacesModel( + List bookableSpaces) { + return SendBookableSpacesToApiParams( + spaceUuids: bookableSpaces.map((space) => space.spaceUuid).toList(), + daysAvailable: bookableSpaces + .expand((space) => space.spaceConfig.bookableDays) + .toSet() + .toList(), + startTime: bookableSpaces.first.spaceConfig.bookingStartTime, + endTime: bookableSpaces.first.spaceConfig.bookingEndTime, + points: bookableSpaces.first.spaceConfig.cost, + ); + } + + Map toJson() => { + 'spaceUuids': spaceUuids, + 'daysAvailable': daysAvailable, + 'startTime': startTime, + 'endTime': endTime, + 'points': points + }; +} From c13119a4e861bb5d41c28621f08355bd1e051da2 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 7 Jul 2025 15:41:02 +0300 Subject: [PATCH 09/71] migrate datatable2 package --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index c4692ac4..023e7465 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,7 +44,7 @@ dependencies: flutter_secure_storage: ^9.2.2 shared_preferences: ^2.3.0 dropdown_button2: ^2.3.9 - data_table_2: ^2.5.15 + data_table_2: ^2.6.0 go_router: intl: ^0.20.2 dropdown_search: ^6.0.2 From 368b1be3c0b23be4f868981c9711a18e046048ca Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 7 Jul 2025 17:07:23 +0300 Subject: [PATCH 10/71] fix conditions for start and end time for reservation --- .../manage_bookable_spaces_screen.dart | 18 +++++-- .../screens/setup_bookable_spaces_dialog.dart | 12 +++-- .../widgets/step_two_details_widget.dart | 50 +++++++++++++++---- .../widgets/time_picker_widget.dart | 5 +- lib/utils/string_utils.dart | 19 +++++++ 5 files changed, 86 insertions(+), 18 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart index f3a21be1..dad161f1 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart @@ -142,12 +142,22 @@ class ManageBookableSpacesWidget extends StatelessWidget { .toList(), ), )), - DataCell(Padding( + DataCell( + Padding( padding: const EdgeInsetsGeometry.only(left: 10), - child: Text(space.spaceConfig.bookingStartTime))), - DataCell(Padding( + child: Text( + space.spaceConfig.bookingStartTime.format(context), + ), + ), + ), + DataCell( + Padding( padding: const EdgeInsetsGeometry.only(left: 10), - child: Text(space.spaceConfig.bookingEndTime))), + child: Text( + space.spaceConfig.bookingEndTime.format(context), + ), + ), + ), DataCell(Padding( padding: const EdgeInsetsGeometry.only(left: 10), child: Text('${space.spaceConfig.cost} Points'))), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart index f073b6ba..a4f32f63 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart @@ -79,6 +79,7 @@ class SetupBookableSpacesDialog extends StatelessWidget { if (selectedSpaces.isNotEmpty) { context.read().goToNextStep(); } else { + ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Please select at least one space.'), @@ -87,18 +88,23 @@ class SetupBookableSpacesDialog extends StatelessWidget { } } else if (stepsState is StepTwoState) { selectedSpaces.forEach( - (e) => - e.spaceConfig.cost = int.parse(pointsController.text), + (e) => e.spaceConfig.cost = int.parse( + pointsController.text.isEmpty + ? '0' + : pointsController.text), ); if (selectedSpaces.any( (element) => !element.isValid, )) { + ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Please fill the required fields.'), ), ); - } else {} + } else { + print(selectedSpaces.first.spaceUuid); + } } }, onCancelPressed: () => context.pop(), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart index 8825fb6a..dc750ebb 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart @@ -5,6 +5,8 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/prese import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/string_utils.dart'; class StepTwoDetailsWidget extends StatelessWidget { final TextEditingController pointsController; @@ -29,22 +31,52 @@ class StepTwoDetailsWidget extends StatelessWidget { TitleAndTimePickerWidget( title: 'Booking Start Time', onTimePicked: (timePicked) { + if (timePicked == null) { + return; + } final nonBookableBloc = context.read(); - nonBookableBloc.selectedBookableSpaces.forEach( - (e) => e.spaceConfig.bookingStartTime = - timePicked!.format(context), - ); + if (isEndTimeAfterStartTime( + timePicked, nonBookableBloc.endTime)) { + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: + Text("You can't choose start Time Before End time"), + duration: Duration(seconds: 2), + backgroundColor: ColorsManager.red, + )); + throw Exception(); + } else { + nonBookableBloc.selectedBookableSpaces.forEach( + (e) => e.spaceConfig.bookingStartTime = timePicked, + ); + } }, ), - SizedBox(width: 20,), + const SizedBox( + width: 20, + ), TitleAndTimePickerWidget( title: 'Booking End Time', onTimePicked: (timePicked) { + if (timePicked == null) { + return; + } final nonBookableBloc = context.read(); - nonBookableBloc.selectedBookableSpaces.forEach( - (e) => e.spaceConfig.bookingEndTime = - timePicked!.format(context), - ); + if (isEndTimeAfterStartTime( + nonBookableBloc.startTime, timePicked)) { + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: + Text("You can't choose End Time After Start time"), + duration: Duration(seconds: 2), + backgroundColor: ColorsManager.red, + )); + throw Exception(); + } else { + nonBookableBloc.selectedBookableSpaces.forEach( + (e) => e.spaceConfig.bookingEndTime = timePicked, + ); + } }, ) ], diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart index 608fc06a..c02491f3 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart @@ -23,7 +23,7 @@ class _TimePickerWidgetState extends State { return InkWell( borderRadius: BorderRadius.circular(10), onTap: () async { - timePicked = await showTimePicker( + final tempTime = await showTimePicker( context: context, initialTime: TimeOfDay.now(), builder: (context, child) { @@ -38,7 +38,8 @@ class _TimePickerWidgetState extends State { ); }, ); - widget.onTimePicked(timePicked); + widget.onTimePicked(tempTime); + timePicked = tempTime; setState(() {}); }, child: Row( diff --git a/lib/utils/string_utils.dart b/lib/utils/string_utils.dart index c8d8e2af..b1424412 100644 --- a/lib/utils/string_utils.dart +++ b/lib/utils/string_utils.dart @@ -1,6 +1,25 @@ +import 'package:flutter/material.dart'; + class StringUtils { static String capitalizeFirstLetter(String text) { if (text.isEmpty) return text; return text[0].toUpperCase() + text.substring(1); } } + +bool isEndTimeAfterStartTime(TimeOfDay start, TimeOfDay end) { + final startMinutes = start.hour * 60 + start.minute; + final endMinutes = end.hour * 60 + end.minute; + + if (endMinutes <= startMinutes) { + return true; + } + + return false; +} + +String formatTimeOfDayTo24HourString(TimeOfDay time) { + final hour = time.hour.toString().padLeft(2, '0'); + final minute = time.minute.toString().padLeft(2, '0'); + return '$hour:$minute'; +} From 42c410d9824d68228129af5ecbcc4282de8b18c7 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 7 Jul 2025 17:07:38 +0300 Subject: [PATCH 11/71] use TimeOfDay instead of String --- .../data/dummy_bookable_spaces_service.dart | 15 +++++----- .../data/dummy_non_nookable_spaces.dart | 13 +++++---- .../domain/models/bookable_space_config.dart | 28 +++++++++++-------- .../send_bookable_spaces_to_api_params.dart | 9 ++++-- .../non_bookaable_spaces_bloc.dart | 10 ++++++- 5 files changed, 47 insertions(+), 28 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart b/lib/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart index c3687119..a4255e2f 100644 --- a/lib/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart +++ b/lib/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart @@ -1,5 +1,6 @@ -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; @@ -16,8 +17,8 @@ class DummyBookableSpacesService implements BookableSpacesService { configUuid: 'uuid', bookableDays: ['wed', 'saturday'], availability: true, - bookingEndTime: '08:00 PM', - bookingStartTime: '5:00 PM', + bookingEndTime: const TimeOfDay(hour: 13, minute: 20), + bookingStartTime: const TimeOfDay(hour: 13, minute: 20), cost: 6, ), spaceUuid: 'uuiiddd', @@ -29,8 +30,8 @@ class DummyBookableSpacesService implements BookableSpacesService { configUuid: 'uuid', bookableDays: ['wed', 'thur'], availability: true, - bookingEndTime: '08:00 PM', - bookingStartTime: '5:00 PM', + bookingEndTime: const TimeOfDay(hour: 13, minute: 20), + bookingStartTime: const TimeOfDay(hour: 13, minute: 20), cost: 6, ), spaceUuid: 'uuiiddd', @@ -42,8 +43,8 @@ class DummyBookableSpacesService implements BookableSpacesService { configUuid: 'uuid', bookableDays: ['wed', 'fri', 'tues'], availability: true, - bookingEndTime: '08:00 PM', - bookingStartTime: '5:00 PM', + bookingEndTime: const TimeOfDay(hour: 13, minute: 20), + bookingStartTime: const TimeOfDay(hour: 13, minute: 20), cost: 6, ), spaceUuid: 'uuiiddd', diff --git a/lib/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart b/lib/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart index 6f59e368..675fcf4c 100644 --- a/lib/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart +++ b/lib/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; @@ -17,8 +18,8 @@ class DummyNonNookableSpaces implements NonBookableSpacesService { configUuid: 'uuid', bookableDays: ['wed', 'saturday'], availability: true, - bookingEndTime: '08:00 PM', - bookingStartTime: '5:00 PM', + bookingEndTime: const TimeOfDay(hour: 13, minute: 20), + bookingStartTime: const TimeOfDay(hour: 6, minute: 20), cost: 6, ), spaceUuid: 'uuiiddd', @@ -30,8 +31,8 @@ class DummyNonNookableSpaces implements NonBookableSpacesService { configUuid: 'uuid', bookableDays: ['wed', 'saturday', 'thuresday'], availability: true, - bookingEndTime: '08:00 PM', - bookingStartTime: '5:00 PM', + bookingEndTime: const TimeOfDay(hour: 13, minute: 20), + bookingStartTime: const TimeOfDay(hour: 5, minute: 20), cost: 5, ), spaceUuid: 'uuiiddd', @@ -50,8 +51,8 @@ class DummyNonNookableSpaces implements NonBookableSpacesService { 'thuresday' ], availability: true, - bookingEndTime: '08:00 PM', - bookingStartTime: '5:00 PM', + bookingEndTime: const TimeOfDay(hour: 13, minute: 20), + bookingStartTime: const TimeOfDay(hour: 15, minute: 20), cost: 2, ), spaceUuid: 'uuiiddd', diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart index 68e7bfd8..958822bd 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart @@ -1,8 +1,10 @@ +import 'package:flutter/material.dart'; + class BookableSpaceConfig { String configUuid; List bookableDays; - String bookingStartTime; - String bookingEndTime; + TimeOfDay bookingStartTime; + TimeOfDay bookingEndTime; int cost; bool availability; BookableSpaceConfig({ @@ -17,8 +19,8 @@ class BookableSpaceConfig { configUuid: '', bookableDays: [], availability: false, - bookingEndTime: '', - bookingStartTime: '', + bookingEndTime: TimeOfDay.now(), + bookingStartTime: TimeOfDay.now(), cost: -1, ); factory BookableSpaceConfig.fromJson(Map json) => @@ -26,14 +28,18 @@ class BookableSpaceConfig { configUuid: json['uuid'] as String, bookableDays: json['daysAvailable'] as List, availability: (json['active'] as bool?) ?? false, - bookingEndTime: json['startTime'] as String, - bookingStartTime: json['endTime'] as String, + bookingEndTime: parseTimeOfDay(json['startTime'] as String), + bookingStartTime: parseTimeOfDay(json['endTime'] as String), cost: json['points'] as int, ); + + static TimeOfDay parseTimeOfDay(String timeString) { + final parts = timeString.split(':'); + final hour = int.parse(parts[0]); + final minute = int.parse(parts[1]); + return TimeOfDay(hour: hour, minute: minute); + } + bool get isValid => - configUuid.isNotEmpty && - bookableDays.isNotEmpty && - bookingStartTime.isNotEmpty && - bookingEndTime.isNotEmpty && - cost > 0; + configUuid.isNotEmpty && bookableDays.isNotEmpty && cost > 0; } diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart b/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart index caa489c0..73e590eb 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart @@ -1,4 +1,5 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/utils/string_utils.dart'; class SendBookableSpacesToApiParams { List spaceUuids; @@ -14,7 +15,7 @@ class SendBookableSpacesToApiParams { required this.points, }); - static SendBookableSpacesToApiParams fromBookableSpacesModel( + static SendBookableSpacesToApiParams fromBookableSpacesModel( List bookableSpaces) { return SendBookableSpacesToApiParams( spaceUuids: bookableSpaces.map((space) => space.spaceUuid).toList(), @@ -22,8 +23,10 @@ class SendBookableSpacesToApiParams { .expand((space) => space.spaceConfig.bookableDays) .toSet() .toList(), - startTime: bookableSpaces.first.spaceConfig.bookingStartTime, - endTime: bookableSpaces.first.spaceConfig.bookingEndTime, + startTime: formatTimeOfDayTo24HourString( + bookableSpaces.first.spaceConfig.bookingStartTime), + endTime: formatTimeOfDayTo24HourString( + bookableSpaces.first.spaceConfig.bookingEndTime), points: bookableSpaces.first.spaceConfig.cost, ); } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart index 24ac31d7..8c33e54d 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart @@ -1,5 +1,6 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; @@ -20,6 +21,12 @@ class NonBookableSpacesBloc on(_onRemoveFromBookableSpaceEvent); on(_onSendBookableSpacesToApi); } + + TimeOfDay get endTime => + selectedBookableSpaces.first.spaceConfig.bookingEndTime; + + TimeOfDay get startTime => + selectedBookableSpaces.first.spaceConfig.bookingStartTime; Future _onLoadUnBookableSpacesEvent(LoadUnBookableSpacesEvent event, Emitter emit) async { emit(NonBookableSpacesLoading()); @@ -80,7 +87,8 @@ class NonBookableSpacesBloc try { await nonBookableSpacesService.sendBookableSpacesToApi( SendBookableSpacesToApiParams.fromBookableSpacesModel( - selectedBookableSpaces), + selectedBookableSpaces, + ), ); } catch (e) { emit( From b128618bfd53334b9d422278bf28ad2820a1ca3d Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Wed, 9 Jul 2025 15:11:27 +0300 Subject: [PATCH 12/71] clean the code for save and next buttons and enhance UI --- .../view/widgets/icon_text_button.dart | 9 +- .../data/remote_bookable_spaces_service.dart | 10 +- .../data/remote_non_bookable_spaces.dart | 11 +- .../domain/models/bookable_space_config.dart | 17 +- .../domain/models/bookable_space_model.dart | 16 +- .../send_bookable_spaces_to_api_params.dart | 8 +- .../non_bookaable_spaces_bloc.dart | 73 +++-- .../non_bookaable_spaces_event.dart | 2 + .../non_bookaable_spaces_state.dart | 15 +- .../manage_bookable_spaces_screen.dart | 295 +++++++++++++++--- .../screens/setup_bookable_spaces_dialog.dart | 103 ++---- .../buttons_divider_bottom_dialog_widget.dart | 69 ++-- .../widgets/column_title_widget.dart | 7 +- .../widgets/details_steps_widget.dart | 33 ++ .../widgets/next_first_step_button.dart | 28 ++ .../widgets/save_second_step_button.dart | 40 +++ .../search_unbookable_spaces_widget.dart | 1 + .../widgets/space_step_part_widget.dart | 293 +++++++++++------ .../widgets/step_two_details_widget.dart | 31 +- .../widgets/stepper_part_widget.dart | 20 +- .../widgets/time_picker_widget.dart | 7 +- .../widgets/week_checkbox_title_widget.dart | 6 +- lib/utils/constants/api_const.dart | 2 +- 23 files changed, 793 insertions(+), 303 deletions(-) create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart diff --git a/lib/pages/access_management/booking_system/view/widgets/icon_text_button.dart b/lib/pages/access_management/booking_system/view/widgets/icon_text_button.dart index afccafdb..3713b7f4 100644 --- a/lib/pages/access_management/booking_system/view/widgets/icon_text_button.dart +++ b/lib/pages/access_management/booking_system/view/widgets/icon_text_button.dart @@ -12,10 +12,13 @@ class SvgTextButton extends StatelessWidget { final double borderRadius; final List boxShadow; final double svgSize; - + final double? fontSize; + final FontWeight? fontWeight; const SvgTextButton({ super.key, required this.svgAsset, + this.fontSize, + this.fontWeight, required this.label, required this.onPressed, this.backgroundColor = ColorsManager.circleRolesBackground, @@ -60,8 +63,8 @@ class SvgTextButton extends StatelessWidget { label, style: TextStyle( color: labelColor, - fontSize: 16, - fontWeight: FontWeight.w500, + fontSize: fontSize ?? 16, + fontWeight: fontWeight ?? FontWeight.w500, ), ), ], diff --git a/lib/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart b/lib/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart index 49563e45..44656482 100644 --- a/lib/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart +++ b/lib/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart @@ -10,15 +10,17 @@ import 'package:syncrow_web/utils/constants/api_const.dart'; class RemoteBookableSpacesService implements BookableSpacesService { final HTTPService _httpService; RemoteBookableSpacesService(this._httpService); - static const _defaultErrorMessage = 'Failed to load tags'; + static const _defaultErrorMessage = 'Failed to load Bookable Spaces'; @override Future> load( BookableSpacesParams param) async { try { final response = await _httpService.get( - //TODO: you have to Chage this API call Path - path: ApiEndpoints.listTags, - //*************|********** */ + path: ApiEndpoints.bookableSpaces, + queryParameters: { + 'configured': true, + 'page': param.currentPage, + }, expectedResponseModel: (json) { final result = json as Map; return PaginatedDataModel.fromJson( diff --git a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart index 5224eb2b..cd06dd7f 100644 --- a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart +++ b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart @@ -18,9 +18,12 @@ class RemoteNonBookableSpaces implements NonBookableSpacesService { NonBookableSpacesParams params) async { try { final response = await _httpService.get( - //TODO: you have to Chage this API call Path - path: ApiEndpoints.listTags, - //*************|********** */ + path: ApiEndpoints.bookableSpaces, + queryParameters: { + 'configured': false, + 'page': params.currentPage, + 'search': params.searchedWords, + }, expectedResponseModel: (json) { final result = json as Map; return PaginatedDataModel.fromJson( @@ -50,7 +53,7 @@ class RemoteNonBookableSpaces implements NonBookableSpacesService { SendBookableSpacesToApiParams params) async { try { await _httpService.post( - path: ApiEndpoints.addBookableSpaces, + path: ApiEndpoints.bookableSpaces, body: params.toJson(), expectedResponseModel: (p0) {}, ); diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart index 958822bd..07008a45 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart @@ -3,30 +3,28 @@ import 'package:flutter/material.dart'; class BookableSpaceConfig { String configUuid; List bookableDays; - TimeOfDay bookingStartTime; - TimeOfDay bookingEndTime; + TimeOfDay? bookingStartTime; + TimeOfDay? bookingEndTime; int cost; bool availability; BookableSpaceConfig({ required this.configUuid, required this.availability, required this.bookableDays, - required this.bookingEndTime, - required this.bookingStartTime, + this.bookingEndTime, + this.bookingStartTime, required this.cost, }); factory BookableSpaceConfig.zero() => BookableSpaceConfig( configUuid: '', bookableDays: [], availability: false, - bookingEndTime: TimeOfDay.now(), - bookingStartTime: TimeOfDay.now(), cost: -1, ); factory BookableSpaceConfig.fromJson(Map json) => BookableSpaceConfig( configUuid: json['uuid'] as String, - bookableDays: json['daysAvailable'] as List, + bookableDays: (json['daysAvailable'] as List).cast(), availability: (json['active'] as bool?) ?? false, bookingEndTime: parseTimeOfDay(json['startTime'] as String), bookingStartTime: parseTimeOfDay(json['endTime'] as String), @@ -41,5 +39,8 @@ class BookableSpaceConfig { } bool get isValid => - configUuid.isNotEmpty && bookableDays.isNotEmpty && cost > 0; + bookableDays.isNotEmpty && + cost > 0 && + bookingStartTime != null && + bookingEndTime != null; } diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart index f108ab08..2c4be010 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart @@ -3,28 +3,29 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domai class BookableSpacemodel { String spaceUuid; String spaceName; - BookableSpaceConfig spaceConfig; + BookableSpaceConfig? spaceConfig; String spaceVirtualAddress; BookableSpacemodel({ required this.spaceUuid, required this.spaceName, - required this.spaceConfig, + this.spaceConfig, required this.spaceVirtualAddress, }); factory BookableSpacemodel.zero() => BookableSpacemodel( spaceUuid: '', spaceName: '', - spaceConfig: BookableSpaceConfig.zero(), spaceVirtualAddress: '', ); factory BookableSpacemodel.fromJson(Map json) => BookableSpacemodel( spaceUuid: json['uuid'] as String, spaceName: json['spaceName'] as String, - spaceConfig: BookableSpaceConfig.fromJson( - json['bookableConfig'] as Map), - spaceVirtualAddress: json['spaceVirtualAddress'] as String, + spaceConfig: json['bookableConfig'] == null + ? BookableSpaceConfig.zero() + : BookableSpaceConfig.fromJson( + json['bookableConfig'] as Map), + spaceVirtualAddress: json['virtualLocation'] as String, ); static List fromJsonList(List jsonList) => @@ -38,5 +39,6 @@ class BookableSpacemodel { spaceUuid.isNotEmpty && spaceName.isNotEmpty && spaceVirtualAddress.isNotEmpty && - spaceConfig.isValid; + spaceConfig != null && + spaceConfig!.isValid; } diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart b/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart index 73e590eb..38f099fb 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart @@ -20,14 +20,14 @@ class SendBookableSpacesToApiParams { return SendBookableSpacesToApiParams( spaceUuids: bookableSpaces.map((space) => space.spaceUuid).toList(), daysAvailable: bookableSpaces - .expand((space) => space.spaceConfig.bookableDays) + .expand((space) => space.spaceConfig!.bookableDays) .toSet() .toList(), startTime: formatTimeOfDayTo24HourString( - bookableSpaces.first.spaceConfig.bookingStartTime), + bookableSpaces.first.spaceConfig!.bookingStartTime!), endTime: formatTimeOfDayTo24HourString( - bookableSpaces.first.spaceConfig.bookingEndTime), - points: bookableSpaces.first.spaceConfig.cost, + bookableSpaces.first.spaceConfig!.bookingEndTime!), + points: bookableSpaces.first.spaceConfig!.cost, ); } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart index 8c33e54d..9d8437fe 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart @@ -20,27 +20,49 @@ class NonBookableSpacesBloc on(_onAddToBookableSpaceEvent); on(_onRemoveFromBookableSpaceEvent); on(_onSendBookableSpacesToApi); + on(_onCheckConfigurValidityEvent); } - TimeOfDay get endTime => - selectedBookableSpaces.first.spaceConfig.bookingEndTime; + TimeOfDay? get endTime => + selectedBookableSpaces.first.spaceConfig!.bookingEndTime; - TimeOfDay get startTime => - selectedBookableSpaces.first.spaceConfig.bookingStartTime; + TimeOfDay? get startTime => + selectedBookableSpaces.first.spaceConfig!.bookingStartTime; Future _onLoadUnBookableSpacesEvent(LoadUnBookableSpacesEvent event, Emitter emit) async { - emit(NonBookableSpacesLoading()); - try { - final nonBookableSpacesList = await nonBookableSpacesService.load( - event.nonBookableSpacesParams, - ); - emit( - NonBookableSpacesLoaded(nonBookableSpaces: nonBookableSpacesList), - ); - } catch (e) { - emit( - NonBookableSpacesError(e.toString()), - ); + if (state is NonBookableSpacesLoaded) { + final currState = state as NonBookableSpacesLoaded; + try { + emit(NonBookableSpacesLoading( + lastNonBookableSpaces: currState.nonBookableSpaces)); + + final nonBookableSpacesList = await nonBookableSpacesService.load( + event.nonBookableSpacesParams, + ); + nonBookableSpacesList.data.addAll(currState.nonBookableSpaces.data); + + emit( + NonBookableSpacesLoaded(nonBookableSpaces: nonBookableSpacesList), + ); + } catch (e) { + emit( + NonBookableSpacesError(e.toString()), + ); + } + } else { + try { + emit(const NonBookableSpacesLoading()); + final nonBookableSpacesList = await nonBookableSpacesService.load( + event.nonBookableSpacesParams, + ); + emit( + NonBookableSpacesLoaded(nonBookableSpaces: nonBookableSpacesList), + ); + } catch (e) { + emit( + NonBookableSpacesError(e.toString()), + ); + } } } @@ -50,7 +72,7 @@ class NonBookableSpacesBloc ) { if (state is NonBookableSpacesLoaded) { final currentState = state as NonBookableSpacesLoaded; - + emit(AddNonBookableSpaceIntoBookableState()); final updatedSelectedSpaces = List.from(currentState.selectedBookableSpaces) ..add(event.nonBookableSpace); @@ -70,7 +92,10 @@ class NonBookableSpacesBloc Emitter emit) { if (state is NonBookableSpacesLoaded) { final currentState = state as NonBookableSpacesLoaded; - currentState.selectedBookableSpaces.remove(event.bookableSpace); + emit(RemoveBookableSpaceIntoNonBookableState()); + if (currentState.selectedBookableSpaces.isNotEmpty) { + currentState.selectedBookableSpaces.remove(event.bookableSpace); + } selectedBookableSpaces.remove(event.bookableSpace); emit( NonBookableSpacesLoaded( @@ -83,17 +108,27 @@ class NonBookableSpacesBloc Future _onSendBookableSpacesToApi(SendBookableSpacesToApi event, Emitter emit) async { - emit(NonBookableSpacesLoading()); + emit(const NonBookableSpacesLoading()); try { await nonBookableSpacesService.sendBookableSpacesToApi( SendBookableSpacesToApiParams.fromBookableSpacesModel( selectedBookableSpaces, ), ); + emit(NonBookableSpacesInitial()); } catch (e) { emit( NonBookableSpacesError(e.toString()), ); } } + + void _onCheckConfigurValidityEvent( + CheckConfigurValidityEvent event, Emitter emit) { + if (selectedBookableSpaces.first.spaceConfig!.isValid) { + emit(ValidSaveButtonState()); + } else { + emit(UnValidSaveButtonState()); + } + } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart index 392cf4d4..4e204c55 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart @@ -29,3 +29,5 @@ class RemoveFromBookableSpaceEvent extends NonBookableSpacesEvent { } class SendBookableSpacesToApi extends NonBookableSpacesEvent {} + +class CheckConfigurValidityEvent extends NonBookableSpacesEvent {} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_state.dart index 850b114d..b8d7a518 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_state.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_state.dart @@ -9,7 +9,12 @@ sealed class NonBookableSpacesState extends Equatable { final class NonBookableSpacesInitial extends NonBookableSpacesState {} -class NonBookableSpacesLoading extends NonBookableSpacesState {} +class NonBookableSpacesLoading extends NonBookableSpacesState { + final PaginatedDataModel? lastNonBookableSpaces; + const NonBookableSpacesLoading({ + this.lastNonBookableSpaces, + }); +} class NonBookableSpacesLoaded extends NonBookableSpacesState { final PaginatedDataModel nonBookableSpaces; @@ -24,3 +29,11 @@ class NonBookableSpacesError extends NonBookableSpacesState { final String error; const NonBookableSpacesError(this.error); } + +class AddNonBookableSpaceIntoBookableState extends NonBookableSpacesState {} + +class RemoveBookableSpaceIntoNonBookableState extends NonBookableSpacesState {} + +class ValidSaveButtonState extends NonBookableSpacesState {} + +class UnValidSaveButtonState extends NonBookableSpacesState {} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart index dad161f1..9ed9cd2d 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart @@ -3,13 +3,15 @@ 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/access_management/booking_system/view/widgets/icon_text_button.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_data_table.dart'; import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/theme/responsive_text_theme.dart'; @@ -64,8 +66,7 @@ class _ManageBookableSpacesPageState extends State { rightBody: const NavigateHomeGridView(), scaffoldBody: BlocProvider( create: (context) => BookableSpacesBloc( - DummyBookableSpacesService(), - // RemoteBookableSpacesService(HTTPService()), + RemoteBookableSpacesService(HTTPService()), )..add(LoadBookableSpacesEvent( BookableSpacesParams(currentPage: 1), )), @@ -90,34 +91,66 @@ class ManageBookableSpacesWidget extends StatelessWidget { child: Column( children: [ Expanded( - flex: 1, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SvgTextButton( - svgAsset: Assets.backButtonIcon, - label: 'Booking Home', + flex: 10, + child: Padding( + padding: const EdgeInsetsGeometry.symmetric(vertical: 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SvgTextButton( + svgSize: 15, + fontSize: 10, + fontWeight: FontWeight.bold, + svgAsset: Assets.backButtonIcon, + label: 'Booking Home', + onPressed: () { + context.pop(); + }), + SvgTextButton( + svgSize: 15, + fontSize: 10, + fontWeight: FontWeight.bold, + svgAsset: Assets.addButtonIcon, + label: 'Set Up a Bookable Spaces', onPressed: () { - context.pop(); - }), - SvgTextButton( - svgAsset: Assets.backButtonIcon, - label: 'Set Up a Bookable Spaces', - onPressed: () async => showDialog( - context: context, - builder: (context) => SetupBookableSpacesDialog(), - ), - ) - ], + final bloc = context.read(); + showDialog( + context: context, + builder: (context) => BlocProvider.value( + value: bloc, + child: SetupBookableSpacesDialog(), + ), + ); + }, + ) + ], + ), )), + const SizedBox( + height: 10, + ), Expanded( - flex: 9, + flex: 85, child: BlocBuilder( builder: (context, state) { if (state is BookableSpacesLoading) { - return const CircularProgressIndicator(); + return const Center(child: CircularProgressIndicator()); } else if (state is BookableSpacesError) { - return Text(state.error); + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(state.error), + const SizedBox( + height: 5, + ), + ElevatedButton( + onPressed: () => context + .read() + .add(LoadBookableSpacesEvent( + BookableSpacesParams(currentPage: 1), + )), + child: const Text('try Again')) + ]); } else if (state is BookableSpacesLoaded) { return CustomDataTable( items: state.bookableSpacesList.data, @@ -125,19 +158,26 @@ class ManageBookableSpacesWidget extends StatelessWidget { DataCell( Padding( padding: const EdgeInsetsGeometry.only(left: 10), - child: Text(space.spaceName)), + child: Text( + space.spaceName, + style: const TextStyle(fontSize: 11), + )), ), DataCell(Padding( padding: const EdgeInsetsGeometry.only(left: 10), - child: Text(space.spaceVirtualAddress))), - DataCell(SizedBox( + child: Text( + space.spaceVirtualAddress, + style: const TextStyle(fontSize: 11), + ))), + DataCell(Container( + padding: const EdgeInsetsGeometry.only(left: 10), width: 200, child: Wrap( spacing: 4, - children: space.spaceConfig.bookableDays + children: space.spaceConfig!.bookableDays .map((day) => Text( day, - style: const TextStyle(fontSize: 12), + style: const TextStyle(fontSize: 11), )) .toList(), ), @@ -146,7 +186,9 @@ class ManageBookableSpacesWidget extends StatelessWidget { Padding( padding: const EdgeInsetsGeometry.only(left: 10), child: Text( - space.spaceConfig.bookingStartTime.format(context), + space.spaceConfig!.bookingStartTime! + .format(context), + style: const TextStyle(fontSize: 11), ), ), ), @@ -154,18 +196,31 @@ class ManageBookableSpacesWidget extends StatelessWidget { Padding( padding: const EdgeInsetsGeometry.only(left: 10), child: Text( - space.spaceConfig.bookingEndTime.format(context), + space.spaceConfig!.bookingEndTime!.format(context), + style: const TextStyle(fontSize: 11), ), ), ), DataCell(Padding( padding: const EdgeInsetsGeometry.only(left: 10), - child: Text('${space.spaceConfig.cost} Points'))), + child: Text( + '${space.spaceConfig!.cost} Points', + style: const TextStyle(fontSize: 11), + ))), DataCell(Center( child: Transform.scale( scale: 0.7, child: Switch( - value: space.spaceConfig.availability, + value: space.spaceConfig!.availability, + trackColor: WidgetStateProperty.resolveWith( + (Set states) { + return ColorsManager.blue1; + }), + inactiveTrackColor: ColorsManager.lightGrayColor, + thumbColor: WidgetStateProperty.resolveWith( + (Set states) { + return ColorsManager.whiteColors; + }), onChanged: (value) {}, ), ), @@ -173,13 +228,22 @@ class ManageBookableSpacesWidget extends StatelessWidget { DataCell(Center( child: ElevatedButton( onPressed: () {}, - child: SvgPicture.asset(Assets.settings), + style: ElevatedButton.styleFrom( + padding: EdgeInsets.zero, + fixedSize: const Size(50, 30), + elevation: 1, + ), + child: SvgPicture.asset( + Assets.settings, + height: 15, + color: ColorsManager.blue1, + ), ), )), ], columnsTitles: const [ - 'space', - 'space Virtual Address', + 'Space', + 'Space Virtual Address', 'Bookable Days', 'Booking Start Time', 'Booking End Time', @@ -193,9 +257,166 @@ class ManageBookableSpacesWidget extends StatelessWidget { } }, ), - ) + ), + const SizedBox( + height: 5, + ), + Expanded( + flex: 5, + child: BlocBuilder( + builder: (context, state) { + if (state is BookableSpacesLoaded) { + final totalPages = state.bookableSpacesList.totalPages; + final currentPage = state.bookableSpacesList.page; + + List paginationItems = []; + + // « Two pages back + if (currentPage > 2) { + paginationItems.add( + _buildArrowButton( + label: '«', + onTap: () { + context.read().add( + LoadBookableSpacesEvent( + BookableSpacesParams( + currentPage: currentPage - 2), + ), + ); + }, + ), + ); + } + + // < One page back + if (currentPage > 1) { + paginationItems.add( + _buildArrowButton( + label: '<', + onTap: () { + context.read().add( + LoadBookableSpacesEvent( + BookableSpacesParams( + currentPage: currentPage - 1), + ), + ); + }, + ), + ); + } + + // Page numbers + for (int i = 1; i <= totalPages; i++) { + paginationItems.add( + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: GestureDetector( + onTap: () { + if (i != currentPage) { + context.read().add( + LoadBookableSpacesEvent( + BookableSpacesParams(currentPage: i), + ), + ); + } + }, + child: Container( + width: 30, + height: 30, + alignment: Alignment.center, + decoration: BoxDecoration( + color: i == currentPage + ? ColorsManager.dialogBlueTitle + : Colors.grey[300], + borderRadius: BorderRadius.circular(8), + ), + child: Text( + '$i', + style: TextStyle( + color: i == currentPage + ? Colors.white + : Colors.black, + fontWeight: i == currentPage + ? FontWeight.bold + : FontWeight.normal, + ), + ), + ), + ), + ), + ); + } + + // > One page forward + if (currentPage < totalPages) { + paginationItems.add( + _buildArrowButton( + label: '>', + onTap: () { + context.read().add( + LoadBookableSpacesEvent( + BookableSpacesParams( + currentPage: currentPage + 1), + ), + ); + }, + ), + ); + } + + // » Two pages forward + if (currentPage + 1 < totalPages) { + paginationItems.add( + _buildArrowButton( + label: '»', + onTap: () { + context.read().add( + LoadBookableSpacesEvent( + BookableSpacesParams( + currentPage: currentPage + 2), + ), + ); + }, + ), + ); + } + + return Row( + mainAxisAlignment: MainAxisAlignment.end, + children: paginationItems, + ); + } else { + return const SizedBox.shrink(); + } + }, + ), + ), ], ), ); } + + Widget _buildArrowButton( + {required String label, required VoidCallback onTap}) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(8), + ), + child: Text( + label, + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ); + } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart index a4f32f63..b2d48b23 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart @@ -1,14 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:go_router/go_router.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class SetupBookableSpacesDialog extends StatelessWidget { @@ -23,7 +23,7 @@ class SetupBookableSpacesDialog extends StatelessWidget { ), BlocProvider( create: (context) => NonBookableSpacesBloc( - DummyNonNookableSpaces(), + RemoteNonBookableSpaces(HTTPService()), )..add( LoadUnBookableSpacesEvent( nonBookableSpacesParams: @@ -33,13 +33,16 @@ class SetupBookableSpacesDialog extends StatelessWidget { ), ], child: AlertDialog( + backgroundColor: ColorsManager.whiteColors, contentPadding: EdgeInsets.zero, - title: Text( - 'Set Up a Bookable Spaces', - style: TextStyle( - fontWeight: FontWeight.w700, - color: ColorsManager.dialogBlueTitle, - fontSize: 15, + title: Center( + child: Text( + 'Set Up a Bookable Spaces', + style: TextStyle( + fontWeight: FontWeight.w700, + color: ColorsManager.dialogBlueTitle, + fontSize: 15, + ), ), ), content: Column( @@ -69,46 +72,14 @@ class SetupBookableSpacesDialog extends StatelessWidget { ], ), Builder(builder: (context) { - return ButtonsDividerBottomDialogWidget( - onNextPressed: () { - final stepsState = context.read().state; - final selectedSpaces = context - .read() - .selectedBookableSpaces; - if (stepsState is StepOneState) { - if (selectedSpaces.isNotEmpty) { - context.read().goToNextStep(); - } else { - ScaffoldMessenger.of(context).clearSnackBars(); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Please select at least one space.'), - ), - ); - } - } else if (stepsState is StepTwoState) { - selectedSpaces.forEach( - (e) => e.spaceConfig.cost = int.parse( - pointsController.text.isEmpty - ? '0' - : pointsController.text), - ); - if (selectedSpaces.any( - (element) => !element.isValid, - )) { - ScaffoldMessenger.of(context).clearSnackBars(); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Please fill the required fields.'), - ), - ); - } else { - print(selectedSpaces.first.spaceUuid); - } - } - }, - onCancelPressed: () => context.pop(), - ); + final stepsState = context.watch().state; + final nonBookableBloc = context.watch(); + final selectedSpaces = nonBookableBloc.selectedBookableSpaces; + return stepsState is StepOneState + ? NextFirstStepButton(selectedSpaces: selectedSpaces) + : SaveSecondStepButton( + selectedSpaces: selectedSpaces, + pointsController: pointsController); }), ], ), @@ -116,31 +87,3 @@ class SetupBookableSpacesDialog extends StatelessWidget { ); } } - -class DetailsStepsWidget extends StatelessWidget { - final TextEditingController pointsController; - const DetailsStepsWidget({ - super.key, - required this.pointsController, - }); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20), - child: BlocBuilder( - builder: (context, state) { - if (state is StepOneState) { - return SpacesStepDetailsWidget(); - } else if (state is StepTwoState) { - return StepTwoDetailsWidget( - pointsController: pointsController, - ); - } else { - return const SizedBox(); - } - }, - ), - ); - } -} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart index e177b9b1..581ab89f 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart @@ -1,13 +1,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class ButtonsDividerBottomDialogWidget extends StatelessWidget { - final void Function() onNextPressed; + final String title; + final void Function()? onNextPressed; final void Function() onCancelPressed; const ButtonsDividerBottomDialogWidget({ super.key, + required this.title, required this.onNextPressed, required this.onCancelPressed, }); @@ -43,38 +49,51 @@ class ButtonsDividerBottomDialogWidget extends StatelessWidget { ), child: const Text( 'Cancel', - style: TextStyle(color: ColorsManager.grayBorder), + style: TextStyle(color: ColorsManager.blackColor), ), ), ), ), Expanded( - child: BlocBuilder( - builder: (context, state) { - return InkWell( - borderRadius: const BorderRadius.only( - bottomRight: Radius.circular(26), - ), - onTap: onNextPressed, - child: Container( - height: 40, - alignment: Alignment.center, - decoration: const BoxDecoration( - border: Border( - right: BorderSide( - color: ColorsManager.grayBorder, + child: + BlocConsumer( + listener: (context, nonBookableState) { + if (nonBookableState is NonBookableSpacesInitial) { + context.pop(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Spaces Added Successfully', + style: TextStyle(color: ColorsManager.activeGreen), + ), + duration: Duration(seconds: 2), + behavior: SnackBarBehavior.floating, + ), + ); + context.read().add( + LoadBookableSpacesEvent( + BookableSpacesParams(currentPage: 1), ), + ); + } else if (nonBookableState is NonBookableSpacesError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + nonBookableState.error, + style: + const TextStyle(color: ColorsManager.activeGreen), ), - borderRadius: BorderRadius.only( - bottomRight: Radius.circular(26), - ), - ), - child: Text( - state is StepOneState ? 'Next' : 'Save', - style: const TextStyle( - color: ColorsManager.blueColor, - ), + duration: const Duration(seconds: 2), + behavior: SnackBarBehavior.floating, ), + ); + } + }, + builder: (context, nonBookableState) { + return TextButton( + onPressed: onNextPressed, + child: Text( + title, ), ); }, diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/column_title_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/column_title_widget.dart index 4ff11ec6..5525d6a4 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/column_title_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/column_title_widget.dart @@ -31,7 +31,10 @@ class ColumnTitleWidget extends StatelessWidget { ), child: Text( title, - style: const TextStyle(color: ColorsManager.grayColor), + style: const TextStyle( + color: ColorsManager.grayColor, + fontSize: 12, + ), )); } -} \ No newline at end of file +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart new file mode 100644 index 00000000..504c25e0 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart'; + +class DetailsStepsWidget extends StatelessWidget { + final TextEditingController pointsController; + const DetailsStepsWidget({ + super.key, + required this.pointsController, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20), + child: BlocBuilder( + builder: (context, state) { + if (state is StepOneState) { + return const SpacesStepDetailsWidget(); + } else if (state is StepTwoState) { + return StepTwoDetailsWidget( + pointsController: pointsController, + ); + } else { + return const SizedBox(); + } + }, + ), + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart new file mode 100644 index 00000000..a404a900 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart'; + +class NextFirstStepButton extends StatelessWidget { + final List selectedSpaces; + + const NextFirstStepButton({ + super.key, + required this.selectedSpaces, + }); + + @override + Widget build(BuildContext context) { + return ButtonsDividerBottomDialogWidget( + title: 'Next', + onNextPressed: selectedSpaces.isEmpty + ? null + : () { + context.read().goToNextStep(); + }, + onCancelPressed: () => context.pop(), + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart new file mode 100644 index 00000000..cf57e601 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart'; + +class SaveSecondStepButton extends StatelessWidget { + final List selectedSpaces; + final TextEditingController pointsController; + + const SaveSecondStepButton({ + super.key, + required this.selectedSpaces, + required this.pointsController, + }); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return ButtonsDividerBottomDialogWidget( + title: 'Save', + onNextPressed: state is UnValidSaveButtonState + ? null + : () { + if (selectedSpaces.any( + (element) => element.isValid, + )) { + context.read().add( + SendBookableSpacesToApi(), + ); + } + }, + onCancelPressed: () => context.pop(), + ); + }, + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart index be9931e5..0153624f 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart @@ -25,6 +25,7 @@ class SearchUnbookableSpacesWidget extends StatelessWidget { return Container( width: width ?? 480, height: height ?? 30, + padding: const EdgeInsets.only(top: 4), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart index d58d3689..a1296ece 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart @@ -6,114 +6,221 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domai import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -class SpacesStepDetailsWidget extends StatelessWidget { - SpacesStepDetailsWidget({ +class SpacesStepDetailsWidget extends StatefulWidget { + const SpacesStepDetailsWidget({ super.key, }); + + @override + State createState() => + _SpacesStepDetailsWidgetState(); +} + +class _SpacesStepDetailsWidgetState extends State { Timer? _debounce; + ScrollController scrollController = ScrollController(); + int currentPage = 1; + String? currentSearchTerm; + bool isLoadingMore = false; + + @override + void initState() { + super.initState(); + + scrollController.addListener(() { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 100) { + final state = context.read().state; + if (state is NonBookableSpacesLoaded && + state.nonBookableSpaces.hasNext && + !isLoadingMore) { + isLoadingMore = true; + currentPage++; + context.read().add( + LoadUnBookableSpacesEvent( + nonBookableSpacesParams: NonBookableSpacesParams( + currentPage: currentPage, + searchedWords: currentSearchTerm, + ), + ), + ); + } + } + }); + } + + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state is NonBookableSpacesLoading) { - return const Center(child: CircularProgressIndicator()); - } else if (state is NonBookableSpacesError) { - return Text(state.error); - } else if (state is NonBookableSpacesLoaded) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Select Space', + style: TextStyle( + fontWeight: FontWeight.w700, + color: ColorsManager.blackColor, + ), + ), + const SizedBox( + height: 20, + ), + Container( + width: 450, + height: 480, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: const [ + BoxShadow( + color: Color(0x40000000), + offset: Offset.zero, + blurRadius: 5, + ), + ], + ), + child: Column( children: [ - const Text( - 'Select Space', - style: TextStyle( - fontWeight: FontWeight.w700, - color: ColorsManager.blackColor, - ), - ), - const SizedBox( - height: 20, - ), Container( - width: 450, - height: 480, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(20), - boxShadow: const [ - BoxShadow( - color: Color(0x40000000), - offset: Offset(0, 4), - blurRadius: 5, - ), - ], + width: 520, + height: 70, + padding: + const EdgeInsets.symmetric(vertical: 15, horizontal: 20), + decoration: const BoxDecoration( + color: Color(0xFFF8F8F8), + borderRadius: BorderRadius.vertical( + top: Radius.circular(20), + ), ), - child: Column( - children: [ - Container( - width: 520, - height: 70, - padding: const EdgeInsets.symmetric( - vertical: 15, horizontal: 20), - decoration: const BoxDecoration( - color: Color(0xFFF8F8F8), - borderRadius: BorderRadius.vertical( - top: Radius.circular(20), - ), - ), - child: SearchUnbookableSpacesWidget( - title: 'Search', - onChanged: (p0) { - if (_debounce?.isActive ?? false) _debounce!.cancel(); - _debounce = - Timer(const Duration(milliseconds: 500), () { - context.read().add( - LoadUnBookableSpacesEvent( - nonBookableSpacesParams: - NonBookableSpacesParams( - currentPage: 1, - searchedWords: p0, - ), - ), - ); - }); - }, - ), - ), - Expanded( - child: Container( - width: 490, - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.vertical( - bottom: Radius.circular(20), - ), - ), - padding: - const EdgeInsets.only(top: 10, left: 20, bottom: 5), - child: ListView.separated( - separatorBuilder: (context, index) => const SizedBox( + child: SearchUnbookableSpacesWidget( + title: 'Search', + onChanged: (p0) { + if (_debounce?.isActive ?? false) _debounce!.cancel(); + _debounce = Timer(const Duration(milliseconds: 500), () { + currentSearchTerm = p0; + currentPage = 1; + context.read().add( + LoadUnBookableSpacesEvent( + nonBookableSpacesParams: NonBookableSpacesParams( + currentPage: currentPage, + searchedWords: currentSearchTerm, + ), + ), + ); + }); + }, + ), + ), + Expanded( + child: + BlocConsumer( + listener: (context, state) { + if (state is NonBookableSpacesLoaded) { + isLoadingMore = false; + } + }, + builder: (context, state) { + if (state is NonBookableSpacesError) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(state.error), + const SizedBox( height: 5, ), - itemCount: state.nonBookableSpaces.data.length, - itemBuilder: (context, index) => CheckBoxSpaceWidget( - nonBookableSpace: - state.nonBookableSpaces.data[index], - ), - ), - ), - ) - ], + ElevatedButton( + onPressed: () { + context.read().add( + LoadUnBookableSpacesEvent( + nonBookableSpacesParams: + NonBookableSpacesParams( + currentPage: currentPage, + searchedWords: currentSearchTerm, + ), + ), + ); + }, + child: const Text('Try Again')) + ], + ); + } else if (state is NonBookableSpacesLoading) { + if (state.lastNonBookableSpaces == null) { + return const Center( + child: CircularProgressIndicator(), + ); + } else { + return UnbookableListWidget( + scrollController: scrollController, + nonBookableSpaces: state.lastNonBookableSpaces!, + ); + } + } else if (state is NonBookableSpacesLoaded) { + return UnbookableListWidget( + scrollController: scrollController, + nonBookableSpaces: state.nonBookableSpaces, + ); + } else { + return const SizedBox(); + } + }, ), ) ], - ); - } else { - return const SizedBox(); - } - }, + ), + ) + ], + ); + } +} + +class UnbookableListWidget extends StatelessWidget { + final PaginatedDataModel nonBookableSpaces; + const UnbookableListWidget({ + super.key, + required this.scrollController, + required this.nonBookableSpaces, + }); + + final ScrollController scrollController; + + @override + Widget build(BuildContext context) { + return Container( + width: 490, + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(20), + ), + ), + padding: const EdgeInsets.only(top: 10, left: 20, bottom: 5), + child: ListView.separated( + separatorBuilder: (context, index) => const SizedBox( + height: 5, + ), + controller: scrollController, + itemCount: nonBookableSpaces.data.length, + itemBuilder: (context, index) { + if (index < nonBookableSpaces.data.length) { + return CheckBoxSpaceWidget( + nonBookableSpace: nonBookableSpaces.data[index], + ); + } else { + return const Padding( + padding: EdgeInsets.symmetric(vertical: 10), + child: Center(child: CircularProgressIndicator()), + ); + } + }, + ), ); } } @@ -159,7 +266,7 @@ class _CheckBoxSpaceWidgetState extends State { const SizedBox( width: 5, ), - Text(widget.nonBookableSpace.spaceName), + Expanded(child: Text(widget.nonBookableSpace.spaceName)), ], ); } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart index dc750ebb..4c694b66 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart @@ -35,8 +35,10 @@ class StepTwoDetailsWidget extends StatelessWidget { return; } final nonBookableBloc = context.read(); - if (isEndTimeAfterStartTime( - timePicked, nonBookableBloc.endTime)) { + + if (nonBookableBloc.endTime != null && + isEndTimeAfterStartTime( + timePicked, nonBookableBloc.endTime!)) { ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: @@ -47,7 +49,7 @@ class StepTwoDetailsWidget extends StatelessWidget { throw Exception(); } else { nonBookableBloc.selectedBookableSpaces.forEach( - (e) => e.spaceConfig.bookingStartTime = timePicked, + (e) => e.spaceConfig!.bookingStartTime = timePicked, ); } }, @@ -62,8 +64,9 @@ class StepTwoDetailsWidget extends StatelessWidget { return; } final nonBookableBloc = context.read(); - if (isEndTimeAfterStartTime( - nonBookableBloc.startTime, timePicked)) { + if (nonBookableBloc.startTime != null && + isEndTimeAfterStartTime( + nonBookableBloc.startTime!, timePicked)) { ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: @@ -74,7 +77,7 @@ class StepTwoDetailsWidget extends StatelessWidget { throw Exception(); } else { nonBookableBloc.selectedBookableSpaces.forEach( - (e) => e.spaceConfig.bookingEndTime = timePicked, + (e) => e.spaceConfig!.bookingEndTime = timePicked, ); } }, @@ -102,6 +105,22 @@ class StepTwoDetailsWidget extends StatelessWidget { ), SearchUnbookableSpacesWidget( title: 'Ex: 0', + height: 40, + onChanged: (p0) { + context + .read() + .selectedBookableSpaces + .forEach( + (e) => e.spaceConfig!.cost = int.parse( + pointsController.text.isEmpty + ? '0' + : pointsController.text, + ), + ); + context + .read() + .add(CheckConfigurValidityEvent()); + }, controller: pointsController, inputFormatters: [FilteringTextInputFormatter.digitsOnly], suffix: const SizedBox(), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart index e7e3d43d..622fdbe0 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart @@ -24,6 +24,7 @@ class StepperPartWidget extends StatelessWidget { title: 'Space', ), Container( + padding: const EdgeInsets.only(left: 3), alignment: Alignment.centerLeft, height: 50, child: const VerticalDivider( @@ -32,7 +33,8 @@ class StepperPartWidget extends StatelessWidget { const CircleTitleStepperWidget( title: 'Settings', titleColor: ColorsManager.softGray, - circleColor: ColorsManager.softGray, + circleColor: ColorsManager.whiteColors, + borderColor: ColorsManager.textGray, ) ], ); @@ -51,9 +53,11 @@ class StepperPartWidget extends StatelessWidget { size: 12, ), circleColor: ColorsManager.trueIconGreen, - radius: 3, + radius: 15, + borderColor: ColorsManager.trueIconGreen, ), Container( + padding: const EdgeInsets.only(left: 3), alignment: Alignment.centerLeft, height: 50, child: const VerticalDivider( @@ -81,12 +85,14 @@ class CircleTitleStepperWidget extends StatelessWidget { final double? radius; final Widget? cicleIcon; final Color? circleColor; + final Color? borderColor; final Color? titleColor; final String title; const CircleTitleStepperWidget({ super.key, required this.title, this.circleColor, + this.borderColor, this.cicleIcon, this.titleColor, this.radius, @@ -96,9 +102,13 @@ class CircleTitleStepperWidget extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - CircleAvatar( - minRadius: radius ?? 5, - backgroundColor: circleColor ?? ColorsManager.blue1, + Container( + width: radius ?? 15, + height: radius ?? 15, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: circleColor ?? ColorsManager.blue1, + border: Border.all(color: borderColor ?? ColorsManager.blue1)), child: cicleIcon, ), const SizedBox( diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart index c02491f3..1cb87ebd 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -40,6 +42,7 @@ class _TimePickerWidgetState extends State { ); widget.onTimePicked(tempTime); timePicked = tempTime; + context.read().add(CheckConfigurValidityEvent()); setState(() {}); }, child: Row( @@ -70,9 +73,7 @@ class _TimePickerWidgetState extends State { alignment: Alignment.centerLeft, padding: const EdgeInsets.symmetric(horizontal: 8), child: Text( - timePicked == null - ? TimeOfDay.now().format(context) - : timePicked!.format(context), + timePicked == null ? 'HH:MM' : timePicked!.format(context), style: const TextStyle(color: Color(0xB2D5D5D5)), ), ), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart index 6fc142b6..c0d3c1b8 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart @@ -42,9 +42,13 @@ class _WeekDaysCheckboxRowState extends State { for (var space in context .read() .selectedBookableSpaces) { - space.spaceConfig.bookableDays = selectedDays; + space.spaceConfig!.bookableDays = selectedDays; } }); + + context + .read() + .add(CheckConfigurValidityEvent()); }, ), ), diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 24175a37..671a930f 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -141,5 +141,5 @@ abstract class ApiEndpoints { static const String saveSchedule = '/schedule/{deviceUuid}'; ////booking System - static const String addBookableSpaces = '/bookable-spaces'; + static const String bookableSpaces = '/bookable-spaces'; } From 55a73eee7f270b5eeeddfc8f00aee2c8881bdca3 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 10 Jul 2025 15:48:13 +0300 Subject: [PATCH 13/71] we can switch pages in access managment --- .../booking_system/view/booking_page.dart | 12 +++++++----- .../access_management/view/access_management.dart | 12 +++++++++--- lib/utils/app_routes.dart | 5 ----- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/pages/access_management/booking_system/view/booking_page.dart b/lib/pages/access_management/booking_system/view/booking_page.dart index d35f258d..4b4ca49f 100644 --- a/lib/pages/access_management/booking_system/view/booking_page.dart +++ b/lib/pages/access_management/booking_system/view/booking_page.dart @@ -1,11 +1,14 @@ import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; + import 'package:syncrow_web/pages/access_management/booking_system/view/widgets/icon_text_button.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -import 'package:syncrow_web/utils/constants/routes_const.dart'; class BookingPage extends StatelessWidget { - const BookingPage({super.key}); + final PageController pageController; + const BookingPage({ + super.key, + required this.pageController, + }); @override Widget build(BuildContext context) { @@ -36,8 +39,7 @@ class BookingPage extends StatelessWidget { svgAsset: Assets.homeIcon, label: 'Manage Bookable Spaces', onPressed: () { - context - .go(RoutesConst.manageBookableSapcesPage); + pageController.jumpToPage(2); }), const SizedBox(width: 20), SvgTextButton( diff --git a/lib/pages/access_management/view/access_management.dart b/lib/pages/access_management/view/access_management.dart index e035d252..732dc9ce 100644 --- a/lib/pages/access_management/view/access_management.dart +++ b/lib/pages/access_management/view/access_management.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.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/booking_system/view/booking_page.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart'; import 'package:syncrow_web/pages/access_management/view/access_overview_content.dart'; import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; @@ -71,9 +72,14 @@ class _AccessManagementPageState extends State scaffoldBody: PageView( controller: _pageController, physics: const NeverScrollableScrollPhysics(), - children: const [ - AccessOverviewContent(), - BookingPage(), + children: [ + const AccessOverviewContent(), + BookingPage( + pageController: _pageController, + ), + ManageBookableSpacesPage( + pageController: _pageController, + ), ], ), ), diff --git a/lib/utils/app_routes.dart b/lib/utils/app_routes.dart index b93965ce..263bdbd6 100644 --- a/lib/utils/app_routes.dart +++ b/lib/utils/app_routes.dart @@ -1,5 +1,4 @@ import 'package:go_router/go_router.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart'; import 'package:syncrow_web/pages/access_management/view/access_management.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/views/analytics_page.dart'; import 'package:syncrow_web/pages/auth/view/login_page.dart'; @@ -29,10 +28,6 @@ class AppRoutes { path: RoutesConst.accessManagementPage, builder: (context, state) => const AccessManagementPage(), ), - GoRoute( - path: RoutesConst.manageBookableSapcesPage, - builder: (context, state) => const ManageBookableSpacesPage(), - ), GoRoute( path: RoutesConst.deviceManagementPage, builder: (context, state) => const DeviceManagementPage(), From b5d72b2a2a13baf5a67354a41bec2f3c474ae989 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 10 Jul 2025 15:49:30 +0300 Subject: [PATCH 14/71] refactor code --- .../update_bookable_spaces_event.dart | 15 ++ .../update_bookable_spaces_state.dart | 30 +++ .../bottom_pagination_part_widget.dart | 158 ++++++++++++++++ .../table_part_widget.dart | 177 ++++++++++++++++++ .../top_part_widget.dart | 75 ++++++++ .../widgets/points_part_widget.dart | 125 +++++++++++++ .../widgets/unbookable_list_widget.dart | 52 +++++ .../shared/models/paginated_data_model.dart | 18 ++ 8 files changed, 650 insertions(+) create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_event.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_state.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_event.dart new file mode 100644 index 00000000..1076f589 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_event.dart @@ -0,0 +1,15 @@ +part of 'update_bookable_spaces_bloc.dart'; + +sealed class UpdateBookableSpaceEvent extends Equatable { + const UpdateBookableSpaceEvent(); + + @override + List get props => []; +} + +class UpdateBookableSpace extends UpdateBookableSpaceEvent { + final UpdateBookableSpaceParam updatedParams; + const UpdateBookableSpace({ + required this.updatedParams, + }); +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_state.dart new file mode 100644 index 00000000..e173dd21 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_state.dart @@ -0,0 +1,30 @@ +part of 'update_bookable_spaces_bloc.dart'; + +sealed class UpdateBookableSpacesState extends Equatable { + const UpdateBookableSpacesState(); + + @override + List get props => []; +} + +final class UpdateBookableSpacesInitial extends UpdateBookableSpacesState {} + +final class UpdateBookableSpaceLoading extends UpdateBookableSpacesState { + final String updatingSpaceUuid; + + const UpdateBookableSpaceLoading(this.updatingSpaceUuid); +} + +final class UpdateBookableSpaceSuccess extends UpdateBookableSpacesState { + final BookableSpaceConfig bookableSpaceConfig; + const UpdateBookableSpaceSuccess({ + required this.bookableSpaceConfig, + }); +} + +final class UpdateBookableSpaceFailure extends UpdateBookableSpacesState { + final String error; + const UpdateBookableSpaceFailure({ + required this.error, + }); +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart new file mode 100644 index 00000000..247d91a6 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart @@ -0,0 +1,158 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class BottomPaginationPartWidget extends StatelessWidget { + const BottomPaginationPartWidget({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is BookableSpacesLoaded) { + final totalPages = state.bookableSpacesList.totalPages; + final currentPage = state.bookableSpacesList.page; + + List paginationItems = []; + + // « Two pages back + if (currentPage > 2) { + paginationItems.add( + _buildArrowButton( + label: '«', + onTap: () { + context.read().add( + LoadBookableSpacesEvent( + BookableSpacesParams(currentPage: currentPage - 2), + ), + ); + }, + ), + ); + } + + // < One page back + if (currentPage > 1) { + paginationItems.add( + _buildArrowButton( + label: '<', + onTap: () { + context.read().add( + LoadBookableSpacesEvent( + BookableSpacesParams(currentPage: currentPage - 1), + ), + ); + }, + ), + ); + } + + // Page numbers + for (int i = 1; i <= totalPages; i++) { + paginationItems.add( + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: GestureDetector( + onTap: () { + if (i != currentPage) { + context.read().add( + LoadBookableSpacesEvent( + BookableSpacesParams(currentPage: i), + ), + ); + } + }, + child: Container( + width: 30, + height: 30, + alignment: Alignment.center, + decoration: BoxDecoration( + color: i == currentPage + ? ColorsManager.dialogBlueTitle + : Colors.grey[300], + borderRadius: BorderRadius.circular(8), + ), + child: Text( + '$i', + style: TextStyle( + color: i == currentPage ? Colors.white : Colors.black, + fontWeight: i == currentPage + ? FontWeight.bold + : FontWeight.normal, + ), + ), + ), + ), + ), + ); + } + + // > One page forward + if (currentPage < totalPages) { + paginationItems.add( + _buildArrowButton( + label: '>', + onTap: () { + context.read().add( + LoadBookableSpacesEvent( + BookableSpacesParams(currentPage: currentPage + 1), + ), + ); + }, + ), + ); + } + + // » Two pages forward + if (currentPage + 1 < totalPages) { + paginationItems.add( + _buildArrowButton( + label: '»', + onTap: () { + context.read().add( + LoadBookableSpacesEvent( + BookableSpacesParams(currentPage: currentPage + 2), + ), + ); + }, + ), + ); + } + + return Row( + mainAxisAlignment: MainAxisAlignment.end, + children: paginationItems, + ); + } else { + return const SizedBox.shrink(); + } + }, + ); + } + + Widget _buildArrowButton( + {required String label, required VoidCallback onTap}) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(8), + ), + child: Text( + label, + style: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart new file mode 100644 index 00000000..3534f987 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_data_table.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class TablePartWidget extends StatelessWidget { + const TablePartWidget({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is BookableSpacesLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is BookableSpacesError) { + return Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + Text(state.error), + const SizedBox( + height: 5, + ), + ElevatedButton( + onPressed: () => context + .read() + .add(LoadBookableSpacesEvent( + BookableSpacesParams(currentPage: 1), + )), + child: const Text('try Again')) + ]); + } else if (state is BookableSpacesLoaded) { + return CustomDataTable( + items: state.bookableSpacesList.data, + cellsWidgets: (space) => [ + DataCell( + Padding( + padding: const EdgeInsetsGeometry.only(left: 10), + child: Text( + space.spaceName, + style: const TextStyle(fontSize: 11), + )), + ), + DataCell(Padding( + padding: const EdgeInsetsGeometry.only(left: 10), + child: Text( + space.spaceVirtualAddress, + style: const TextStyle(fontSize: 11), + ))), + DataCell(Container( + padding: const EdgeInsetsGeometry.only(left: 10), + width: 200, + child: Wrap( + spacing: 4, + children: space.spaceConfig!.bookableDays + .map((day) => Text( + day, + style: const TextStyle(fontSize: 11), + )) + .toList(), + ), + )), + DataCell( + Padding( + padding: const EdgeInsetsGeometry.only(left: 10), + child: Text( + space.spaceConfig!.bookingStartTime!.format(context), + style: const TextStyle(fontSize: 11), + ), + ), + ), + DataCell( + Padding( + padding: const EdgeInsetsGeometry.only(left: 10), + child: Text( + space.spaceConfig!.bookingEndTime!.format(context), + style: const TextStyle(fontSize: 11), + ), + ), + ), + DataCell(Padding( + padding: const EdgeInsetsGeometry.only(left: 10), + child: Text( + '${space.spaceConfig!.cost} Points', + style: const TextStyle(fontSize: 11), + ))), + DataCell(Center( + child: Transform.scale( + scale: 0.7, + child: BlocConsumer( + listener: (context, updateState) { + if (updateState is UpdateBookableSpaceSuccess) { + context.read().add( + InsertUpdatedSpaceEvent( + bookableSpaces: state.bookableSpacesList, + bookableSpace: space, + updatedBookableSpaceConfig: + updateState.bookableSpaceConfig, + ), + ); + } + }, + builder: (context, updateState) { + final isLoading = + updateState is UpdateBookableSpaceLoading && + updateState.updatingSpaceUuid == space.spaceUuid; + if (isLoading) { + return const Center(child: CircularProgressIndicator()); + } + return Switch( + trackOutlineColor: + WidgetStateProperty.resolveWith( + (Set states) { + return ColorsManager.whiteColors; + }), + value: space.spaceConfig!.availability, + activeTrackColor: ColorsManager.blueColor, + inactiveTrackColor: ColorsManager.grayBorder, + thumbColor: WidgetStateProperty.resolveWith( + (Set states) { + return ColorsManager.whiteColors; + }), + onChanged: (value) { + context.read().add( + UpdateBookableSpace( + updatedParams: UpdateBookableSpaceParam( + spaceUuid: space.spaceUuid, + availability: value, + )), + ); + }, + ); + }, + ), + ), + )), + DataCell(Center( + child: ElevatedButton( + onPressed: () {}, + style: ElevatedButton.styleFrom( + padding: EdgeInsets.zero, + fixedSize: const Size(50, 30), + elevation: 1, + ), + child: SvgPicture.asset( + Assets.settings, + height: 15, + color: ColorsManager.blue1, + ), + ), + )), + ], + columnsTitles: const [ + 'Space', + 'Space Virtual Address', + 'Bookable Days', + 'Booking Start Time', + 'Booking End Time', + 'Cost', + 'Availability', + 'Settings', + ], + ); + } else { + return const SizedBox(); + } + }, + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart new file mode 100644 index 00000000..506773dc --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/view/widgets/icon_text_button.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class TopPartWidget extends StatelessWidget { + const TopPartWidget({ + super.key, + required this.pageController, + }); + + final PageController pageController; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsetsGeometry.symmetric(vertical: 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: SvgPicture.asset( + Assets.backButtonIcon, + height: 15, + ), + onPressed: () { + pageController.jumpToPage(1); + }), + const SizedBox( + width: 10, + ), + Text( + 'Manage Bookable Spaces', + style: TextStyle( + fontSize: 18, + color: ColorsManager.vividBlue.withValues( + alpha: 0.7, + ), + fontWeight: FontWeight.w700), + ) + ], + ), + SvgTextButton( + svgSize: 15, + fontSize: 10, + fontWeight: FontWeight.bold, + svgAsset: Assets.addButtonIcon, + label: 'Set Up a Bookable Spaces', + onPressed: () { + final bloc = context.read(); + showDialog( + context: context, + builder: (context) => BlocProvider.value( + value: bloc, + child: SetupBookableSpacesDialog(), + ), + ); + }, + ) + ], + ), + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart new file mode 100644 index 00000000..06d11127 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart @@ -0,0 +1,125 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class PointsPartWidget extends StatelessWidget { + const PointsPartWidget({ + super.key, + required this.pointsController, + }); + + final TextEditingController pointsController; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + if (state is ActivatePointsSwitch) + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ) + else + const SizedBox( + width: 11, + ), + const Text('Points/hrs'), + ], + ), + Transform.scale( + scale: 0.7, + child: Switch( + trackOutlineColor: WidgetStateProperty.resolveWith( + (Set states) { + return ColorsManager.whiteColors; + }), + activeTrackColor: ColorsManager.blueColor, + inactiveTrackColor: ColorsManager.grayBorder, + thumbColor: WidgetStateProperty.resolveWith( + (Set states) { + return ColorsManager.whiteColors; + }), + value: context.watch().switchValue, + onChanged: (value) { + if (value) { + context + .read() + .activateSwitch(); + context + .read() + .selectedBookableSpaces + .forEach( + (e) => e.spaceConfig!.cost = -1, + ); + context + .read() + .add(CheckConfigurValidityEvent()); + } else { + context + .read() + .unActivateSwitch(); + pointsController.clear(); + context + .read() + .selectedBookableSpaces + .forEach( + (e) => e.spaceConfig!.cost = 0, + ); + context + .read() + .add(CheckConfigurValidityEvent()); + } + }, + ), + ) + ], + ), + const SizedBox( + height: 5, + ), + if (state is ActivatePointsSwitch) + SearchUnbookableSpacesWidget( + title: 'Ex: 0', + height: 40, + onChanged: (p0) { + context + .read() + .selectedBookableSpaces + .forEach( + (e) => e.spaceConfig!.cost = int.parse( + pointsController.text.isEmpty + ? '0' + : pointsController.text, + ), + ); + context + .read() + .add(CheckConfigurValidityEvent()); + }, + controller: pointsController, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + suffix: const SizedBox(), + ) + else + const SizedBox(), + ], + ); + }, + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart new file mode 100644 index 00000000..92bb66e3 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; + +class UnbookableListWidget extends StatelessWidget { + final PaginatedDataModel nonBookableSpaces; + const UnbookableListWidget({ + super.key, + required this.scrollController, + required this.nonBookableSpaces, + }); + + final ScrollController scrollController; + + @override + Widget build(BuildContext context) { + return Container( + width: 490, + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(20), + ), + ), + padding: const EdgeInsets.only(top: 10, left: 20, bottom: 5), + child: ListView.separated( + separatorBuilder: (context, index) => const SizedBox( + height: 5, + ), + controller: scrollController, + itemCount: nonBookableSpaces.data.length, + itemBuilder: (context, index) { + if (index < nonBookableSpaces.data.length) { + return CheckBoxSpaceWidget( + nonBookableSpace: nonBookableSpaces.data[index], + selectedSpaces: + context.read().selectedBookableSpaces, + ); + } else { + return const Padding( + padding: EdgeInsets.symmetric(vertical: 10), + child: Center(child: CircularProgressIndicator()), + ); + } + }, + ), + ); + } +} diff --git a/lib/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart b/lib/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart index ac35975d..80fcee90 100644 --- a/lib/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart +++ b/lib/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart @@ -40,4 +40,22 @@ class PaginatedDataModel extends Equatable { totalItems, totalPages, ]; + + PaginatedDataModel copyWith({ + List? data, + int? page, + int? size, + bool? hasNext, + int? totalItems, + int? totalPages, + }) { + return PaginatedDataModel( + data: data ?? this.data, + page: page ?? this.page, + size: size ?? this.size, + hasNext: hasNext ?? this.hasNext, + totalItems: totalItems ?? this.totalItems, + totalPages: totalPages ?? this.totalPages, + ); + } } From 494a0005907a8e9cc5bcf00ab1a6e4dc28108c21 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 10 Jul 2025 15:50:24 +0300 Subject: [PATCH 15/71] update bookable space logic --- .../domain/models/bookable_space_config.dart | 19 ++++++++++++++- .../domain/models/bookable_space_model.dart | 14 +++++++++++ .../params/update_bookable_space_param.dart | 24 +++++++++++++++++++ .../update_bookable_space_service.dart | 6 +++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/domain/service/update_bookable_space_service.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart index 07008a45..9c61c087 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart @@ -40,7 +40,24 @@ class BookableSpaceConfig { bool get isValid => bookableDays.isNotEmpty && - cost > 0 && + cost >= 0 && bookingStartTime != null && bookingEndTime != null; + + BookableSpaceConfig copyWith({ + List? bookableDays, + TimeOfDay? bookingStartTime, + TimeOfDay? bookingEndTime, + int? cost, + bool? availability, + }) { + return BookableSpaceConfig( + configUuid: configUuid, + availability: availability ?? this.availability, + bookableDays: bookableDays ?? this.bookableDays, + cost: cost ?? this.cost, + bookingEndTime: bookingEndTime ?? this.bookingEndTime, + bookingStartTime: bookingStartTime ?? this.bookingStartTime, + ); + } } diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart index 2c4be010..8c6bba7a 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart @@ -41,4 +41,18 @@ class BookableSpacemodel { spaceVirtualAddress.isNotEmpty && spaceConfig != null && spaceConfig!.isValid; + + BookableSpacemodel copyWith({ + String? spaceUuid, + String? spaceName, + BookableSpaceConfig? spaceConfig, + String? spaceVirtualAddress, + }) { + return BookableSpacemodel( + spaceUuid: spaceUuid ?? this.spaceUuid, + spaceName: spaceName ?? this.spaceName, + spaceConfig: spaceConfig ?? this.spaceConfig, + spaceVirtualAddress: spaceVirtualAddress ?? this.spaceVirtualAddress, + ); + } } diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart b/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart new file mode 100644 index 00000000..07c79445 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart @@ -0,0 +1,24 @@ +class UpdateBookableSpaceParam { + String spaceUuid; + + List? bookableDays; + String? bookingStartTime; + String? bookingEndTime; + int? cost; + bool? availability; + UpdateBookableSpaceParam({ + required this.spaceUuid, + this.bookingStartTime, + this.bookingEndTime, + this.bookableDays, + this.availability, + this.cost, + }); + Map toJson() => { + if (bookableDays != null) 'daysAvailable': bookableDays, + if (bookingStartTime != null) 'startTime': bookingStartTime, + if (bookingEndTime != null) 'endTime': bookingEndTime, + if (cost != null) 'points': cost, + if (availability != null) 'active': availability, + }; +} diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/service/update_bookable_space_service.dart b/lib/pages/access_management/manage_bookable_spaces/domain/service/update_bookable_space_service.dart new file mode 100644 index 00000000..509c69eb --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/domain/service/update_bookable_space_service.dart @@ -0,0 +1,6 @@ +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart'; + +abstract class UpdateBookableSpaceService { + Future update(UpdateBookableSpaceParam updateParam); +} From a1fa049a058cd15f2f30c67b148cc2fec3d59c27 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 10 Jul 2025 15:50:56 +0300 Subject: [PATCH 16/71] no need for dummy data --- .../data/dummy_bookable_spaces_service.dart | 61 ------------------- 1 file changed, 61 deletions(-) delete mode 100644 lib/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart b/lib/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart deleted file mode 100644 index a4255e2f..00000000 --- a/lib/pages/access_management/manage_bookable_spaces/data/dummy_bookable_spaces_service.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart'; -import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; - -class DummyBookableSpacesService implements BookableSpacesService { - @override - Future> load( - BookableSpacesParams param) async { - return PaginatedDataModel( - data: [ - BookableSpacemodel( - spaceName: 'space1', - spaceConfig: BookableSpaceConfig( - configUuid: 'uuid', - bookableDays: ['wed', 'saturday'], - availability: true, - bookingEndTime: const TimeOfDay(hour: 13, minute: 20), - bookingStartTime: const TimeOfDay(hour: 13, minute: 20), - cost: 6, - ), - spaceUuid: 'uuiiddd', - spaceVirtualAddress: 'idvirtual1', - ), - BookableSpacemodel( - spaceName: 'space2', - spaceConfig: BookableSpaceConfig( - configUuid: 'uuid', - bookableDays: ['wed', 'thur'], - availability: true, - bookingEndTime: const TimeOfDay(hour: 13, minute: 20), - bookingStartTime: const TimeOfDay(hour: 13, minute: 20), - cost: 6, - ), - spaceUuid: 'uuiiddd', - spaceVirtualAddress: 'idvirtual1', - ), - BookableSpacemodel( - spaceName: 'space3', - spaceConfig: BookableSpaceConfig( - configUuid: 'uuid', - bookableDays: ['wed', 'fri', 'tues'], - availability: true, - bookingEndTime: const TimeOfDay(hour: 13, minute: 20), - bookingStartTime: const TimeOfDay(hour: 13, minute: 20), - cost: 6, - ), - spaceUuid: 'uuiiddd', - spaceVirtualAddress: 'idvirtual1', - ) - ], - page: 1, - size: 1, - hasNext: false, - totalItems: 3, - totalPages: 1, - ); - } -} From df46a5b90530f700158134a610230d9d85751639 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 10 Jul 2025 15:51:17 +0300 Subject: [PATCH 17/71] update spaces remote files --- .../remote_update_bookable_space_service.dart | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 lib/pages/access_management/manage_bookable_spaces/data/remote_update_bookable_space_service.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/data/remote_update_bookable_space_service.dart b/lib/pages/access_management/manage_bookable_spaces/data/remote_update_bookable_space_service.dart new file mode 100644 index 00000000..1c0b1c8e --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/data/remote_update_bookable_space_service.dart @@ -0,0 +1,40 @@ +import 'package:dio/dio.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/update_bookable_space_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + +class RemoteUpdateBookableSpaceService implements UpdateBookableSpaceService { + final HTTPService _httpService; + RemoteUpdateBookableSpaceService(this._httpService); + static const _defaultErrorMessage = 'Failed to load Bookable Spaces'; + @override + Future update( + UpdateBookableSpaceParam updateParam) async { + try { + final response = await _httpService.put( + path: '${ApiEndpoints.bookableSpaces}/${updateParam.spaceUuid}', + body: updateParam.toJson(), + expectedResponseModel: (json) { + return BookableSpaceConfig.fromJson( + json['data'] as Map); + }, + ); + return response; + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [ + _defaultErrorMessage, + errorMessage, + ].join(': '); + throw APIException(formattedErrorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } +} From d58da9644ffac20463f66e2d05be0745311d098d Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 10 Jul 2025 15:51:44 +0300 Subject: [PATCH 18/71] add toggling for points --- .../cubit/toggle_points_switch_cubit.dart | 18 +++ .../cubit/toggle_points_switch_state.dart | 14 ++ .../update_bookable_spaces_bloc.dart | 35 +++++ .../widgets/booking_period_widget.dart | 122 ++++++++++++++++++ .../widgets/check_box_space_widget.dart | 59 +++++++++ 5 files changed, 248 insertions(+) create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_state.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart new file mode 100644 index 00000000..0b0f11f0 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart @@ -0,0 +1,18 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; + +part 'toggle_points_switch_state.dart'; + +class TogglePointsSwitchCubit extends Cubit { + TogglePointsSwitchCubit() : super(TogglePointsSwitchInitial()); + bool switchValue = true; + void activateSwitch() { + switchValue = true; + emit(ActivatePointsSwitch()); + } + + void unActivateSwitch() { + switchValue = false; + emit(UnActivatePointsSwitch()); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_state.dart new file mode 100644 index 00000000..872ae8dc --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_state.dart @@ -0,0 +1,14 @@ +part of 'toggle_points_switch_cubit.dart'; + +sealed class TogglePointsSwitchState extends Equatable { + const TogglePointsSwitchState(); + + @override + List get props => []; +} + +final class TogglePointsSwitchInitial extends TogglePointsSwitchState {} + +class ActivatePointsSwitch extends TogglePointsSwitchState {} + +class UnActivatePointsSwitch extends TogglePointsSwitchState {} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart new file mode 100644 index 00000000..ccece88e --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart @@ -0,0 +1,35 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/update_bookable_space_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; + +part 'update_bookable_spaces_event.dart'; +part 'update_bookable_spaces_state.dart'; + +class UpdateBookableSpacesBloc + extends Bloc { + final UpdateBookableSpaceService updateBookableSpaceService; + UpdateBookableSpacesBloc(this.updateBookableSpaceService) + : super(UpdateBookableSpacesInitial()) { + on(_onUpdateBookableSpace); + } + + Future _onUpdateBookableSpace(UpdateBookableSpace event, + Emitter emit) async { + emit(UpdateBookableSpaceLoading(event.updatedParams.spaceUuid)); + try { + final updatedSpace = + await updateBookableSpaceService.update(event.updatedParams); + + emit(UpdateBookableSpaceSuccess(bookableSpaceConfig: updatedSpace)); + } on APIException catch (e) { + emit(UpdateBookableSpaceFailure(error: e.message)); + } catch (e) { + emit( + UpdateBookableSpaceFailure(error: e.toString()), + ); + } + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart new file mode 100644 index 00000000..d10afee1 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/string_utils.dart'; + +class BookingPeriodWidget extends StatelessWidget { + const BookingPeriodWidget({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + const Text('Booking Period'), + ], + ), + Container( + width: 300, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: ColorsManager.graysColor, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + TimePickerWidget( + title: 'Start Time', + onTimePicked: (timePicked) { + if (timePicked == null) { + return; + } + final nonBookableBloc = + context.read(); + + if (nonBookableBloc.endTime != null && + isEndTimeAfterStartTime( + timePicked, nonBookableBloc.endTime!)) { + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context) + .showSnackBar(const SnackBar( + content: Text( + "You can't choose start Time Before End time"), + duration: Duration(seconds: 2), + backgroundColor: ColorsManager.red, + )); + throw Exception(); + } else { + nonBookableBloc.selectedBookableSpaces.forEach( + (e) => e.spaceConfig!.bookingStartTime = timePicked, + ); + } + }, + ), + const Icon( + Icons.arrow_right_alt, + color: ColorsManager.grayColor, + ), + TimePickerWidget( + title: 'End Time', + onTimePicked: (timePicked) { + if (timePicked == null) { + return; + } + final nonBookableBloc = + context.read(); + if (nonBookableBloc.startTime != null && + isEndTimeAfterStartTime( + nonBookableBloc.startTime!, timePicked)) { + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context) + .showSnackBar(const SnackBar( + content: Text( + "You can't choose End Time After Start time"), + duration: Duration(seconds: 2), + backgroundColor: ColorsManager.red, + )); + throw Exception(); + } else { + nonBookableBloc.selectedBookableSpaces.forEach( + (e) => e.spaceConfig!.bookingEndTime = timePicked, + ); + } + }, + ), + Container( + width: 50, + height: 32, + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + bottomLeft: Radius.circular(10), + ), + ), + alignment: Alignment.center, + child: SvgPicture.asset( + Assets.clockIcon, + height: 15, + color: ColorsManager.blackColor.withValues(alpha: 0.4), + ), + ) + ], + ), + ), + ], + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart new file mode 100644 index 00000000..937ee35e --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; + +class CheckBoxSpaceWidget extends StatefulWidget { + final BookableSpacemodel nonBookableSpace; + final List selectedSpaces; + const CheckBoxSpaceWidget({ + super.key, + required this.nonBookableSpace, + required this.selectedSpaces, + }); + + @override + State createState() => _CheckBoxSpaceWidgetState(); +} + +class _CheckBoxSpaceWidgetState extends State { + bool isChecked = false; + @override + void initState() { + isChecked = widget.selectedSpaces.any( + (element) => element.spaceUuid == widget.nonBookableSpace.spaceUuid, + ); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Checkbox( + value: isChecked, + onChanged: (value) => setState(() { + isChecked = value ?? false; + if (isChecked) { + context.read().add( + AddToBookableSpaceEvent( + nonBookableSpace: widget.nonBookableSpace, + ), + ); + } else { + context.read().add( + RemoveFromBookableSpaceEvent( + bookableSpace: widget.nonBookableSpace, + ), + ); + } + }), + ), + const SizedBox( + width: 5, + ), + Expanded(child: Text(widget.nonBookableSpace.spaceName)), + ], + ); + } +} From aab2b4a52a995d2ee46eef79e4827c13b0e5b3b0 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 10 Jul 2025 15:52:15 +0300 Subject: [PATCH 19/71] insert after update and refactor --- .../bookable_spaces_bloc.dart | 29 ++ .../bookable_spaces_event.dart | 11 + .../bookable_spaces_state.dart | 2 + .../manage_bookable_spaces_screen.dart | 411 ++---------------- .../widgets/next_first_step_button.dart | 4 + 5 files changed, 84 insertions(+), 373 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart index 14288a25..009db2b9 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart @@ -1,5 +1,6 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart'; @@ -15,6 +16,7 @@ class BookableSpacesBloc BookableSpacesBloc(this.bookableSpacesService) : super(BookableSpacesInitial()) { on(_onLoadBookableSpaces); + on(_onInsertUpdatedSpaceEven); } Future _onLoadBookableSpaces( @@ -31,4 +33,31 @@ class BookableSpacesBloc ); } } + + void _onInsertUpdatedSpaceEven( + InsertUpdatedSpaceEvent event, Emitter emit) { + emit(InsertingUpdatedSpaceState()); + + if (event.bookableSpace.spaceConfig!.configUuid == + event.updatedBookableSpaceConfig.configUuid) { + final editedBookableSpace = event.bookableSpaces.data.firstWhere( + (element) => element.spaceUuid == event.bookableSpace.spaceUuid, + ); + final config = editedBookableSpace.spaceConfig!.copyWith( + availability: event.updatedBookableSpaceConfig.availability, + bookableDays: event.updatedBookableSpaceConfig.bookableDays, + bookingEndTime: event.updatedBookableSpaceConfig.bookingEndTime, + bookingStartTime: event.updatedBookableSpaceConfig.bookingStartTime, + cost: event.updatedBookableSpaceConfig.cost, + ); + editedBookableSpace.spaceConfig = config; + final index = event.bookableSpaces.data.indexWhere( + (element) => element.spaceUuid == event.bookableSpace.spaceUuid, + ); + event.bookableSpaces.data.removeAt(index); + event.bookableSpaces.data.insert(index, editedBookableSpace); + } + + emit(BookableSpacesLoaded(bookableSpacesList: event.bookableSpaces)); + } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart index 47a1b396..46cfe908 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart @@ -11,3 +11,14 @@ class LoadBookableSpacesEvent extends BookableSpacesEvent { final BookableSpacesParams params; const LoadBookableSpacesEvent(this.params); } + +class InsertUpdatedSpaceEvent extends BookableSpacesEvent { + final PaginatedDataModel bookableSpaces; + final BookableSpacemodel bookableSpace; + final BookableSpaceConfig updatedBookableSpaceConfig; + const InsertUpdatedSpaceEvent({ + required this.bookableSpaces, + required this.bookableSpace, + required this.updatedBookableSpaceConfig, + }); +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_state.dart index d722ddef..d22f585a 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_state.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_state.dart @@ -24,3 +24,5 @@ final class BookableSpacesError extends BookableSpacesState { required this.error, }); } + +class InsertingUpdatedSpaceState extends BookableSpacesState {} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart index 9ed9cd2d..1c2e98f6 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart @@ -1,24 +1,22 @@ 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/access_management/booking_system/view/widgets/icon_text_button.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_update_bookable_space_service.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_data_table.dart'; -import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart'; + import 'package:syncrow_web/services/api/http_service.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/constants/assets.dart'; -import 'package:syncrow_web/utils/extension/build_context_x.dart'; -import 'package:syncrow_web/utils/theme/responsive_text_theme.dart'; -import 'package:syncrow_web/web_layout/web_scaffold.dart'; class ManageBookableSpacesPage extends StatefulWidget { - const ManageBookableSpacesPage({super.key}); + final PageController pageController; + const ManageBookableSpacesPage({ + super.key, + required this.pageController, + }); @override State createState() => @@ -26,63 +24,39 @@ class ManageBookableSpacesPage extends StatefulWidget { } class _ManageBookableSpacesPageState extends State { - final PageController _pageController = PageController(initialPage: 1); - int _currentPageIndex = 1; @override Widget build(BuildContext context) { - return WebScaffold( - enableMenuSidebar: false, - appBarTitle: Text( - 'Access Management', - style: ResponsiveTextTheme.of(context).deviceManagementTitle, - ), - centerBody: Row( - mainAxisSize: MainAxisSize.min, - children: [ - TextButton( - onPressed: () => _switchPage(0), - child: Text( - 'Access Overview', - style: context.textTheme.titleMedium?.copyWith( - color: _currentPageIndex == 0 ? Colors.white : Colors.grey, - fontWeight: - _currentPageIndex == 0 ? FontWeight.w700 : FontWeight.w400, + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => BookableSpacesBloc( + RemoteBookableSpacesService(HTTPService()), + )..add( + LoadBookableSpacesEvent( + BookableSpacesParams(currentPage: 1), ), ), + ), + BlocProvider( + create: (context) => UpdateBookableSpacesBloc( + RemoteUpdateBookableSpaceService(HTTPService()), ), - TextButton( - onPressed: () => _switchPage(1), - child: Text( - 'Booking System', - style: context.textTheme.titleMedium?.copyWith( - color: _currentPageIndex == 1 ? Colors.white : Colors.grey, - fontWeight: - _currentPageIndex == 1 ? FontWeight.w700 : FontWeight.w400, - ), - ), - ), - ], - ), - rightBody: const NavigateHomeGridView(), - scaffoldBody: BlocProvider( - create: (context) => BookableSpacesBloc( - RemoteBookableSpacesService(HTTPService()), - )..add(LoadBookableSpacesEvent( - BookableSpacesParams(currentPage: 1), - )), - child: const ManageBookableSpacesWidget(), + ) + ], + child: ManageBookableSpacesWidget( + pageController: widget.pageController, ), ); } - - void _switchPage(int index) { - setState(() => _currentPageIndex = index); - _pageController.jumpToPage(index); - } } class ManageBookableSpacesWidget extends StatelessWidget { - const ManageBookableSpacesWidget({super.key}); + final PageController pageController; + + const ManageBookableSpacesWidget({ + super.key, + required this.pageController, + }); @override Widget build(BuildContext context) { @@ -91,332 +65,23 @@ class ManageBookableSpacesWidget extends StatelessWidget { child: Column( children: [ Expanded( - flex: 10, - child: Padding( - padding: const EdgeInsetsGeometry.symmetric(vertical: 5), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SvgTextButton( - svgSize: 15, - fontSize: 10, - fontWeight: FontWeight.bold, - svgAsset: Assets.backButtonIcon, - label: 'Booking Home', - onPressed: () { - context.pop(); - }), - SvgTextButton( - svgSize: 15, - fontSize: 10, - fontWeight: FontWeight.bold, - svgAsset: Assets.addButtonIcon, - label: 'Set Up a Bookable Spaces', - onPressed: () { - final bloc = context.read(); - showDialog( - context: context, - builder: (context) => BlocProvider.value( - value: bloc, - child: SetupBookableSpacesDialog(), - ), - ); - }, - ) - ], - ), - )), + flex: 10, child: TopPartWidget(pageController: pageController)), const SizedBox( height: 10, ), - Expanded( + const Expanded( flex: 85, - child: BlocBuilder( - builder: (context, state) { - if (state is BookableSpacesLoading) { - return const Center(child: CircularProgressIndicator()); - } else if (state is BookableSpacesError) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(state.error), - const SizedBox( - height: 5, - ), - ElevatedButton( - onPressed: () => context - .read() - .add(LoadBookableSpacesEvent( - BookableSpacesParams(currentPage: 1), - )), - child: const Text('try Again')) - ]); - } else if (state is BookableSpacesLoaded) { - return CustomDataTable( - items: state.bookableSpacesList.data, - cellsWidgets: (space) => [ - DataCell( - Padding( - padding: const EdgeInsetsGeometry.only(left: 10), - child: Text( - space.spaceName, - style: const TextStyle(fontSize: 11), - )), - ), - DataCell(Padding( - padding: const EdgeInsetsGeometry.only(left: 10), - child: Text( - space.spaceVirtualAddress, - style: const TextStyle(fontSize: 11), - ))), - DataCell(Container( - padding: const EdgeInsetsGeometry.only(left: 10), - width: 200, - child: Wrap( - spacing: 4, - children: space.spaceConfig!.bookableDays - .map((day) => Text( - day, - style: const TextStyle(fontSize: 11), - )) - .toList(), - ), - )), - DataCell( - Padding( - padding: const EdgeInsetsGeometry.only(left: 10), - child: Text( - space.spaceConfig!.bookingStartTime! - .format(context), - style: const TextStyle(fontSize: 11), - ), - ), - ), - DataCell( - Padding( - padding: const EdgeInsetsGeometry.only(left: 10), - child: Text( - space.spaceConfig!.bookingEndTime!.format(context), - style: const TextStyle(fontSize: 11), - ), - ), - ), - DataCell(Padding( - padding: const EdgeInsetsGeometry.only(left: 10), - child: Text( - '${space.spaceConfig!.cost} Points', - style: const TextStyle(fontSize: 11), - ))), - DataCell(Center( - child: Transform.scale( - scale: 0.7, - child: Switch( - value: space.spaceConfig!.availability, - trackColor: WidgetStateProperty.resolveWith( - (Set states) { - return ColorsManager.blue1; - }), - inactiveTrackColor: ColorsManager.lightGrayColor, - thumbColor: WidgetStateProperty.resolveWith( - (Set states) { - return ColorsManager.whiteColors; - }), - onChanged: (value) {}, - ), - ), - )), - DataCell(Center( - child: ElevatedButton( - onPressed: () {}, - style: ElevatedButton.styleFrom( - padding: EdgeInsets.zero, - fixedSize: const Size(50, 30), - elevation: 1, - ), - child: SvgPicture.asset( - Assets.settings, - height: 15, - color: ColorsManager.blue1, - ), - ), - )), - ], - columnsTitles: const [ - 'Space', - 'Space Virtual Address', - 'Bookable Days', - 'Booking Start Time', - 'Booking End Time', - 'Cost', - 'Availability', - 'Settings', - ], - ); - } else { - return const SizedBox(); - } - }, - ), + child: TablePartWidget(), ), const SizedBox( height: 5, ), - Expanded( + const Expanded( flex: 5, - child: BlocBuilder( - builder: (context, state) { - if (state is BookableSpacesLoaded) { - final totalPages = state.bookableSpacesList.totalPages; - final currentPage = state.bookableSpacesList.page; - - List paginationItems = []; - - // « Two pages back - if (currentPage > 2) { - paginationItems.add( - _buildArrowButton( - label: '«', - onTap: () { - context.read().add( - LoadBookableSpacesEvent( - BookableSpacesParams( - currentPage: currentPage - 2), - ), - ); - }, - ), - ); - } - - // < One page back - if (currentPage > 1) { - paginationItems.add( - _buildArrowButton( - label: '<', - onTap: () { - context.read().add( - LoadBookableSpacesEvent( - BookableSpacesParams( - currentPage: currentPage - 1), - ), - ); - }, - ), - ); - } - - // Page numbers - for (int i = 1; i <= totalPages; i++) { - paginationItems.add( - Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: GestureDetector( - onTap: () { - if (i != currentPage) { - context.read().add( - LoadBookableSpacesEvent( - BookableSpacesParams(currentPage: i), - ), - ); - } - }, - child: Container( - width: 30, - height: 30, - alignment: Alignment.center, - decoration: BoxDecoration( - color: i == currentPage - ? ColorsManager.dialogBlueTitle - : Colors.grey[300], - borderRadius: BorderRadius.circular(8), - ), - child: Text( - '$i', - style: TextStyle( - color: i == currentPage - ? Colors.white - : Colors.black, - fontWeight: i == currentPage - ? FontWeight.bold - : FontWeight.normal, - ), - ), - ), - ), - ), - ); - } - - // > One page forward - if (currentPage < totalPages) { - paginationItems.add( - _buildArrowButton( - label: '>', - onTap: () { - context.read().add( - LoadBookableSpacesEvent( - BookableSpacesParams( - currentPage: currentPage + 1), - ), - ); - }, - ), - ); - } - - // » Two pages forward - if (currentPage + 1 < totalPages) { - paginationItems.add( - _buildArrowButton( - label: '»', - onTap: () { - context.read().add( - LoadBookableSpacesEvent( - BookableSpacesParams( - currentPage: currentPage + 2), - ), - ); - }, - ), - ); - } - - return Row( - mainAxisAlignment: MainAxisAlignment.end, - children: paginationItems, - ); - } else { - return const SizedBox.shrink(); - } - }, - ), + child: BottomPaginationPartWidget(), ), ], ), ); } - - Widget _buildArrowButton( - {required String label, required VoidCallback onTap}) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: GestureDetector( - onTap: onTap, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - decoration: BoxDecoration( - color: Colors.grey[300], - borderRadius: BorderRadius.circular(8), - ), - child: Text( - label, - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ); - } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart index a404a900..bf1f1af6 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart'; @@ -21,6 +22,9 @@ class NextFirstStepButton extends StatelessWidget { ? null : () { context.read().goToNextStep(); + context + .read() + .add(CheckConfigurValidityEvent()); }, onCancelPressed: () => context.pop(), ); From bbf289180449ba47408e8be3e261fc0d41e9ef80 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 10 Jul 2025 15:52:38 +0300 Subject: [PATCH 20/71] refactor code --- .../widgets/space_step_part_widget.dart | 94 +---------- .../widgets/step_two_details_widget.dart | 148 +----------------- 2 files changed, 9 insertions(+), 233 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart index a1296ece..4706d77f 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart @@ -2,11 +2,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart'; -import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class SpacesStepDetailsWidget extends StatefulWidget { @@ -180,94 +179,3 @@ class _SpacesStepDetailsWidgetState extends State { ); } } - -class UnbookableListWidget extends StatelessWidget { - final PaginatedDataModel nonBookableSpaces; - const UnbookableListWidget({ - super.key, - required this.scrollController, - required this.nonBookableSpaces, - }); - - final ScrollController scrollController; - - @override - Widget build(BuildContext context) { - return Container( - width: 490, - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.vertical( - bottom: Radius.circular(20), - ), - ), - padding: const EdgeInsets.only(top: 10, left: 20, bottom: 5), - child: ListView.separated( - separatorBuilder: (context, index) => const SizedBox( - height: 5, - ), - controller: scrollController, - itemCount: nonBookableSpaces.data.length, - itemBuilder: (context, index) { - if (index < nonBookableSpaces.data.length) { - return CheckBoxSpaceWidget( - nonBookableSpace: nonBookableSpaces.data[index], - ); - } else { - return const Padding( - padding: EdgeInsets.symmetric(vertical: 10), - child: Center(child: CircularProgressIndicator()), - ); - } - }, - ), - ); - } -} - -class CheckBoxSpaceWidget extends StatefulWidget { - final BookableSpacemodel nonBookableSpace; - - const CheckBoxSpaceWidget({ - super.key, - required this.nonBookableSpace, - }); - - @override - State createState() => _CheckBoxSpaceWidgetState(); -} - -class _CheckBoxSpaceWidgetState extends State { - bool isChecked = false; - - @override - Widget build(BuildContext context) { - return Row( - children: [ - Checkbox( - value: isChecked, - onChanged: (value) => setState(() { - isChecked = value ?? false; - if (isChecked) { - context.read().add( - AddToBookableSpaceEvent( - nonBookableSpace: widget.nonBookableSpace, - ), - ); - } else { - context.read().add( - RemoveFromBookableSpaceEvent( - bookableSpace: widget.nonBookableSpace, - ), - ); - } - }), - ), - const SizedBox( - width: 5, - ), - Expanded(child: Text(widget.nonBookableSpace.spaceName)), - ], - ); - } -} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart index 4c694b66..365a8b8f 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart @@ -1,12 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/string_utils.dart'; class StepTwoDetailsWidget extends StatelessWidget { final TextEditingController pointsController; @@ -26,145 +23,16 @@ class StepTwoDetailsWidget extends StatelessWidget { const SizedBox( height: 20, ), - Row( - children: [ - TitleAndTimePickerWidget( - title: 'Booking Start Time', - onTimePicked: (timePicked) { - if (timePicked == null) { - return; - } - final nonBookableBloc = context.read(); - - if (nonBookableBloc.endTime != null && - isEndTimeAfterStartTime( - timePicked, nonBookableBloc.endTime!)) { - ScaffoldMessenger.of(context).clearSnackBars(); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: - Text("You can't choose start Time Before End time"), - duration: Duration(seconds: 2), - backgroundColor: ColorsManager.red, - )); - throw Exception(); - } else { - nonBookableBloc.selectedBookableSpaces.forEach( - (e) => e.spaceConfig!.bookingStartTime = timePicked, - ); - } - }, - ), - const SizedBox( - width: 20, - ), - TitleAndTimePickerWidget( - title: 'Booking End Time', - onTimePicked: (timePicked) { - if (timePicked == null) { - return; - } - final nonBookableBloc = context.read(); - if (nonBookableBloc.startTime != null && - isEndTimeAfterStartTime( - nonBookableBloc.startTime!, timePicked)) { - ScaffoldMessenger.of(context).clearSnackBars(); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: - Text("You can't choose End Time After Start time"), - duration: Duration(seconds: 2), - backgroundColor: ColorsManager.red, - )); - throw Exception(); - } else { - nonBookableBloc.selectedBookableSpaces.forEach( - (e) => e.spaceConfig!.bookingEndTime = timePicked, - ); - } - }, - ) - ], - ), + const BookingPeriodWidget(), const SizedBox( height: 20, ), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - '* ', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Colors.red), - ), - const Text('Points/hrs'), - ], - ), - const SizedBox( - height: 5, - ), - SearchUnbookableSpacesWidget( - title: 'Ex: 0', - height: 40, - onChanged: (p0) { - context - .read() - .selectedBookableSpaces - .forEach( - (e) => e.spaceConfig!.cost = int.parse( - pointsController.text.isEmpty - ? '0' - : pointsController.text, - ), - ); - context - .read() - .add(CheckConfigurValidityEvent()); - }, - controller: pointsController, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - suffix: const SizedBox(), - ), + BlocProvider( + create: (context) => TogglePointsSwitchCubit()..activateSwitch(), + child: PointsPartWidget(pointsController: pointsController), + ) ], ), ); } } - -class TitleAndTimePickerWidget extends StatelessWidget { - final void Function(TimeOfDay? timePicked) onTimePicked; - final String title; - const TitleAndTimePickerWidget({ - super.key, - required this.onTimePicked, - required this.title, - }); - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - '* ', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: Colors.red), - ), - Text(title), - ], - ), - const SizedBox( - height: 5, - ), - TimePickerWidget( - onTimePicked: onTimePicked, - ), - ], - ); - } -} From 6e4f0c3c0cd56ae89a3c819096be3162b6f0daf5 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 10 Jul 2025 15:52:52 +0300 Subject: [PATCH 21/71] edit time picker as in figma --- .../widgets/time_picker_widget.dart | 49 +++++++------------ 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart index 1cb87ebd..b3b1c00f 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart @@ -8,9 +8,11 @@ import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class TimePickerWidget extends StatefulWidget { + final String title; const TimePickerWidget({ super.key, required this.onTimePicked, + required this.title, }); final void Function(TimeOfDay? timePicked) onTimePicked; @@ -45,39 +47,24 @@ class _TimePickerWidgetState extends State { context.read().add(CheckConfigurValidityEvent()); setState(() {}); }, - child: Row( - children: [ - Container( - width: 56, - height: 32, - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), - bottomLeft: Radius.circular(10), - ), - ), - alignment: Alignment.center, - child: SvgPicture.asset(Assets.clockIcon), + child: Container( + width: 100, + height: 32, + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + topRight: Radius.circular(10), + bottomRight: Radius.circular(10), ), - Container( - width: 120, - height: 32, - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topRight: Radius.circular(10), - bottomRight: Radius.circular(10), - ), - ), - alignment: Alignment.centerLeft, - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Text( - timePicked == null ? 'HH:MM' : timePicked!.format(context), - style: const TextStyle(color: Color(0xB2D5D5D5)), - ), + ), + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text( + timePicked == null ? widget.title : timePicked!.format(context), + style: TextStyle( + color: ColorsManager.blackColor.withValues(alpha: 0.4), + fontSize: 12, ), - ], + ), ), ); } From a9895f54629350139ce804a652b2031248cdf7fb Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Fri, 11 Jul 2025 10:27:18 +0300 Subject: [PATCH 22/71] fix checkbox issue --- .../non_bookaable_spaces_bloc.dart | 4 +- .../widgets/check_box_space_widget.dart | 55 ++++++++----------- 2 files changed, 25 insertions(+), 34 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart index 9d8437fe..432b0317 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart @@ -42,7 +42,9 @@ class NonBookableSpacesBloc nonBookableSpacesList.data.addAll(currState.nonBookableSpaces.data); emit( - NonBookableSpacesLoaded(nonBookableSpaces: nonBookableSpacesList), + NonBookableSpacesLoaded( + nonBookableSpaces: nonBookableSpacesList, + ), ); } catch (e) { emit( diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart index 937ee35e..b5610166 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart @@ -3,56 +3,45 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; -class CheckBoxSpaceWidget extends StatefulWidget { +class CheckBoxSpaceWidget extends StatelessWidget { final BookableSpacemodel nonBookableSpace; final List selectedSpaces; + const CheckBoxSpaceWidget({ super.key, required this.nonBookableSpace, required this.selectedSpaces, }); - @override - State createState() => _CheckBoxSpaceWidgetState(); -} - -class _CheckBoxSpaceWidgetState extends State { - bool isChecked = false; - @override - void initState() { - isChecked = widget.selectedSpaces.any( - (element) => element.spaceUuid == widget.nonBookableSpace.spaceUuid, - ); - super.initState(); - } - @override Widget build(BuildContext context) { + final isChecked = selectedSpaces.any( + (element) => element.spaceUuid == nonBookableSpace.spaceUuid, + ); + return Row( children: [ Checkbox( value: isChecked, - onChanged: (value) => setState(() { - isChecked = value ?? false; - if (isChecked) { - context.read().add( - AddToBookableSpaceEvent( - nonBookableSpace: widget.nonBookableSpace, - ), - ); + onChanged: (value) { + final bloc = context.read(); + if (value ?? false) { + bloc.add( + AddToBookableSpaceEvent( + nonBookableSpace: nonBookableSpace, + ), + ); } else { - context.read().add( - RemoveFromBookableSpaceEvent( - bookableSpace: widget.nonBookableSpace, - ), - ); + bloc.add( + RemoveFromBookableSpaceEvent( + bookableSpace: nonBookableSpace, + ), + ); } - }), + }, ), - const SizedBox( - width: 5, - ), - Expanded(child: Text(widget.nonBookableSpace.spaceName)), + const SizedBox(width: 5), + Expanded(child: Text(nonBookableSpace.spaceName)), ], ); } From e9b4d35f97e3c08af688191244d94939e81d28dc Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 15 Jul 2025 15:17:43 +0300 Subject: [PATCH 23/71] add settings and edit feature to bookable spaces --- .../data/remote_non_bookable_spaces.dart | 2 +- .../domain/models/bookable_space_config.dart | 4 +- .../send_bookable_spaces_to_api_params.dart | 41 +++++++++++++++++++ .../params/update_bookable_space_param.dart | 18 ++++++++ .../service/non_bookable_spaces_service.dart | 2 +- .../non_bookaable_spaces_bloc.dart | 18 +++++++- .../non_bookaable_spaces_event.dart | 9 ++++ .../blocs/steps_cubit/cubit/steps_cubit.dart | 4 ++ .../blocs/steps_cubit/cubit/steps_state.dart | 1 - .../update_bookable_spaces_bloc.dart | 1 + .../update_bookable_spaces_event.dart | 2 + .../screens/setup_bookable_spaces_dialog.dart | 39 +++++++++++++----- .../widgets/booking_period_widget.dart | 35 +++++++++------- .../buttons_divider_bottom_dialog_widget.dart | 3 +- .../widgets/details_steps_widget.dart | 4 ++ .../table_part_widget.dart | 26 +++++++++++- .../widgets/points_part_widget.dart | 27 +++++++++--- .../widgets/save_second_step_button.dart | 27 ++++++++++-- .../widgets/step_two_details_widget.dart | 21 ++++++++-- .../widgets/stepper_part_widget.dart | 4 -- .../widgets/time_picker_widget.dart | 2 - .../widgets/week_checkbox_title_widget.dart | 20 ++++++++- 22 files changed, 255 insertions(+), 55 deletions(-) create mode 100644 lib/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart index cd06dd7f..5b286e5e 100644 --- a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart +++ b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart @@ -2,7 +2,7 @@ import 'package:dio/dio.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; import 'package:syncrow_web/services/api/api_exception.dart'; import 'package:syncrow_web/services/api/http_service.dart'; diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart index 9c61c087..e0ba9090 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart @@ -26,8 +26,8 @@ class BookableSpaceConfig { configUuid: json['uuid'] as String, bookableDays: (json['daysAvailable'] as List).cast(), availability: (json['active'] as bool?) ?? false, - bookingEndTime: parseTimeOfDay(json['startTime'] as String), - bookingStartTime: parseTimeOfDay(json['endTime'] as String), + bookingStartTime: parseTimeOfDay(json['startTime'] as String), + bookingEndTime: parseTimeOfDay(json['endTime'] as String), cost: json['points'] as int, ); diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart b/lib/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart new file mode 100644 index 00000000..38f099fb --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart @@ -0,0 +1,41 @@ +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/utils/string_utils.dart'; + +class SendBookableSpacesToApiParams { + List spaceUuids; + List daysAvailable; + String startTime; + String endTime; + int points; + SendBookableSpacesToApiParams({ + required this.spaceUuids, + required this.daysAvailable, + required this.startTime, + required this.endTime, + required this.points, + }); + + static SendBookableSpacesToApiParams fromBookableSpacesModel( + List bookableSpaces) { + return SendBookableSpacesToApiParams( + spaceUuids: bookableSpaces.map((space) => space.spaceUuid).toList(), + daysAvailable: bookableSpaces + .expand((space) => space.spaceConfig!.bookableDays) + .toSet() + .toList(), + startTime: formatTimeOfDayTo24HourString( + bookableSpaces.first.spaceConfig!.bookingStartTime!), + endTime: formatTimeOfDayTo24HourString( + bookableSpaces.first.spaceConfig!.bookingEndTime!), + points: bookableSpaces.first.spaceConfig!.cost, + ); + } + + Map toJson() => { + 'spaceUuids': spaceUuids, + 'daysAvailable': daysAvailable, + 'startTime': startTime, + 'endTime': endTime, + 'points': points + }; +} diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart b/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart index 07c79445..57962772 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart @@ -1,3 +1,7 @@ +import 'package:flutter/foundation.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/utils/string_utils.dart'; + class UpdateBookableSpaceParam { String spaceUuid; @@ -14,6 +18,20 @@ class UpdateBookableSpaceParam { this.availability, this.cost, }); + factory UpdateBookableSpaceParam.fromBookableModel( + BookableSpacemodel bookableSpace) { + return UpdateBookableSpaceParam( + spaceUuid: bookableSpace.spaceUuid, + availability: bookableSpace.spaceConfig!.availability, + bookableDays: bookableSpace.spaceConfig!.bookableDays, + cost: bookableSpace.spaceConfig!.cost, + bookingStartTime: formatTimeOfDayTo24HourString( + bookableSpace.spaceConfig!.bookingStartTime!), + bookingEndTime: formatTimeOfDayTo24HourString( + bookableSpace.spaceConfig!.bookingEndTime!), + ); + } + Map toJson() => { if (bookableDays != null) 'daysAvailable': bookableDays, if (bookingStartTime != null) 'startTime': bookingStartTime, diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart b/lib/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart index cbcb24fa..d4bc82fa 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart @@ -1,6 +1,6 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; abstract class NonBookableSpacesService { diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart index 432b0317..6b9852ee 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart @@ -3,8 +3,8 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; part 'non_bookaable_spaces_event.dart'; @@ -16,11 +16,13 @@ class NonBookableSpacesBloc List selectedBookableSpaces = []; NonBookableSpacesBloc(this.nonBookableSpacesService) : super(NonBookableSpacesInitial()) { + on(_onCallInitStateEvent); on(_onLoadUnBookableSpacesEvent); on(_onAddToBookableSpaceEvent); on(_onRemoveFromBookableSpaceEvent); on(_onSendBookableSpacesToApi); on(_onCheckConfigurValidityEvent); + on(_onEditModeSelected); } TimeOfDay? get endTime => @@ -28,6 +30,12 @@ class NonBookableSpacesBloc TimeOfDay? get startTime => selectedBookableSpaces.first.spaceConfig!.bookingStartTime; + + void _onCallInitStateEvent( + CallInitStateEvent event, Emitter emit) { + emit(NonBookableSpacesInitial()); + } + Future _onLoadUnBookableSpacesEvent(LoadUnBookableSpacesEvent event, Emitter emit) async { if (state is NonBookableSpacesLoaded) { @@ -117,7 +125,7 @@ class NonBookableSpacesBloc selectedBookableSpaces, ), ); - emit(NonBookableSpacesInitial()); + add(CallInitStateEvent()); } catch (e) { emit( NonBookableSpacesError(e.toString()), @@ -133,4 +141,10 @@ class NonBookableSpacesBloc emit(UnValidSaveButtonState()); } } + + void _onEditModeSelected( + EditModeSelected event, Emitter emit) { + selectedBookableSpaces.clear(); + selectedBookableSpaces.add(event.editingBookableSpace); + } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart index 4e204c55..f3000560 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart @@ -7,6 +7,8 @@ sealed class NonBookableSpacesEvent extends Equatable { List get props => []; } +class CallInitStateEvent extends NonBookableSpacesEvent {} + class LoadUnBookableSpacesEvent extends NonBookableSpacesEvent { final NonBookableSpacesParams nonBookableSpacesParams; const LoadUnBookableSpacesEvent({ @@ -31,3 +33,10 @@ class RemoveFromBookableSpaceEvent extends NonBookableSpacesEvent { class SendBookableSpacesToApi extends NonBookableSpacesEvent {} class CheckConfigurValidityEvent extends NonBookableSpacesEvent {} + +class EditModeSelected extends NonBookableSpacesEvent { + final BookableSpacemodel editingBookableSpace; + const EditModeSelected({ + required this.editingBookableSpace, + }); +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart index 07dba931..974a2b96 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart @@ -10,6 +10,10 @@ class StepsCubit extends Cubit { emit(StepOneState()); } + void editValueInit() { + emit(StepTwoState()); + } + void goToNextStep() { if (state is StepOneState) { emit(StepTwoState()); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_state.dart index d6b8dd37..707d0a41 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_state.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_state.dart @@ -13,4 +13,3 @@ final class StepOneState extends StepsState {} final class StepTwoState extends StepsState {} -final class StepEditMode extends StepsState {} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart index ccece88e..9f99b3ea 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart @@ -24,6 +24,7 @@ class UpdateBookableSpacesBloc await updateBookableSpaceService.update(event.updatedParams); emit(UpdateBookableSpaceSuccess(bookableSpaceConfig: updatedSpace)); + event.onSuccess?.call(); } on APIException catch (e) { emit(UpdateBookableSpaceFailure(error: e.message)); } catch (e) { diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_event.dart index 1076f589..6047d41a 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_event.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_event.dart @@ -8,8 +8,10 @@ sealed class UpdateBookableSpaceEvent extends Equatable { } class UpdateBookableSpace extends UpdateBookableSpaceEvent { + final void Function()? onSuccess; final UpdateBookableSpaceParam updatedParams; const UpdateBookableSpace({ required this.updatedParams, + this.onSuccess, }); } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart index b2d48b23..63a6574b 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; @@ -13,23 +14,36 @@ import 'package:syncrow_web/utils/color_manager.dart'; class SetupBookableSpacesDialog extends StatelessWidget { final TextEditingController pointsController = TextEditingController(); - SetupBookableSpacesDialog({super.key}); + final BookableSpacemodel? editingBookableSpace; + SetupBookableSpacesDialog({ + super.key, + this.editingBookableSpace, + }); @override Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider( - create: (context) => StepsCubit()..initDialogValue(), + create: editingBookableSpace == null + ? (context) => StepsCubit()..initDialogValue() + : (context) => StepsCubit()..editValueInit(), ), BlocProvider( - create: (context) => NonBookableSpacesBloc( - RemoteNonBookableSpaces(HTTPService()), - )..add( - LoadUnBookableSpacesEvent( - nonBookableSpacesParams: - NonBookableSpacesParams(currentPage: 1), - ), - ), + create: editingBookableSpace == null + ? (context) => NonBookableSpacesBloc( + RemoteNonBookableSpaces(HTTPService()), + )..add( + LoadUnBookableSpacesEvent( + nonBookableSpacesParams: + NonBookableSpacesParams(currentPage: 1), + ), + ) + : (context) => NonBookableSpacesBloc( + RemoteNonBookableSpaces(HTTPService()), + )..add( + EditModeSelected( + editingBookableSpace: editingBookableSpace!), + ), ), ], child: AlertDialog( @@ -67,6 +81,7 @@ class SetupBookableSpacesDialog extends StatelessWidget { flex: 7, child: DetailsStepsWidget( pointsController: pointsController, + editingBookableSpace: editingBookableSpace, ), ) ], @@ -79,7 +94,9 @@ class SetupBookableSpacesDialog extends StatelessWidget { ? NextFirstStepButton(selectedSpaces: selectedSpaces) : SaveSecondStepButton( selectedSpaces: selectedSpaces, - pointsController: pointsController); + pointsController: pointsController, + isEditingMode: editingBookableSpace != null, + ); }), ], ), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart index d10afee1..f3f2d5cc 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -8,8 +9,10 @@ import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/string_utils.dart'; class BookingPeriodWidget extends StatelessWidget { + final BookableSpacemodel? editingBookableSpace; const BookingPeriodWidget({ super.key, + this.editingBookableSpace, }); @override @@ -39,22 +42,23 @@ class BookingPeriodWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ TimePickerWidget( - title: 'Start Time', + title: editingBookableSpace == null + ? 'Start Time' + : editingBookableSpace!.spaceConfig!.bookingStartTime! + .format(context), onTimePicked: (timePicked) { if (timePicked == null) { return; } - final nonBookableBloc = - context.read(); - + final nonBookableBloc = context.read(); + if (nonBookableBloc.endTime != null && isEndTimeAfterStartTime( timePicked, nonBookableBloc.endTime!)) { ScaffoldMessenger.of(context).clearSnackBars(); - ScaffoldMessenger.of(context) - .showSnackBar(const SnackBar( - content: Text( - "You can't choose start Time Before End time"), + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: + Text("You can't choose start Time Before End time"), duration: Duration(seconds: 2), backgroundColor: ColorsManager.red, )); @@ -71,21 +75,22 @@ class BookingPeriodWidget extends StatelessWidget { color: ColorsManager.grayColor, ), TimePickerWidget( - title: 'End Time', + title: editingBookableSpace == null + ? 'End Time' + : editingBookableSpace!.spaceConfig!.bookingEndTime! + .format(context), onTimePicked: (timePicked) { if (timePicked == null) { return; } - final nonBookableBloc = - context.read(); + final nonBookableBloc = context.read(); if (nonBookableBloc.startTime != null && isEndTimeAfterStartTime( nonBookableBloc.startTime!, timePicked)) { ScaffoldMessenger.of(context).clearSnackBars(); - ScaffoldMessenger.of(context) - .showSnackBar(const SnackBar( - content: Text( - "You can't choose End Time After Start time"), + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: + Text("You can't choose End Time After Start time"), duration: Duration(seconds: 2), backgroundColor: ColorsManager.red, )); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart index 581ab89f..71af98ac 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart @@ -4,7 +4,6 @@ import 'package:go_router/go_router.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class ButtonsDividerBottomDialogWidget extends StatelessWidget { @@ -63,7 +62,7 @@ class ButtonsDividerBottomDialogWidget extends StatelessWidget { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( - 'Spaces Added Successfully', + 'Operation Done Successfully', style: TextStyle(color: ColorsManager.activeGreen), ), duration: Duration(seconds: 2), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart index 504c25e0..bca22b8d 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart @@ -1,14 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart'; class DetailsStepsWidget extends StatelessWidget { final TextEditingController pointsController; + final BookableSpacemodel? editingBookableSpace; const DetailsStepsWidget({ super.key, required this.pointsController, + this.editingBookableSpace, }); @override @@ -22,6 +25,7 @@ class DetailsStepsWidget extends StatelessWidget { } else if (state is StepTwoState) { return StepTwoDetailsWidget( pointsController: pointsController, + editingBookableSpace:editingBookableSpace ); } else { return const SizedBox(); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart index 3534f987..fb8c16c3 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart @@ -1,12 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_update_bookable_space_service.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_data_table.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -143,7 +146,28 @@ class TablePartWidget extends StatelessWidget { )), DataCell(Center( child: ElevatedButton( - onPressed: () {}, + onPressed: () { + final bookableBloc = context.read(); + + showDialog( + context: context, + builder: (context) => MultiBlocProvider( + providers: [ + BlocProvider.value( + value: bookableBloc, + ), + BlocProvider( + create: (context) => UpdateBookableSpacesBloc( + RemoteUpdateBookableSpaceService(HTTPService()), + ), + ), + ], + child: SetupBookableSpacesDialog( + editingBookableSpace: space, + ), + ), + ); + }, style: ElevatedButton.styleFrom( padding: EdgeInsets.zero, fixedSize: const Size(50, 30), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart index 06d11127..f62e4941 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart @@ -1,19 +1,36 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -class PointsPartWidget extends StatelessWidget { +class PointsPartWidget extends StatefulWidget { + final BookableSpacemodel? editingBookableSpace; const PointsPartWidget({ super.key, required this.pointsController, + this.editingBookableSpace, }); final TextEditingController pointsController; + @override + State createState() => _PointsPartWidgetState(); +} + +class _PointsPartWidgetState extends State { + @override + void initState() { + if (widget.editingBookableSpace != null) { + widget.pointsController.text = + widget.editingBookableSpace!.spaceConfig!.cost.toString(); + } + super.initState(); + } + @override Widget build(BuildContext context) { return BlocBuilder( @@ -73,7 +90,7 @@ class PointsPartWidget extends StatelessWidget { context .read() .unActivateSwitch(); - pointsController.clear(); + widget.pointsController.clear(); context .read() .selectedBookableSpaces @@ -102,16 +119,16 @@ class PointsPartWidget extends StatelessWidget { .selectedBookableSpaces .forEach( (e) => e.spaceConfig!.cost = int.parse( - pointsController.text.isEmpty + widget.pointsController.text.isEmpty ? '0' - : pointsController.text, + : widget.pointsController.text, ), ); context .read() .add(CheckConfigurValidityEvent()); }, - controller: pointsController, + controller: widget.pointsController, inputFormatters: [FilteringTextInputFormatter.digitsOnly], suffix: const SizedBox(), ) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart index cf57e601..b2f6dc33 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart @@ -2,17 +2,21 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart'; class SaveSecondStepButton extends StatelessWidget { final List selectedSpaces; final TextEditingController pointsController; + final bool isEditingMode; const SaveSecondStepButton({ super.key, required this.selectedSpaces, required this.pointsController, + required this.isEditingMode, }); @override @@ -27,9 +31,11 @@ class SaveSecondStepButton extends StatelessWidget { if (selectedSpaces.any( (element) => element.isValid, )) { - context.read().add( - SendBookableSpacesToApi(), - ); + isEditingMode + ? callEditLogic(context) + : context.read().add( + SendBookableSpacesToApi(), + ); } }, onCancelPressed: () => context.pop(), @@ -37,4 +43,19 @@ class SaveSecondStepButton extends StatelessWidget { }, ); } + + void callEditLogic(BuildContext context) { + context.read().add( + UpdateBookableSpace( + onSuccess: () => + context.read().add(CallInitStateEvent()), + updatedParams: UpdateBookableSpaceParam.fromBookableModel( + context + .read() + .selectedBookableSpaces + .first, + ), + ), + ); + } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart index 365a8b8f..d4177a77 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart'; @@ -7,9 +8,11 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/prese class StepTwoDetailsWidget extends StatelessWidget { final TextEditingController pointsController; + final BookableSpacemodel? editingBookableSpace; const StepTwoDetailsWidget({ super.key, required this.pointsController, + this.editingBookableSpace, }); @override Widget build(BuildContext context) { @@ -19,17 +22,27 @@ class StepTwoDetailsWidget extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const WeekDaysCheckboxRow(), + WeekDaysCheckboxRow( + editingBookableSpace: editingBookableSpace, + ), const SizedBox( height: 20, ), - const BookingPeriodWidget(), + BookingPeriodWidget( + editingBookableSpace: editingBookableSpace, + ), const SizedBox( height: 20, ), BlocProvider( - create: (context) => TogglePointsSwitchCubit()..activateSwitch(), - child: PointsPartWidget(pointsController: pointsController), + create: editingBookableSpace == null + ? (context) => TogglePointsSwitchCubit()..activateSwitch() + : editingBookableSpace!.spaceConfig!.cost == 0 + ? (context) => TogglePointsSwitchCubit()..unActivateSwitch() + : (context) => TogglePointsSwitchCubit()..activateSwitch(), + child: PointsPartWidget( + pointsController: pointsController, + editingBookableSpace: editingBookableSpace), ) ], ), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart index 622fdbe0..29e8ba42 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart @@ -68,10 +68,6 @@ class StepperPartWidget extends StatelessWidget { ) ], ); - } else if (state is StepEditMode) { - return const CircleTitleStepperWidget( - title: 'Settings', - ); } else { return const SizedBox(); } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart index b3b1c00f..daa9c4af 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart @@ -1,11 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/constants/assets.dart'; class TimePickerWidget extends StatefulWidget { final String title; diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart index c0d3c1b8..77ad2351 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart @@ -1,9 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; class WeekDaysCheckboxRow extends StatefulWidget { - const WeekDaysCheckboxRow({super.key}); + final BookableSpacemodel? editingBookableSpace; + const WeekDaysCheckboxRow({ + super.key, + this.editingBookableSpace, + }); @override State createState() => _WeekDaysCheckboxRowState(); @@ -19,6 +24,19 @@ class _WeekDaysCheckboxRowState extends State { 'Sat': false, 'Sun': false, }; + @override + void initState() { + super.initState(); + + final existingDays = + widget.editingBookableSpace?.spaceConfig?.bookableDays ?? []; + + for (var day in _daysChecked.keys) { + if (existingDays.contains(day)) { + _daysChecked[day] = true; + } + } + } @override Widget build(BuildContext context) { From ab326fa820030e2b28eae16b2e9eca80abbbfbae Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 15 Jul 2025 15:18:14 +0300 Subject: [PATCH 24/71] delete dummy files --- .../data/dummy_non_nookable_spaces.dart | 73 ------------------- .../send_bookable_spaces_to_api_params.dart | 41 ----------- 2 files changed, 114 deletions(-) delete mode 100644 lib/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart delete mode 100644 lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart b/lib/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart deleted file mode 100644 index 675fcf4c..00000000 --- a/lib/pages/access_management/manage_bookable_spaces/data/dummy_non_nookable_spaces.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart'; -import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; - -class DummyNonNookableSpaces implements NonBookableSpacesService { - @override - Future> load( - NonBookableSpacesParams params) { - return Future.value(PaginatedDataModel( - data: [ - BookableSpacemodel( - spaceName: 'space3', - spaceConfig: BookableSpaceConfig( - configUuid: 'uuid', - bookableDays: ['wed', 'saturday'], - availability: true, - bookingEndTime: const TimeOfDay(hour: 13, minute: 20), - bookingStartTime: const TimeOfDay(hour: 6, minute: 20), - cost: 6, - ), - spaceUuid: 'uuiiddd', - spaceVirtualAddress: 'idvirtual1', - ), - BookableSpacemodel( - spaceName: 'space3', - spaceConfig: BookableSpaceConfig( - configUuid: 'uuid', - bookableDays: ['wed', 'saturday', 'thuresday'], - availability: true, - bookingEndTime: const TimeOfDay(hour: 13, minute: 20), - bookingStartTime: const TimeOfDay(hour: 5, minute: 20), - cost: 5, - ), - spaceUuid: 'uuiiddd', - spaceVirtualAddress: 'idvirtual2', - ), - BookableSpacemodel( - spaceName: 'space3', - spaceConfig: BookableSpaceConfig( - configUuid: 'uuid', - bookableDays: [ - 'saturday', - 'sunday', - 'Monday', - 'tuesday', - 'wed', - 'thuresday' - ], - availability: true, - bookingEndTime: const TimeOfDay(hour: 13, minute: 20), - bookingStartTime: const TimeOfDay(hour: 15, minute: 20), - cost: 2, - ), - spaceUuid: 'uuiiddd', - spaceVirtualAddress: 'idvirtual3', - ) - ], - page: 1, - size: 1, - hasNext: false, - totalPages: 0, - totalItems: 0, - )); - } - - @override - Future sendBookableSpacesToApi( - SendBookableSpacesToApiParams params) async {} -} diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart b/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart deleted file mode 100644 index 38f099fb..00000000 --- a/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_to_api_params.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; -import 'package:syncrow_web/utils/string_utils.dart'; - -class SendBookableSpacesToApiParams { - List spaceUuids; - List daysAvailable; - String startTime; - String endTime; - int points; - SendBookableSpacesToApiParams({ - required this.spaceUuids, - required this.daysAvailable, - required this.startTime, - required this.endTime, - required this.points, - }); - - static SendBookableSpacesToApiParams fromBookableSpacesModel( - List bookableSpaces) { - return SendBookableSpacesToApiParams( - spaceUuids: bookableSpaces.map((space) => space.spaceUuid).toList(), - daysAvailable: bookableSpaces - .expand((space) => space.spaceConfig!.bookableDays) - .toSet() - .toList(), - startTime: formatTimeOfDayTo24HourString( - bookableSpaces.first.spaceConfig!.bookingStartTime!), - endTime: formatTimeOfDayTo24HourString( - bookableSpaces.first.spaceConfig!.bookingEndTime!), - points: bookableSpaces.first.spaceConfig!.cost, - ); - } - - Map toJson() => { - 'spaceUuids': spaceUuids, - 'daysAvailable': daysAvailable, - 'startTime': startTime, - 'endTime': endTime, - 'points': points - }; -} From f5def4b4d7359d1210716ad6ead56a5369dd3c1c Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 15 Jul 2025 15:26:37 +0300 Subject: [PATCH 25/71] to open Request --- .../widgets/main_manage_bookable_widgets/top_part_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart index 506773dc..66c24f88 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart @@ -57,7 +57,7 @@ class TopPartWidget extends StatelessWidget { fontWeight: FontWeight.bold, svgAsset: Assets.addButtonIcon, label: 'Set Up a Bookable Spaces', - onPressed: () { + onPressed: () { final bloc = context.read(); showDialog( context: context, From e8c36f5af62b3f1164e6807c92e80c17ecfb841f Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 15 Jul 2025 15:36:20 +0300 Subject: [PATCH 26/71] fix after merge --- .../widgets/main_manage_bookable_widgets/top_part_widget.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart index 66c24f88..2a0d0514 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:syncrow_web/pages/access_management/booking_system/view/widgets/icon_text_button.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -57,7 +57,7 @@ class TopPartWidget extends StatelessWidget { fontWeight: FontWeight.bold, svgAsset: Assets.addButtonIcon, label: 'Set Up a Bookable Spaces', - onPressed: () { + onPressed: () { final bloc = context.read(); showDialog( context: context, From 6b8827f4d9dc586b625b4a6b6a6493a12fae22a6 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 15 Jul 2025 15:37:08 +0300 Subject: [PATCH 27/71] fixes after merge --- .../data/services/remote_bookable_spaces_service.dart | 2 +- .../access_management/booking_system/view/booking_page.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/access_management/booking_system/data/services/remote_bookable_spaces_service.dart b/lib/pages/access_management/booking_system/data/services/remote_bookable_spaces_service.dart index 3c2610db..5ed71116 100644 --- a/lib/pages/access_management/booking_system/data/services/remote_bookable_spaces_service.dart +++ b/lib/pages/access_management/booking_system/data/services/remote_bookable_spaces_service.dart @@ -18,7 +18,7 @@ class RemoteBookableSpacesService implements BookableSystemService { }) async { try { final response = await _httpService.get( - path: ApiEndpoints.getBookableSpaces, + path: ApiEndpoints.bookableSpaces, queryParameters: { 'page': param.page, 'size': param.size, diff --git a/lib/pages/access_management/booking_system/view/booking_page.dart b/lib/pages/access_management/booking_system/view/booking_page.dart index 4b4ca49f..67f7dbf3 100644 --- a/lib/pages/access_management/booking_system/view/booking_page.dart +++ b/lib/pages/access_management/booking_system/view/booking_page.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart'; -import 'package:syncrow_web/pages/access_management/booking_system/view/widgets/icon_text_button.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class BookingPage extends StatelessWidget { From db157f30c5a86f2c69c1ddf136454fe315f0eb88 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Wed, 16 Jul 2025 10:35:16 +0300 Subject: [PATCH 28/71] first notes requests --- .../bookable_spaces_bloc.dart | 2 +- .../bookable_spaces_event.dart | 4 +-- .../manage_bookable_spaces_screen.dart | 7 ++--- .../screens/setup_bookable_spaces_dialog.dart | 27 ++++++++++++++----- .../buttons_divider_bottom_dialog_widget.dart | 2 +- .../widgets/details_steps_widget.dart | 22 +++++++-------- .../bottom_pagination_part_widget.dart | 4 +-- .../table_part_widget.dart | 6 ++--- .../top_part_widget.dart | 4 +-- .../search_unbookable_spaces_widget.dart | 14 +++++----- .../widgets/space_step_part_widget.dart | 2 +- lib/utils/color_manager.dart | 3 +++ 12 files changed, 56 insertions(+), 41 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart index 009db2b9..d5a492be 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart @@ -23,7 +23,7 @@ class BookableSpacesBloc LoadBookableSpacesEvent event, Emitter emit) async { emit(BookableSpacesLoading()); try { - final bookableSpaces = await bookableSpacesService.load(event.params); + final bookableSpaces = await bookableSpacesService.load(event.param); emit(BookableSpacesLoaded(bookableSpacesList: bookableSpaces)); } on APIException catch (e) { emit(BookableSpacesError(error: e.message)); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart index 46cfe908..7c5344c8 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart @@ -8,8 +8,8 @@ sealed class BookableSpacesEvent extends Equatable { } class LoadBookableSpacesEvent extends BookableSpacesEvent { - final BookableSpacesParams params; - const LoadBookableSpacesEvent(this.params); + final BookableSpacesParams param; + const LoadBookableSpacesEvent(this.param); } class InsertUpdatedSpaceEvent extends BookableSpacesEvent { diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart index 1c2e98f6..78911114 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart @@ -65,20 +65,21 @@ class ManageBookableSpacesWidget extends StatelessWidget { child: Column( children: [ Expanded( - flex: 10, child: TopPartWidget(pageController: pageController)), + flex: 10, + child: RowOfButtonsTitleWidget(pageController: pageController)), const SizedBox( height: 10, ), const Expanded( flex: 85, - child: TablePartWidget(), + child: TableOfBookableSpacesWidget(), ), const SizedBox( height: 5, ), const Expanded( flex: 5, - child: BottomPaginationPartWidget(), + child: PaginationButtonsWidget(), ), ], ), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart index 63a6574b..ec3db30a 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart @@ -12,24 +12,37 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/prese import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -class SetupBookableSpacesDialog extends StatelessWidget { - final TextEditingController pointsController = TextEditingController(); +class SetupBookableSpacesDialog extends StatefulWidget { final BookableSpacemodel? editingBookableSpace; SetupBookableSpacesDialog({ super.key, this.editingBookableSpace, }); + + @override + State createState() => + _SetupBookableSpacesDialogState(); +} + +class _SetupBookableSpacesDialogState extends State { + final TextEditingController pointsController = TextEditingController(); + @override + void dispose() { + pointsController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider( - create: editingBookableSpace == null + create: widget.editingBookableSpace == null ? (context) => StepsCubit()..initDialogValue() : (context) => StepsCubit()..editValueInit(), ), BlocProvider( - create: editingBookableSpace == null + create: widget.editingBookableSpace == null ? (context) => NonBookableSpacesBloc( RemoteNonBookableSpaces(HTTPService()), )..add( @@ -42,7 +55,7 @@ class SetupBookableSpacesDialog extends StatelessWidget { RemoteNonBookableSpaces(HTTPService()), )..add( EditModeSelected( - editingBookableSpace: editingBookableSpace!), + editingBookableSpace: widget.editingBookableSpace!), ), ), ], @@ -81,7 +94,7 @@ class SetupBookableSpacesDialog extends StatelessWidget { flex: 7, child: DetailsStepsWidget( pointsController: pointsController, - editingBookableSpace: editingBookableSpace, + editingBookableSpace: widget.editingBookableSpace, ), ) ], @@ -95,7 +108,7 @@ class SetupBookableSpacesDialog extends StatelessWidget { : SaveSecondStepButton( selectedSpaces: selectedSpaces, pointsController: pointsController, - isEditingMode: editingBookableSpace != null, + isEditingMode: widget.editingBookableSpace != null, ); }), ], diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart index 71af98ac..80981897 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart @@ -80,7 +80,7 @@ class ButtonsDividerBottomDialogWidget extends StatelessWidget { content: Text( nonBookableState.error, style: - const TextStyle(color: ColorsManager.activeGreen), + const TextStyle(color: ColorsManager.red), ), duration: const Duration(seconds: 2), behavior: SnackBarBehavior.floating, diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart index bca22b8d..32913c89 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart @@ -18,20 +18,16 @@ class DetailsStepsWidget extends StatelessWidget { Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20), - child: BlocBuilder( - builder: (context, state) { - if (state is StepOneState) { - return const SpacesStepDetailsWidget(); - } else if (state is StepTwoState) { - return StepTwoDetailsWidget( + child: BlocBuilder(builder: (context, state) { + return switch (state) { + StepOneState() => const SpacesStepDetailsWidget(), + StepTwoState() => StepTwoDetailsWidget( pointsController: pointsController, - editingBookableSpace:editingBookableSpace - ); - } else { - return const SizedBox(); - } - }, - ), + editingBookableSpace: editingBookableSpace, + ), + StepsInitial() => const SizedBox(), + }; + }), ); } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart index 247d91a6..104425c3 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart @@ -4,8 +4,8 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domai import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -class BottomPaginationPartWidget extends StatelessWidget { - const BottomPaginationPartWidget({super.key}); +class PaginationButtonsWidget extends StatelessWidget { + const PaginationButtonsWidget({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart index fb8c16c3..0f1ddc4b 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart @@ -13,8 +13,8 @@ import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -class TablePartWidget extends StatelessWidget { - const TablePartWidget({ +class TableOfBookableSpacesWidget extends StatelessWidget { + const TableOfBookableSpacesWidget({ super.key, }); @@ -36,7 +36,7 @@ class TablePartWidget extends StatelessWidget { .add(LoadBookableSpacesEvent( BookableSpacesParams(currentPage: 1), )), - child: const Text('try Again')) + child: const Text('Try Again')) ]); } else if (state is BookableSpacesLoaded) { return CustomDataTable( diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart index 2a0d0514..1778ad19 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart @@ -7,8 +7,8 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/prese import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -class TopPartWidget extends StatelessWidget { - const TopPartWidget({ +class RowOfButtonsTitleWidget extends StatelessWidget { + const RowOfButtonsTitleWidget({ super.key, required this.pageController, }); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart index 0153624f..f2266874 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; class SearchUnbookableSpacesWidget extends StatelessWidget { final String title; @@ -27,11 +28,11 @@ class SearchUnbookableSpacesWidget extends StatelessWidget { height: height ?? 30, padding: const EdgeInsets.only(top: 4), decoration: BoxDecoration( - color: Colors.white, + color: ColorsManager.whiteColors, borderRadius: BorderRadius.circular(12), boxShadow: const [ BoxShadow( - color: Color(0x26000000), + color: ColorsManager.shadowOfSearchTextfield, offset: Offset(0, 4), blurRadius: 5, ), @@ -45,14 +46,15 @@ class SearchUnbookableSpacesWidget extends StatelessWidget { contentPadding: const EdgeInsets.symmetric(vertical: 5, horizontal: 15), hintText: title, - hintStyle: const TextStyle(color: Colors.grey), + hintStyle: const TextStyle(color: ColorsManager.hintTextGrey), border: InputBorder.none, - suffixIcon: - suffix ?? const Icon(Icons.search, size: 20, color: Colors.grey), + suffixIcon: suffix ?? + const Icon(Icons.search, + size: 20, color: ColorsManager.hintTextGrey), ), style: const TextStyle( fontSize: 14, - color: Colors.grey, + color: ColorsManager.hintTextGrey, ), ), ); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart index 4706d77f..13a89106 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart @@ -80,7 +80,7 @@ class _SpacesStepDetailsWidgetState extends State { borderRadius: BorderRadius.circular(20), boxShadow: const [ BoxShadow( - color: Color(0x40000000), + color: ColorsManager.shadowOfDetailsContainer, offset: Offset.zero, blurRadius: 5, ), diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 2f66f3a3..20f129c1 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -87,4 +87,7 @@ abstract class ColorsManager { static const Color grey50 = Color(0xFF718096); static const Color red100 = Color(0xFFFE0202); static const Color grey800 = Color(0xffF8F8F8); + static const Color shadowOfSearchTextfield = Color(0x26000000); + static const Color hintTextGrey = Colors.grey; + static const Color shadowOfDetailsContainer = Color(0x40000000); } From 739b491bd82bbe74cd9cb3568d42d53540985ea2 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Wed, 16 Jul 2025 11:21:32 +0300 Subject: [PATCH 29/71] debouncer Note --- .../data/remote_non_bookable_spaces.dart | 75 +++++++++++-------- .../widgets/space_step_part_widget.dart | 24 +++--- 2 files changed, 53 insertions(+), 46 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart index 5b286e5e..c42e8129 100644 --- a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart +++ b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart @@ -1,51 +1,62 @@ +import 'dart:async'; + import 'package:dio/dio.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; import 'package:syncrow_web/services/api/api_exception.dart'; import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; class RemoteNonBookableSpaces implements NonBookableSpacesService { + Timer? _debounce; + final HTTPService _httpService; RemoteNonBookableSpaces(this._httpService); static const _defaultErrorMessage = 'Failed to load Spaces'; @override Future> load( - NonBookableSpacesParams params) async { - try { - final response = await _httpService.get( - path: ApiEndpoints.bookableSpaces, - queryParameters: { - 'configured': false, - 'page': params.currentPage, - 'search': params.searchedWords, - }, - expectedResponseModel: (json) { - final result = json as Map; - return PaginatedDataModel.fromJson( - result, - BookableSpacemodel.fromJsonList, - ); - }, - ); - return response; - } on DioException catch (e) { - final message = e.response?.data as Map?; - final error = message?['error'] as Map?; - final errorMessage = error?['error'] as String? ?? ''; - final formattedErrorMessage = [ - _defaultErrorMessage, - errorMessage, - ].join(': '); - throw APIException(formattedErrorMessage); - } catch (e) { - final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); - throw APIException(formattedErrorMessage); - } + NonBookableSpacesParams params) { + final completer = Completer>(); + + _debounce?.cancel(); + _debounce = Timer(const Duration(milliseconds: 500), () async { + try { + final response = await _httpService.get( + path: ApiEndpoints.bookableSpaces, + queryParameters: { + 'configured': false, + 'page': params.currentPage, + 'search': params.searchedWords, + }, + expectedResponseModel: (json) { + final result = json as Map; + return PaginatedDataModel.fromJson( + result, + BookableSpacemodel.fromJsonList, + ); + }, + ); + completer.complete(response); + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [ + _defaultErrorMessage, + errorMessage, + ].join(': '); + completer.completeError(APIException(formattedErrorMessage)); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + completer.completeError(APIException(formattedErrorMessage)); + } + }); + + return completer.future; } @override diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart index 13a89106..953ec1f6 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart @@ -19,7 +19,6 @@ class SpacesStepDetailsWidget extends StatefulWidget { } class _SpacesStepDetailsWidgetState extends State { - Timer? _debounce; ScrollController scrollController = ScrollController(); int currentPage = 1; String? currentSearchTerm; @@ -94,7 +93,7 @@ class _SpacesStepDetailsWidgetState extends State { padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 20), decoration: const BoxDecoration( - color: Color(0xFFF8F8F8), + color: ColorsManager.circleRolesBackground, borderRadius: BorderRadius.vertical( top: Radius.circular(20), ), @@ -102,19 +101,16 @@ class _SpacesStepDetailsWidgetState extends State { child: SearchUnbookableSpacesWidget( title: 'Search', onChanged: (p0) { - if (_debounce?.isActive ?? false) _debounce!.cancel(); - _debounce = Timer(const Duration(milliseconds: 500), () { - currentSearchTerm = p0; - currentPage = 1; - context.read().add( - LoadUnBookableSpacesEvent( - nonBookableSpacesParams: NonBookableSpacesParams( - currentPage: currentPage, - searchedWords: currentSearchTerm, - ), + currentSearchTerm = p0; + currentPage = 1; + context.read().add( + LoadUnBookableSpacesEvent( + nonBookableSpacesParams: NonBookableSpacesParams( + currentPage: currentPage, + searchedWords: currentSearchTerm, ), - ); - }); + ), + ); }, ), ), From 7f9f39811b180ba5c9b14e089487d1fcecba36ae Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Wed, 16 Jul 2025 11:26:14 +0300 Subject: [PATCH 30/71] use switch state instead of if else --- .../widgets/space_step_part_widget.dart | 60 +++++++++---------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart index 953ec1f6..60ba2441 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; @@ -123,15 +121,13 @@ class _SpacesStepDetailsWidgetState extends State { } }, builder: (context, state) { - if (state is NonBookableSpacesError) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(state.error), - const SizedBox( - height: 5, - ), - ElevatedButton( + return switch (state) { + NonBookableSpacesError(error: final error) => Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(error), + const SizedBox(height: 5), + ElevatedButton( onPressed: () { context.read().add( LoadUnBookableSpacesEvent( @@ -143,28 +139,28 @@ class _SpacesStepDetailsWidgetState extends State { ), ); }, - child: const Text('Try Again')) - ], - ); - } else if (state is NonBookableSpacesLoading) { - if (state.lastNonBookableSpaces == null) { - return const Center( - child: CircularProgressIndicator(), - ); - } else { - return UnbookableListWidget( + child: const Text('Try Again'), + ), + ], + ), + NonBookableSpacesLoading(lastNonBookableSpaces: null) => + const Center(child: CircularProgressIndicator()), + NonBookableSpacesLoading( + lastNonBookableSpaces: final spaces + ) => + UnbookableListWidget( scrollController: scrollController, - nonBookableSpaces: state.lastNonBookableSpaces!, - ); - } - } else if (state is NonBookableSpacesLoaded) { - return UnbookableListWidget( - scrollController: scrollController, - nonBookableSpaces: state.nonBookableSpaces, - ); - } else { - return const SizedBox(); - } + nonBookableSpaces: spaces!, + ), + NonBookableSpacesLoaded( + nonBookableSpaces: final spaces + ) => + UnbookableListWidget( + scrollController: scrollController, + nonBookableSpaces: spaces, + ), + _ => const SizedBox(), + }; }, ), ) From 6db60a2a970c66c79134bd9b7c3e4a09e8c927e5 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Wed, 16 Jul 2025 11:31:54 +0300 Subject: [PATCH 31/71] dont use context in async block --- .../presentation/widgets/time_picker_widget.dart | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart index daa9c4af..628921d6 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart @@ -7,12 +7,12 @@ import 'package:syncrow_web/utils/color_manager.dart'; class TimePickerWidget extends StatefulWidget { final String title; - const TimePickerWidget({ + TimePickerWidget({ super.key, required this.onTimePicked, required this.title, }); - + late NonBookableSpacesBloc nonBookableSpacesBloc; final void Function(TimeOfDay? timePicked) onTimePicked; @override State createState() => _TimePickerWidgetState(); @@ -20,6 +20,13 @@ class TimePickerWidget extends StatefulWidget { class _TimePickerWidgetState extends State { TimeOfDay? timePicked; + @override + void initState() { + widget.nonBookableSpacesBloc = context.read(); + + super.initState(); + } + @override Widget build(BuildContext context) { return InkWell( @@ -42,7 +49,7 @@ class _TimePickerWidgetState extends State { ); widget.onTimePicked(tempTime); timePicked = tempTime; - context.read().add(CheckConfigurValidityEvent()); + widget.nonBookableSpacesBloc.add(CheckConfigurValidityEvent()); setState(() {}); }, child: Container( From bee4e054049ce0c502d9aff8d31355d6031e4d5b Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Wed, 16 Jul 2025 12:10:11 +0300 Subject: [PATCH 32/71] delete unused route --- lib/utils/constants/routes_const.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/utils/constants/routes_const.dart b/lib/utils/constants/routes_const.dart index 19116f17..83b1896f 100644 --- a/lib/utils/constants/routes_const.dart +++ b/lib/utils/constants/routes_const.dart @@ -7,5 +7,4 @@ class RoutesConst { static const String spacesManagementPage = '/spaces_management-page'; static const String rolesAndPermissions = '/roles_and_Permissions-page'; static const String analytics = '/syncrow_analytics'; - static const String manageBookableSapcesPage = '/manage_bookable_spaces'; } From 9d130139f7407a567b5bf2b1bbd8aed7c10360f2 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Wed, 16 Jul 2025 12:17:09 +0300 Subject: [PATCH 33/71] return true refactor code --- lib/utils/string_utils.dart | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/utils/string_utils.dart b/lib/utils/string_utils.dart index b1424412..5df37e20 100644 --- a/lib/utils/string_utils.dart +++ b/lib/utils/string_utils.dart @@ -11,11 +11,7 @@ bool isEndTimeAfterStartTime(TimeOfDay start, TimeOfDay end) { final startMinutes = start.hour * 60 + start.minute; final endMinutes = end.hour * 60 + end.minute; - if (endMinutes <= startMinutes) { - return true; - } - - return false; + return endMinutes <= startMinutes; } String formatTimeOfDayTo24HourString(TimeOfDay time) { From 0e4d37ccac4e30272cf2513a3865e1d85e12342f Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 17 Jul 2025 14:50:29 +0300 Subject: [PATCH 34/71] break down nonBokable Bloc into two blocs --- .../non_bookaable_spaces_bloc.dart | 87 +------------------ .../non_bookaable_spaces_event.dart | 25 ------ .../non_bookaable_spaces_state.dart | 7 -- .../setup_bookable_spaces_bloc.dart | 78 +++++++++++++++++ .../setup_bookable_spaces_event.dart | 32 +++++++ .../setup_bookable_spaces_state.dart | 39 +++++++++ .../toggle_points_switch_cubit.dart | 18 ++++ .../toggle_points_switch_state.dart | 14 +++ .../screens/setup_bookable_spaces_dialog.dart | 40 +++++---- .../widgets/check_box_space_widget.dart | 72 ++++++++++----- 10 files changed, 254 insertions(+), 158 deletions(-) create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_event.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_state.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_state.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart index 6b9852ee..608a44ff 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart @@ -1,9 +1,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; @@ -13,24 +11,13 @@ part 'non_bookaable_spaces_state.dart'; class NonBookableSpacesBloc extends Bloc { NonBookableSpacesService nonBookableSpacesService; - List selectedBookableSpaces = []; + NonBookableSpacesBloc(this.nonBookableSpacesService) : super(NonBookableSpacesInitial()) { on(_onCallInitStateEvent); on(_onLoadUnBookableSpacesEvent); - on(_onAddToBookableSpaceEvent); - on(_onRemoveFromBookableSpaceEvent); - on(_onSendBookableSpacesToApi); - on(_onCheckConfigurValidityEvent); - on(_onEditModeSelected); } - TimeOfDay? get endTime => - selectedBookableSpaces.first.spaceConfig!.bookingEndTime; - - TimeOfDay? get startTime => - selectedBookableSpaces.first.spaceConfig!.bookingStartTime; - void _onCallInitStateEvent( CallInitStateEvent event, Emitter emit) { emit(NonBookableSpacesInitial()); @@ -75,76 +62,4 @@ class NonBookableSpacesBloc } } } - - void _onAddToBookableSpaceEvent( - AddToBookableSpaceEvent event, - Emitter emit, - ) { - if (state is NonBookableSpacesLoaded) { - final currentState = state as NonBookableSpacesLoaded; - emit(AddNonBookableSpaceIntoBookableState()); - final updatedSelectedSpaces = - List.from(currentState.selectedBookableSpaces) - ..add(event.nonBookableSpace); - - selectedBookableSpaces.add(event.nonBookableSpace); - - emit( - NonBookableSpacesLoaded( - nonBookableSpaces: currentState.nonBookableSpaces, - selectedBookableSpaces: updatedSelectedSpaces, - ), - ); - } - } - - void _onRemoveFromBookableSpaceEvent(RemoveFromBookableSpaceEvent event, - Emitter emit) { - if (state is NonBookableSpacesLoaded) { - final currentState = state as NonBookableSpacesLoaded; - emit(RemoveBookableSpaceIntoNonBookableState()); - if (currentState.selectedBookableSpaces.isNotEmpty) { - currentState.selectedBookableSpaces.remove(event.bookableSpace); - } - selectedBookableSpaces.remove(event.bookableSpace); - emit( - NonBookableSpacesLoaded( - nonBookableSpaces: currentState.nonBookableSpaces, - selectedBookableSpaces: currentState.selectedBookableSpaces, - ), - ); - } - } - - Future _onSendBookableSpacesToApi(SendBookableSpacesToApi event, - Emitter emit) async { - emit(const NonBookableSpacesLoading()); - try { - await nonBookableSpacesService.sendBookableSpacesToApi( - SendBookableSpacesToApiParams.fromBookableSpacesModel( - selectedBookableSpaces, - ), - ); - add(CallInitStateEvent()); - } catch (e) { - emit( - NonBookableSpacesError(e.toString()), - ); - } - } - - void _onCheckConfigurValidityEvent( - CheckConfigurValidityEvent event, Emitter emit) { - if (selectedBookableSpaces.first.spaceConfig!.isValid) { - emit(ValidSaveButtonState()); - } else { - emit(UnValidSaveButtonState()); - } - } - - void _onEditModeSelected( - EditModeSelected event, Emitter emit) { - selectedBookableSpaces.clear(); - selectedBookableSpaces.add(event.editingBookableSpace); - } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart index f3000560..e185fae6 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_event.dart @@ -15,28 +15,3 @@ class LoadUnBookableSpacesEvent extends NonBookableSpacesEvent { required this.nonBookableSpacesParams, }); } - -class AddToBookableSpaceEvent extends NonBookableSpacesEvent { - final BookableSpacemodel nonBookableSpace; - const AddToBookableSpaceEvent({ - required this.nonBookableSpace, - }); -} - -class RemoveFromBookableSpaceEvent extends NonBookableSpacesEvent { - final BookableSpacemodel bookableSpace; - const RemoveFromBookableSpaceEvent({ - required this.bookableSpace, - }); -} - -class SendBookableSpacesToApi extends NonBookableSpacesEvent {} - -class CheckConfigurValidityEvent extends NonBookableSpacesEvent {} - -class EditModeSelected extends NonBookableSpacesEvent { - final BookableSpacemodel editingBookableSpace; - const EditModeSelected({ - required this.editingBookableSpace, - }); -} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_state.dart index b8d7a518..a5804f9d 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_state.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_state.dart @@ -30,10 +30,3 @@ class NonBookableSpacesError extends NonBookableSpacesState { const NonBookableSpacesError(this.error); } -class AddNonBookableSpaceIntoBookableState extends NonBookableSpacesState {} - -class RemoveBookableSpaceIntoNonBookableState extends NonBookableSpacesState {} - -class ValidSaveButtonState extends NonBookableSpacesState {} - -class UnValidSaveButtonState extends NonBookableSpacesState {} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart new file mode 100644 index 00000000..6787f0c1 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart @@ -0,0 +1,78 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; + +part 'setup_bookable_spaces_event.dart'; +part 'setup_bookable_spaces_state.dart'; + +class SetupBookableSpacesBloc + extends Bloc { + NonBookableSpacesService nonBookableSpacesService; + List selectedBookableSpaces = []; + SetupBookableSpacesBloc(this.nonBookableSpacesService) + : super(SetupBookableSpacesInitial()) { + on(_onAddToBookableSpaceEvent); + on(_onRemoveFromBookableSpaceEvent); + on(_onSendBookableSpacesToApi); + on(_onCheckConfigurValidityEvent); + on(_onEditModeSelected); + } + TimeOfDay? get endTime => + selectedBookableSpaces.first.spaceConfig!.bookingEndTime; + + TimeOfDay? get startTime => + selectedBookableSpaces.first.spaceConfig!.bookingStartTime; + + void _onAddToBookableSpaceEvent( + AddToBookableSpaceEvent event, + Emitter emit, + ) { + emit(InProgressState()); + selectedBookableSpaces.add(event.nonBookableSpace); + emit(AddNonBookableSpaceIntoBookableState( + bookableSpaces: selectedBookableSpaces)); + } + + void _onRemoveFromBookableSpaceEvent(RemoveFromBookableSpaceEvent event, + Emitter emit) { + emit(InProgressState()); + selectedBookableSpaces.remove(event.bookableSpace); + emit(RemoveBookableSpaceIntoNonBookableState( + bookableSpaces: selectedBookableSpaces)); + } + + Future _onSendBookableSpacesToApi(SendBookableSpacesToApi event, + Emitter emit) async { + emit(SendBookableSpacesLoading()); + try { + await nonBookableSpacesService.sendBookableSpacesToApi( + SendBookableSpacesToApiParams.fromBookableSpacesModel( + selectedBookableSpaces, + ), + ); + emit(SendBookableSpacesSuccess()); + } catch (e) { + emit( + SendBookableSpacesError(e.toString()), + ); + } + } + + void _onCheckConfigurValidityEvent(CheckConfigurValidityEvent event, + Emitter emit) { + if (selectedBookableSpaces.first.spaceConfig!.isValid) { + emit(ValidSaveButtonState()); + } else { + emit(UnValidSaveButtonState()); + } + } + + void _onEditModeSelected( + EditModeSelected event, Emitter emit) { + selectedBookableSpaces.clear(); + selectedBookableSpaces.add(event.editingBookableSpace); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_event.dart new file mode 100644 index 00000000..d6fe715c --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_event.dart @@ -0,0 +1,32 @@ +part of 'setup_bookable_spaces_bloc.dart'; + +sealed class SetupBookableSpacesEvent extends Equatable { + const SetupBookableSpacesEvent(); + + @override + List get props => []; +} +class AddToBookableSpaceEvent extends SetupBookableSpacesEvent { + final BookableSpacemodel nonBookableSpace; + const AddToBookableSpaceEvent({ + required this.nonBookableSpace, + }); +} + +class RemoveFromBookableSpaceEvent extends SetupBookableSpacesEvent { + final BookableSpacemodel bookableSpace; + const RemoveFromBookableSpaceEvent({ + required this.bookableSpace, + }); +} + +class SendBookableSpacesToApi extends SetupBookableSpacesEvent {} + +class CheckConfigurValidityEvent extends SetupBookableSpacesEvent {} + +class EditModeSelected extends SetupBookableSpacesEvent { + final BookableSpacemodel editingBookableSpace; + const EditModeSelected({ + required this.editingBookableSpace, + }); +} \ No newline at end of file diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_state.dart new file mode 100644 index 00000000..d8f1f4f3 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_state.dart @@ -0,0 +1,39 @@ +part of 'setup_bookable_spaces_bloc.dart'; + +sealed class SetupBookableSpacesState extends Equatable { + const SetupBookableSpacesState(); + + @override + List get props => []; +} + +final class SetupBookableSpacesInitial extends SetupBookableSpacesState {} + +class AddNonBookableSpaceIntoBookableState extends SetupBookableSpacesState { + final List bookableSpaces; + const AddNonBookableSpaceIntoBookableState({ + required this.bookableSpaces, + }); +} + +class InProgressState extends SetupBookableSpacesState {} + +class RemoveBookableSpaceIntoNonBookableState extends SetupBookableSpacesState { + final List bookableSpaces; + const RemoveBookableSpaceIntoNonBookableState({ + required this.bookableSpaces, + }); +} + +class ValidSaveButtonState extends SetupBookableSpacesState {} + +class UnValidSaveButtonState extends SetupBookableSpacesState {} + +class SendBookableSpacesLoading extends SetupBookableSpacesState {} + +class SendBookableSpacesSuccess extends SetupBookableSpacesState {} + +class SendBookableSpacesError extends SetupBookableSpacesState { + final String error; + const SendBookableSpacesError(this.error); +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart new file mode 100644 index 00000000..0b0f11f0 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart @@ -0,0 +1,18 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; + +part 'toggle_points_switch_state.dart'; + +class TogglePointsSwitchCubit extends Cubit { + TogglePointsSwitchCubit() : super(TogglePointsSwitchInitial()); + bool switchValue = true; + void activateSwitch() { + switchValue = true; + emit(ActivatePointsSwitch()); + } + + void unActivateSwitch() { + switchValue = false; + emit(UnActivatePointsSwitch()); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_state.dart new file mode 100644 index 00000000..872ae8dc --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_state.dart @@ -0,0 +1,14 @@ +part of 'toggle_points_switch_cubit.dart'; + +sealed class TogglePointsSwitchState extends Equatable { + const TogglePointsSwitchState(); + + @override + List get props => []; +} + +final class TogglePointsSwitchInitial extends TogglePointsSwitchState {} + +class ActivatePointsSwitch extends TogglePointsSwitchState {} + +class UnActivatePointsSwitch extends TogglePointsSwitchState {} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart index ec3db30a..3b72c30c 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart @@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart'; @@ -42,22 +43,25 @@ class _SetupBookableSpacesDialogState extends State { : (context) => StepsCubit()..editValueInit(), ), BlocProvider( - create: widget.editingBookableSpace == null - ? (context) => NonBookableSpacesBloc( - RemoteNonBookableSpaces(HTTPService()), - )..add( - LoadUnBookableSpacesEvent( - nonBookableSpacesParams: - NonBookableSpacesParams(currentPage: 1), - ), - ) - : (context) => NonBookableSpacesBloc( - RemoteNonBookableSpaces(HTTPService()), - )..add( - EditModeSelected( - editingBookableSpace: widget.editingBookableSpace!), - ), + create: (context) => NonBookableSpacesBloc( + RemoteNonBookableSpaces(HTTPService()), + )..add( + LoadUnBookableSpacesEvent( + nonBookableSpacesParams: + NonBookableSpacesParams(currentPage: 1), + ), + ), ), + BlocProvider( + create: widget.editingBookableSpace == null + ? (context) => SetupBookableSpacesBloc( + RemoteNonBookableSpaces(HTTPService())) + : (context) => SetupBookableSpacesBloc( + RemoteNonBookableSpaces(HTTPService())) + ..add(EditModeSelected( + editingBookableSpace: widget.editingBookableSpace!, + )), + ) ], child: AlertDialog( backgroundColor: ColorsManager.whiteColors, @@ -101,8 +105,10 @@ class _SetupBookableSpacesDialogState extends State { ), Builder(builder: (context) { final stepsState = context.watch().state; - final nonBookableBloc = context.watch(); - final selectedSpaces = nonBookableBloc.selectedBookableSpaces; + final setupBookableSpacesBloc = + context.watch(); + final selectedSpaces = + setupBookableSpacesBloc.selectedBookableSpaces; return stepsState is StepOneState ? NextFirstStepButton(selectedSpaces: selectedSpaces) : SaveSecondStepButton( diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart index b5610166..e37c8e0e 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; class CheckBoxSpaceWidget extends StatelessWidget { final BookableSpacemodel nonBookableSpace; @@ -15,33 +16,58 @@ class CheckBoxSpaceWidget extends StatelessWidget { @override Widget build(BuildContext context) { - final isChecked = selectedSpaces.any( - (element) => element.spaceUuid == nonBookableSpace.spaceUuid, - ); - return Row( children: [ - Checkbox( - value: isChecked, - onChanged: (value) { - final bloc = context.read(); - if (value ?? false) { - bloc.add( - AddToBookableSpaceEvent( - nonBookableSpace: nonBookableSpace, - ), - ); - } else { - bloc.add( - RemoveFromBookableSpaceEvent( - bookableSpace: nonBookableSpace, - ), - ); - } + BlocBuilder( + builder: (context, state) { + final isChecked = switch (state) { + AddNonBookableSpaceIntoBookableState( + bookableSpaces: final spaces + ) => + spaces.any((s) => s.spaceUuid == nonBookableSpace.spaceUuid), + RemoveBookableSpaceIntoNonBookableState( + bookableSpaces: final spaces + ) => + spaces.any((s) => s.spaceUuid == nonBookableSpace.spaceUuid), + _ => false, + }; + + return Checkbox( + value: isChecked, + onChanged: (value) { + final bloc = context.read(); + if (value ?? false) { + bloc.add(AddToBookableSpaceEvent( + nonBookableSpace: nonBookableSpace)); + } else { + bloc.add(RemoveFromBookableSpaceEvent( + bookableSpace: nonBookableSpace)); + } + }, + ); }, ), const SizedBox(width: 5), - Expanded(child: Text(nonBookableSpace.spaceName)), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + nonBookableSpace.spaceName, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: ColorsManager.textGray, + ), + ), + Text( + nonBookableSpace.spaceVirtualAddress, + style: const TextStyle( + fontSize: 12, + color: ColorsManager.textGray, + ), + ), + ], + )), ], ); } From 9396f37fea6cee0540f10af47551d4b51b7d6711 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 17 Jul 2025 14:51:07 +0300 Subject: [PATCH 35/71] refactor code --- .../cubit/toggle_points_switch_cubit.dart | 18 ----------------- .../cubit/toggle_points_switch_state.dart | 14 ------------- .../widgets/booking_period_widget.dart | 20 ++++++++++--------- .../widgets/next_first_step_button.dart | 4 ++-- .../widgets/points_part_widget.dart | 16 +++++++-------- .../widgets/save_second_step_button.dart | 18 ++++++++++++++--- .../widgets/step_two_details_widget.dart | 2 +- .../widgets/time_picker_widget.dart | 8 ++++---- .../widgets/unbookable_list_widget.dart | 7 ++++--- .../widgets/week_checkbox_title_widget.dart | 6 +++--- 10 files changed, 48 insertions(+), 65 deletions(-) delete mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart delete mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_state.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart deleted file mode 100644 index 0b0f11f0..00000000 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:bloc/bloc.dart'; -import 'package:equatable/equatable.dart'; - -part 'toggle_points_switch_state.dart'; - -class TogglePointsSwitchCubit extends Cubit { - TogglePointsSwitchCubit() : super(TogglePointsSwitchInitial()); - bool switchValue = true; - void activateSwitch() { - switchValue = true; - emit(ActivatePointsSwitch()); - } - - void unActivateSwitch() { - switchValue = false; - emit(UnActivatePointsSwitch()); - } -} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_state.dart deleted file mode 100644 index 872ae8dc..00000000 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_state.dart +++ /dev/null @@ -1,14 +0,0 @@ -part of 'toggle_points_switch_cubit.dart'; - -sealed class TogglePointsSwitchState extends Equatable { - const TogglePointsSwitchState(); - - @override - List get props => []; -} - -final class TogglePointsSwitchInitial extends TogglePointsSwitchState {} - -class ActivatePointsSwitch extends TogglePointsSwitchState {} - -class UnActivatePointsSwitch extends TogglePointsSwitchState {} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart index f3f2d5cc..0f9be036 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart @@ -2,7 +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/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -50,11 +50,12 @@ class BookingPeriodWidget extends StatelessWidget { if (timePicked == null) { return; } - final nonBookableBloc = context.read(); + final setupBookableSpacesBloc = + context.read(); - if (nonBookableBloc.endTime != null && + if (setupBookableSpacesBloc.endTime != null && isEndTimeAfterStartTime( - timePicked, nonBookableBloc.endTime!)) { + timePicked, setupBookableSpacesBloc.endTime!)) { ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: @@ -64,7 +65,7 @@ class BookingPeriodWidget extends StatelessWidget { )); throw Exception(); } else { - nonBookableBloc.selectedBookableSpaces.forEach( + setupBookableSpacesBloc.selectedBookableSpaces.forEach( (e) => e.spaceConfig!.bookingStartTime = timePicked, ); } @@ -83,10 +84,11 @@ class BookingPeriodWidget extends StatelessWidget { if (timePicked == null) { return; } - final nonBookableBloc = context.read(); - if (nonBookableBloc.startTime != null && + final setupBookableSpacesBloc = + context.read(); + if (setupBookableSpacesBloc.startTime != null && isEndTimeAfterStartTime( - nonBookableBloc.startTime!, timePicked)) { + setupBookableSpacesBloc.startTime!, timePicked)) { ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: @@ -96,7 +98,7 @@ class BookingPeriodWidget extends StatelessWidget { )); throw Exception(); } else { - nonBookableBloc.selectedBookableSpaces.forEach( + setupBookableSpacesBloc.selectedBookableSpaces.forEach( (e) => e.spaceConfig!.bookingEndTime = timePicked, ); } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart index bf1f1af6..23c759a8 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart'; @@ -23,7 +23,7 @@ class NextFirstStepButton extends StatelessWidget { : () { context.read().goToNextStep(); context - .read() + .read() .add(CheckConfigurValidityEvent()); }, onCancelPressed: () => context.pop(), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart index f62e4941..97dad088 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -78,13 +78,13 @@ class _PointsPartWidgetState extends State { .read() .activateSwitch(); context - .read() + .read() .selectedBookableSpaces .forEach( (e) => e.spaceConfig!.cost = -1, ); context - .read() + .read() .add(CheckConfigurValidityEvent()); } else { context @@ -92,13 +92,13 @@ class _PointsPartWidgetState extends State { .unActivateSwitch(); widget.pointsController.clear(); context - .read() + .read() .selectedBookableSpaces .forEach( (e) => e.spaceConfig!.cost = 0, ); context - .read() + .read() .add(CheckConfigurValidityEvent()); } }, @@ -115,7 +115,7 @@ class _PointsPartWidgetState extends State { height: 40, onChanged: (p0) { context - .read() + .read() .selectedBookableSpaces .forEach( (e) => e.spaceConfig!.cost = int.parse( @@ -125,7 +125,7 @@ class _PointsPartWidgetState extends State { ), ); context - .read() + .read() .add(CheckConfigurValidityEvent()); }, controller: widget.pointsController, diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart index b2f6dc33..67b88d32 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart @@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart'; @@ -21,7 +22,18 @@ class SaveSecondStepButton extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocConsumer( + listener: (context, state) { + if (state is SendBookableSpacesSuccess) { + context.read().add(CallInitStateEvent()); + } else if (state is SendBookableSpacesError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.error), + ), + ); + } + }, builder: (context, state) { return ButtonsDividerBottomDialogWidget( title: 'Save', @@ -33,7 +45,7 @@ class SaveSecondStepButton extends StatelessWidget { )) { isEditingMode ? callEditLogic(context) - : context.read().add( + : context.read().add( SendBookableSpacesToApi(), ); } @@ -51,7 +63,7 @@ class SaveSecondStepButton extends StatelessWidget { context.read().add(CallInitStateEvent()), updatedParams: UpdateBookableSpaceParam.fromBookableModel( context - .read() + .read() .selectedBookableSpaces .first, ), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart index d4177a77..bd5263fe 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/cubit/toggle_points_switch_cubit.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart'; diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart index 628921d6..da94341b 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -12,7 +12,7 @@ class TimePickerWidget extends StatefulWidget { required this.onTimePicked, required this.title, }); - late NonBookableSpacesBloc nonBookableSpacesBloc; + late SetupBookableSpacesBloc setupBookableSpacesBloc; final void Function(TimeOfDay? timePicked) onTimePicked; @override State createState() => _TimePickerWidgetState(); @@ -22,7 +22,7 @@ class _TimePickerWidgetState extends State { TimeOfDay? timePicked; @override void initState() { - widget.nonBookableSpacesBloc = context.read(); + widget.setupBookableSpacesBloc = context.read(); super.initState(); } @@ -49,7 +49,7 @@ class _TimePickerWidgetState extends State { ); widget.onTimePicked(tempTime); timePicked = tempTime; - widget.nonBookableSpacesBloc.add(CheckConfigurValidityEvent()); + widget.setupBookableSpacesBloc.add(CheckConfigurValidityEvent()); setState(() {}); }, child: Container( diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart index 92bb66e3..225514bb 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; @@ -36,8 +36,9 @@ class UnbookableListWidget extends StatelessWidget { if (index < nonBookableSpaces.data.length) { return CheckBoxSpaceWidget( nonBookableSpace: nonBookableSpaces.data[index], - selectedSpaces: - context.read().selectedBookableSpaces, + selectedSpaces: context + .read() + .selectedBookableSpaces, ); } else { return const Padding( diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart index 77ad2351..28f3b4c0 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; class WeekDaysCheckboxRow extends StatefulWidget { final BookableSpacemodel? editingBookableSpace; @@ -58,14 +58,14 @@ class _WeekDaysCheckboxRowState extends State { .toList(); for (var space in context - .read() + .read() .selectedBookableSpaces) { space.spaceConfig!.bookableDays = selectedDays; } }); context - .read() + .read() .add(CheckConfigurValidityEvent()); }, ), From 04fcf0a14006728f3dece68f95fce47573b528a6 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 17 Jul 2025 15:03:22 +0300 Subject: [PATCH 36/71] break down Massive widgets into smaller ones --- ...okable_space_switch_activation_widget.dart | 71 ++++++++ .../edit_bookable_space_button_widget.dart | 59 +++++++ .../table_part_widget.dart | 160 +++++------------- 3 files changed, 170 insertions(+), 120 deletions(-) create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart new file mode 100644 index 00000000..4c88fe87 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class BookableSpaceSwitchActivationWidget extends StatelessWidget { + final PaginatedDataModel bookableSpaces; + final BookableSpacemodel space; + const BookableSpaceSwitchActivationWidget({ + super.key, + required this.bookableSpaces, + required this.space, + }); + + @override + Widget build(BuildContext context) { + return Center( + child: Transform.scale( + scale: 0.7, + child: + BlocConsumer( + listener: (context, updateState) { + if (updateState is UpdateBookableSpaceSuccess) { + context.read().add( + InsertUpdatedSpaceEvent( + bookableSpaces: bookableSpaces, + bookableSpace: space, + updatedBookableSpaceConfig: + updateState.bookableSpaceConfig, + ), + ); + } + }, + builder: (context, updateState) { + final isLoading = updateState is UpdateBookableSpaceLoading && + updateState.updatingSpaceUuid == space.spaceUuid; + if (isLoading) { + return const Center(child: CircularProgressIndicator()); + } + return Switch( + trackOutlineColor: WidgetStateProperty.resolveWith( + (Set states) { + return ColorsManager.whiteColors; + }), + value: space.spaceConfig!.availability, + activeTrackColor: ColorsManager.blueColor, + inactiveTrackColor: ColorsManager.grayBorder, + thumbColor: WidgetStateProperty.resolveWith( + (Set states) { + return ColorsManager.whiteColors; + }), + onChanged: (value) { + context.read().add( + UpdateBookableSpace( + updatedParams: UpdateBookableSpaceParam( + spaceUuid: space.spaceUuid, + availability: value, + )), + ); + }, + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart new file mode 100644 index 00000000..8f82e08d --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_update_bookable_space_service.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class EditBookableSpaceButtonWidget extends StatelessWidget { + final BookableSpacemodel? space; + const EditBookableSpaceButtonWidget({ + super.key, + required this.space, + }); + + @override + Widget build(BuildContext context) { + return Center( + child: ElevatedButton( + onPressed: () { + final bookableBloc = context.read(); + + showDialog( + context: context, + builder: (context) => MultiBlocProvider( + providers: [ + BlocProvider.value( + value: bookableBloc, + ), + BlocProvider( + create: (context) => UpdateBookableSpacesBloc( + RemoteUpdateBookableSpaceService(HTTPService()), + ), + ), + ], + child: SetupBookableSpacesDialog( + editingBookableSpace: space, + ), + ), + ); + }, + style: ElevatedButton.styleFrom( + padding: EdgeInsets.zero, + fixedSize: const Size(50, 30), + elevation: 1, + ), + child: SvgPicture.asset( + Assets.settings, + height: 15, + color: ColorsManager.blue1, + ), + ), + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart index 0f1ddc4b..a0ce8ace 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart @@ -1,17 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_update_bookable_space_service.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_data_table.dart'; -import 'package:syncrow_web/services/api/http_service.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart'; class TableOfBookableSpacesWidget extends StatelessWidget { const TableOfBookableSpacesWidget({ @@ -43,12 +37,9 @@ class TableOfBookableSpacesWidget extends StatelessWidget { items: state.bookableSpacesList.data, cellsWidgets: (space) => [ DataCell( - Padding( - padding: const EdgeInsetsGeometry.only(left: 10), - child: Text( - space.spaceName, - style: const TextStyle(fontSize: 11), - )), + DataCellWidget( + title: space.spaceName, + ), ), DataCell(Padding( padding: const EdgeInsetsGeometry.only(left: 10), @@ -62,123 +53,33 @@ class TableOfBookableSpacesWidget extends StatelessWidget { child: Wrap( spacing: 4, children: space.spaceConfig!.bookableDays - .map((day) => Text( - day, - style: const TextStyle(fontSize: 11), - )) + .map( + (day) => DataCellWidget(title: day), + ) .toList(), ), )), DataCell( - Padding( - padding: const EdgeInsetsGeometry.only(left: 10), - child: Text( - space.spaceConfig!.bookingStartTime!.format(context), - style: const TextStyle(fontSize: 11), - ), + DataCellWidget( + title: space.spaceConfig!.bookingStartTime!.format(context), ), ), DataCell( - Padding( - padding: const EdgeInsetsGeometry.only(left: 10), - child: Text( - space.spaceConfig!.bookingEndTime!.format(context), - style: const TextStyle(fontSize: 11), - ), + DataCellWidget( + title: space.spaceConfig!.bookingEndTime!.format(context), ), ), - DataCell(Padding( - padding: const EdgeInsetsGeometry.only(left: 10), - child: Text( - '${space.spaceConfig!.cost} Points', - style: const TextStyle(fontSize: 11), - ))), - DataCell(Center( - child: Transform.scale( - scale: 0.7, - child: BlocConsumer( - listener: (context, updateState) { - if (updateState is UpdateBookableSpaceSuccess) { - context.read().add( - InsertUpdatedSpaceEvent( - bookableSpaces: state.bookableSpacesList, - bookableSpace: space, - updatedBookableSpaceConfig: - updateState.bookableSpaceConfig, - ), - ); - } - }, - builder: (context, updateState) { - final isLoading = - updateState is UpdateBookableSpaceLoading && - updateState.updatingSpaceUuid == space.spaceUuid; - if (isLoading) { - return const Center(child: CircularProgressIndicator()); - } - return Switch( - trackOutlineColor: - WidgetStateProperty.resolveWith( - (Set states) { - return ColorsManager.whiteColors; - }), - value: space.spaceConfig!.availability, - activeTrackColor: ColorsManager.blueColor, - inactiveTrackColor: ColorsManager.grayBorder, - thumbColor: WidgetStateProperty.resolveWith( - (Set states) { - return ColorsManager.whiteColors; - }), - onChanged: (value) { - context.read().add( - UpdateBookableSpace( - updatedParams: UpdateBookableSpaceParam( - spaceUuid: space.spaceUuid, - availability: value, - )), - ); - }, - ); - }, - ), + DataCell( + DataCellWidget( + title: '${space.spaceConfig!.cost} Points', ), + ), + DataCell(BookableSpaceSwitchActivationWidget( + bookableSpaces: state.bookableSpacesList, + space: space, )), - DataCell(Center( - child: ElevatedButton( - onPressed: () { - final bookableBloc = context.read(); - - showDialog( - context: context, - builder: (context) => MultiBlocProvider( - providers: [ - BlocProvider.value( - value: bookableBloc, - ), - BlocProvider( - create: (context) => UpdateBookableSpacesBloc( - RemoteUpdateBookableSpaceService(HTTPService()), - ), - ), - ], - child: SetupBookableSpacesDialog( - editingBookableSpace: space, - ), - ), - ); - }, - style: ElevatedButton.styleFrom( - padding: EdgeInsets.zero, - fixedSize: const Size(50, 30), - elevation: 1, - ), - child: SvgPicture.asset( - Assets.settings, - height: 15, - color: ColorsManager.blue1, - ), - ), + DataCell(EditBookableSpaceButtonWidget( + space: space, )), ], columnsTitles: const [ @@ -199,3 +100,22 @@ class TableOfBookableSpacesWidget extends StatelessWidget { ); } } + +class DataCellWidget extends StatelessWidget { + final String title; + const DataCellWidget({ + super.key, + required this.title, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsetsGeometry.only(left: 10), + child: Text( + title, + style: const TextStyle(fontSize: 11), + ), + ); + } +} From 0cb287567276016ed5f95d74cb944445a01ecc59 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 17 Jul 2025 17:41:22 +0300 Subject: [PATCH 37/71] fix UI to fit with Figma --- .../presentation/view/widgets/icon_text_button.dart | 8 +++++++- .../screens/manage_bookable_spaces_screen.dart | 7 +++++-- .../main_manage_bookable_widgets/top_part_widget.dart | 3 +++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart index c49d6fd7..32201ed5 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart @@ -4,6 +4,8 @@ import 'package:syncrow_web/utils/color_manager.dart'; class SvgTextButton extends StatelessWidget { final String svgAsset; + final double? horizontalPadding; + final double? verticalPadding; final String label; final VoidCallback onPressed; final Color backgroundColor; @@ -25,6 +27,8 @@ class SvgTextButton extends StatelessWidget { this.svgColor = const Color(0xFF496EFF), this.labelColor = Colors.black, this.borderRadius = 10.0, + this.horizontalPadding, + this.verticalPadding, this.boxShadow = const [ BoxShadow( color: ColorsManager.lightGrayColor, @@ -43,7 +47,9 @@ class SvgTextButton extends StatelessWidget { borderRadius: BorderRadius.circular(borderRadius), onTap: onPressed, child: Container( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + padding: EdgeInsets.symmetric( + horizontal: horizontalPadding ?? 24, + vertical: verticalPadding ?? 12), decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(borderRadius), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart index 78911114..11c43cdb 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart @@ -61,7 +61,10 @@ class ManageBookableSpacesWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: const EdgeInsets.all(20), + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 35, + ), child: Column( children: [ Expanded( @@ -75,7 +78,7 @@ class ManageBookableSpacesWidget extends StatelessWidget { child: TableOfBookableSpacesWidget(), ), const SizedBox( - height: 5, + height: 15, ), const Expanded( flex: 5, diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart index 1778ad19..2460cda0 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart @@ -26,6 +26,7 @@ class RowOfButtonsTitleWidget extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + padding: EdgeInsets.zero, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), @@ -52,6 +53,8 @@ class RowOfButtonsTitleWidget extends StatelessWidget { ], ), SvgTextButton( + verticalPadding: 10, + horizontalPadding: 10, svgSize: 15, fontSize: 10, fontWeight: FontWeight.bold, From 0ad562b6cebd85860d5fc90a70b98fd119104509 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Sun, 20 Jul 2025 14:03:50 +0300 Subject: [PATCH 38/71] add icons and types for devices did not added before --- assets/images/4_sceen_switch.svg | 9 +++++++++ assets/images/6_sceen_switch.svg | 12 ++++++++++++ .../device_managment/gateway/view/gateway_view.dart | 1 + .../widgets/space_details_devices_box.dart | 7 ++++++- lib/pages/visitor_password/model/device_model.dart | 8 ++++++++ lib/utils/constants/assets.dart | 2 ++ lib/utils/enum/device_types.dart | 6 ++++++ 7 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 assets/images/4_sceen_switch.svg create mode 100644 assets/images/6_sceen_switch.svg diff --git a/assets/images/4_sceen_switch.svg b/assets/images/4_sceen_switch.svg new file mode 100644 index 00000000..3765e137 --- /dev/null +++ b/assets/images/4_sceen_switch.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/images/6_sceen_switch.svg b/assets/images/6_sceen_switch.svg new file mode 100644 index 00000000..fef2291b --- /dev/null +++ b/assets/images/6_sceen_switch.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/lib/pages/device_managment/gateway/view/gateway_view.dart b/lib/pages/device_managment/gateway/view/gateway_view.dart index d674e4d8..a2476cb8 100644 --- a/lib/pages/device_managment/gateway/view/gateway_view.dart +++ b/lib/pages/device_managment/gateway/view/gateway_view.dart @@ -100,6 +100,7 @@ class _DeviceItem extends StatelessWidget { @override Widget build(BuildContext context) { + print(device.icon); return DeviceControlsContainer( child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_devices_box.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_devices_box.dart index cf65dbb6..4c8fec4f 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_devices_box.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_devices_box.dart @@ -103,7 +103,9 @@ class SpaceDetailsDevicesBox extends StatelessWidget { ).then((resultSpace) { if (resultSpace != null) { if (context.mounted) { - context.read().add(UpdateSpaceDetails(resultSpace)); + context + .read() + .add(UpdateSpaceDetails(resultSpace)); } } }); @@ -133,6 +135,9 @@ class SpaceDetailsDevicesBox extends StatelessWidget { DeviceType.ThreeTouch => Assets.gangSwitch, DeviceType.NCPS => Assets.sensors, DeviceType.PC => Assets.powerClamp, + DeviceType.fourSceen => Assets.fourSceenSwitch, + DeviceType.sixSceen => Assets.sixSceenSwitch, + DeviceType.SOS => Assets.sos, DeviceType.Other => Assets.blackLogo, null => Assets.blackLogo, }; diff --git a/lib/pages/visitor_password/model/device_model.dart b/lib/pages/visitor_password/model/device_model.dart index 75d00350..99f84393 100644 --- a/lib/pages/visitor_password/model/device_model.dart +++ b/lib/pages/visitor_password/model/device_model.dart @@ -84,6 +84,14 @@ class DeviceModel { tempIcon = Assets.curtainIcon; } else if (type == DeviceType.Curtain) { tempIcon = Assets.curtainIcon; + } else if (type == DeviceType.fourSceen) { + tempIcon = Assets.fourSceenSwitch; + } else if (type == DeviceType.sixSceen) { + tempIcon = Assets.sixSceenSwitch; + } else if (type == DeviceType.SOS) { + tempIcon = Assets.sos; + } else if (type == DeviceType.NCPS) { + tempIcon = Assets.presenceSensor; } else { tempIcon = Assets.blackLogo; } diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index b7ad15b5..839ab77d 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -4,6 +4,8 @@ class Assets { static const String webBackground = 'assets/images/web_Background.svg'; static const String webBackgroundPng = 'assets/images/web_Background.png'; static const String blackLogo = 'assets/images/black-logo.png'; + static const String fourSceenSwitch = 'assets/images/4_sceen_switch.svg'; + static const String sixSceenSwitch = 'assets/images/6_sceen_switch.svg'; static const String logo = 'assets/images/Logo.svg'; static const String logoHorizontal = 'assets/images/logo_horizontal.png'; static const String vector = 'assets/images/Vector.png'; diff --git a/lib/utils/enum/device_types.dart b/lib/utils/enum/device_types.dart index 947e63aa..85b5d5fa 100644 --- a/lib/utils/enum/device_types.dart +++ b/lib/utils/enum/device_types.dart @@ -21,6 +21,9 @@ enum DeviceType { NCPS, DoorSensor, PC, + fourSceen, + sixSceen, + SOS, Other, } /* @@ -63,4 +66,7 @@ Map devicesTypesMap = { 'WL': DeviceType.WaterLeak, 'NCPS': DeviceType.NCPS, 'PC': DeviceType.PC, + '4S': DeviceType.fourSceen, + '6S': DeviceType.sixSceen, + 'SOS': DeviceType.SOS, }; From 631766e0ec52e252c63076717d46c63a19ee07fa Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 14:29:48 +0300 Subject: [PATCH 39/71] make sendApi bloc and service --- .../data/remote_non_bookable_spaces.dart | 24 ------------- .../data/remote_send_bookable_spaces.dart | 35 +++++++++++++++++++ .../service/non_bookable_spaces_service.dart | 2 -- .../service/send_bookable_spaces_service.dart | 5 +++ .../send_bookable_spaces_bloc.dart | 33 +++++++++++++++++ .../send_bookable_spaces_event.dart | 13 +++++++ .../send_bookable_spaces_state.dart | 19 ++++++++++ .../setup_bookable_spaces_bloc.dart | 20 +---------- .../setup_bookable_spaces_event.dart | 5 ++- .../setup_bookable_spaces_state.dart | 7 ---- .../screens/setup_bookable_spaces_dialog.dart | 7 ++++ .../widgets/save_second_step_button.dart | 10 ++++-- 12 files changed, 122 insertions(+), 58 deletions(-) create mode 100644 lib/pages/access_management/manage_bookable_spaces/data/remote_send_bookable_spaces.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_service.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_bloc.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_event.dart create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_state.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart index c42e8129..b9a499af 100644 --- a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart +++ b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart @@ -58,28 +58,4 @@ class RemoteNonBookableSpaces implements NonBookableSpacesService { return completer.future; } - - @override - Future sendBookableSpacesToApi( - SendBookableSpacesToApiParams params) async { - try { - await _httpService.post( - path: ApiEndpoints.bookableSpaces, - body: params.toJson(), - expectedResponseModel: (p0) {}, - ); - } on DioException catch (e) { - final message = e.response?.data as Map?; - final error = message?['error'] as Map?; - final errorMessage = error?['error'] as String? ?? ''; - final formattedErrorMessage = [ - _defaultErrorMessage, - errorMessage, - ].join(': '); - throw APIException(formattedErrorMessage); - } catch (e) { - final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); - throw APIException(formattedErrorMessage); - } - } } diff --git a/lib/pages/access_management/manage_bookable_spaces/data/remote_send_bookable_spaces.dart b/lib/pages/access_management/manage_bookable_spaces/data/remote_send_bookable_spaces.dart new file mode 100644 index 00000000..5d50fd55 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/data/remote_send_bookable_spaces.dart @@ -0,0 +1,35 @@ +import 'package:dio/dio.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + +class RemoteSendBookableSpaces implements SendBookableSpacesService { + final HTTPService _httpService; + RemoteSendBookableSpaces(this._httpService); + static const _defaultErrorMessage = 'Failed to load Spaces'; + @override + Future sendBookableSpacesToApi( + SendBookableSpacesToApiParams params) async { + try { + await _httpService.post( + path: ApiEndpoints.bookableSpaces, + body: params.toJson(), + expectedResponseModel: (p0) {}, + ); + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [ + _defaultErrorMessage, + errorMessage, + ].join(': '); + throw APIException(formattedErrorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart b/lib/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart index d4bc82fa..a5034bbf 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart @@ -1,10 +1,8 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; abstract class NonBookableSpacesService { Future> load( NonBookableSpacesParams params); - Future sendBookableSpacesToApi(SendBookableSpacesToApiParams params); } diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_service.dart b/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_service.dart new file mode 100644 index 00000000..6b3f40d5 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_service.dart @@ -0,0 +1,5 @@ +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart'; + +abstract class SendBookableSpacesService{ + Future sendBookableSpacesToApi(SendBookableSpacesToApiParams params); +} \ No newline at end of file diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_bloc.dart new file mode 100644 index 00000000..a2993db9 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_bloc.dart @@ -0,0 +1,33 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_service.dart'; + +part 'send_bookable_spaces_event.dart'; +part 'send_bookable_spaces_state.dart'; + +class SendBookableSpacesBloc + extends Bloc { + SendBookableSpacesService sendBookableSpacesService; + SendBookableSpacesBloc( + this.sendBookableSpacesService, + ) : super(SendBookableSpacesInitial()) { + on(_onSendBookableSpacesToApi); + } + Future _onSendBookableSpacesToApi(SendBookableSpacesToApi event, + Emitter emit) async { + emit(SendBookableSpacesLoading()); + try { + await sendBookableSpacesService.sendBookableSpacesToApi( + SendBookableSpacesToApiParams.fromBookableSpacesModel( + event.selectedBookableSpaces), + ); + emit(SendBookableSpacesSuccess()); + } catch (e) { + emit( + SendBookableSpacesError(e.toString()), + ); + } + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_event.dart new file mode 100644 index 00000000..3e82ea0b --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_event.dart @@ -0,0 +1,13 @@ +part of 'send_bookable_spaces_bloc.dart'; + +sealed class SendBookableSpacesEvent extends Equatable { + const SendBookableSpacesEvent(); + + @override + List get props => []; +} + +class SendBookableSpacesToApi extends SendBookableSpacesEvent { + final List selectedBookableSpaces; + const SendBookableSpacesToApi({required this.selectedBookableSpaces}); +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_state.dart new file mode 100644 index 00000000..2fce5476 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_state.dart @@ -0,0 +1,19 @@ +part of 'send_bookable_spaces_bloc.dart'; + +sealed class SendBookableSpacesState extends Equatable { + const SendBookableSpacesState(); + + @override + List get props => []; +} + +final class SendBookableSpacesInitial extends SendBookableSpacesState {} + +class SendBookableSpacesLoading extends SendBookableSpacesState {} + +class SendBookableSpacesSuccess extends SendBookableSpacesState {} + +class SendBookableSpacesError extends SendBookableSpacesState { + final String error; + const SendBookableSpacesError(this.error); +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart index 6787f0c1..2212da77 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart @@ -2,7 +2,6 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; part 'setup_bookable_spaces_event.dart'; @@ -16,7 +15,7 @@ class SetupBookableSpacesBloc : super(SetupBookableSpacesInitial()) { on(_onAddToBookableSpaceEvent); on(_onRemoveFromBookableSpaceEvent); - on(_onSendBookableSpacesToApi); + on(_onCheckConfigurValidityEvent); on(_onEditModeSelected); } @@ -44,23 +43,6 @@ class SetupBookableSpacesBloc bookableSpaces: selectedBookableSpaces)); } - Future _onSendBookableSpacesToApi(SendBookableSpacesToApi event, - Emitter emit) async { - emit(SendBookableSpacesLoading()); - try { - await nonBookableSpacesService.sendBookableSpacesToApi( - SendBookableSpacesToApiParams.fromBookableSpacesModel( - selectedBookableSpaces, - ), - ); - emit(SendBookableSpacesSuccess()); - } catch (e) { - emit( - SendBookableSpacesError(e.toString()), - ); - } - } - void _onCheckConfigurValidityEvent(CheckConfigurValidityEvent event, Emitter emit) { if (selectedBookableSpaces.first.spaceConfig!.isValid) { diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_event.dart index d6fe715c..68deee73 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_event.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_event.dart @@ -6,6 +6,7 @@ sealed class SetupBookableSpacesEvent extends Equatable { @override List get props => []; } + class AddToBookableSpaceEvent extends SetupBookableSpacesEvent { final BookableSpacemodel nonBookableSpace; const AddToBookableSpaceEvent({ @@ -20,8 +21,6 @@ class RemoveFromBookableSpaceEvent extends SetupBookableSpacesEvent { }); } -class SendBookableSpacesToApi extends SetupBookableSpacesEvent {} - class CheckConfigurValidityEvent extends SetupBookableSpacesEvent {} class EditModeSelected extends SetupBookableSpacesEvent { @@ -29,4 +28,4 @@ class EditModeSelected extends SetupBookableSpacesEvent { const EditModeSelected({ required this.editingBookableSpace, }); -} \ No newline at end of file +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_state.dart index d8f1f4f3..b6dbad70 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_state.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_state.dart @@ -29,11 +29,4 @@ class ValidSaveButtonState extends SetupBookableSpacesState {} class UnValidSaveButtonState extends SetupBookableSpacesState {} -class SendBookableSpacesLoading extends SetupBookableSpacesState {} -class SendBookableSpacesSuccess extends SetupBookableSpacesState {} - -class SendBookableSpacesError extends SetupBookableSpacesState { - final String error; - const SendBookableSpacesError(this.error); -} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart index 3b72c30c..77d07e52 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_send_bookable_spaces.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart'; @@ -61,6 +63,11 @@ class _SetupBookableSpacesDialogState extends State { ..add(EditModeSelected( editingBookableSpace: widget.editingBookableSpace!, )), + ), + BlocProvider( + create: (context) => SendBookableSpacesBloc( + RemoteSendBookableSpaces(HTTPService()), + ), ) ], child: AlertDialog( diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart index 67b88d32..957bab1d 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart @@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart'; @@ -22,7 +23,7 @@ class SaveSecondStepButton extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocConsumer( + return BlocConsumer( listener: (context, state) { if (state is SendBookableSpacesSuccess) { context.read().add(CallInitStateEvent()); @@ -45,8 +46,11 @@ class SaveSecondStepButton extends StatelessWidget { )) { isEditingMode ? callEditLogic(context) - : context.read().add( - SendBookableSpacesToApi(), + : context.read().add( + SendBookableSpacesToApi( + selectedBookableSpaces: context + .read() + .selectedBookableSpaces), ); } }, From ebfdfe1762070390e3bd9c8b347aa6d2e4cb82ba Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 14:29:57 +0300 Subject: [PATCH 40/71] remove unused container --- .../booking_system/view/booking_page.dart | 78 +++++++++---------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/lib/pages/access_management/booking_system/view/booking_page.dart b/lib/pages/access_management/booking_system/view/booking_page.dart index 67f7dbf3..49d10b50 100644 --- a/lib/pages/access_management/booking_system/view/booking_page.dart +++ b/lib/pages/access_management/booking_system/view/booking_page.dart @@ -12,48 +12,46 @@ class BookingPage extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - child: Row( - children: [ - Expanded( - child: Container( - color: Colors.blueGrey[100], - child: const Center( - child: Text( - 'Side bar', - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - ), + return Row( + children: [ + Expanded( + child: Container( + color: Colors.blueGrey[100], + child: const Center( + child: Text( + 'Side bar', + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), ), - )), - Expanded( - flex: 4, - child: Padding( - padding: const EdgeInsets.all(20.0), - child: SizedBox( - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SvgTextButton( - svgAsset: Assets.homeIcon, - label: 'Manage Bookable Spaces', - onPressed: () { - pageController.jumpToPage(2); - }), - const SizedBox(width: 20), - SvgTextButton( - svgAsset: Assets.groupIcon, - label: 'Manage Users', - onPressed: () {}) - ], - ) - ], - ), + ), + )), + Expanded( + flex: 4, + child: Padding( + padding: const EdgeInsets.all(20.0), + child: SizedBox( + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SvgTextButton( + svgAsset: Assets.homeIcon, + label: 'Manage Bookable Spaces', + onPressed: () { + pageController.jumpToPage(2); + }), + const SizedBox(width: 20), + SvgTextButton( + svgAsset: Assets.groupIcon, + label: 'Manage Users', + onPressed: () {}) + ], + ) + ], ), - )) - ], - ), + ), + )) + ], ); } } From b12903059d9508032449678ec13b8f21161d98a5 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 14:34:36 +0300 Subject: [PATCH 41/71] use copywith and final class for bookableSpaceConfig --- .../domain/models/bookable_space_config.dart | 12 ++++++------ .../presentation/widgets/booking_period_widget.dart | 6 ++++-- .../presentation/widgets/points_part_widget.dart | 10 +++++----- .../widgets/week_checkbox_title_widget.dart | 2 +- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart index e0ba9090..1e28b686 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; class BookableSpaceConfig { - String configUuid; - List bookableDays; - TimeOfDay? bookingStartTime; - TimeOfDay? bookingEndTime; - int cost; - bool availability; + final String configUuid; + final List bookableDays; + final TimeOfDay? bookingStartTime; + final TimeOfDay? bookingEndTime; + final int cost; + final bool availability; BookableSpaceConfig({ required this.configUuid, required this.availability, diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart index 0f9be036..75cf34d7 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart @@ -66,7 +66,8 @@ class BookingPeriodWidget extends StatelessWidget { throw Exception(); } else { setupBookableSpacesBloc.selectedBookableSpaces.forEach( - (e) => e.spaceConfig!.bookingStartTime = timePicked, + (e) => + e.spaceConfig!.copyWith(bookingStartTime: timePicked), ); } }, @@ -99,7 +100,8 @@ class BookingPeriodWidget extends StatelessWidget { throw Exception(); } else { setupBookableSpacesBloc.selectedBookableSpaces.forEach( - (e) => e.spaceConfig!.bookingEndTime = timePicked, + (e) => + e.spaceConfig!.copyWith(bookingEndTime: timePicked), ); } }, diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart index 97dad088..86b387f5 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart @@ -81,7 +81,7 @@ class _PointsPartWidgetState extends State { .read() .selectedBookableSpaces .forEach( - (e) => e.spaceConfig!.cost = -1, + (e) => e.spaceConfig!.copyWith(cost: -1), ); context .read() @@ -95,7 +95,7 @@ class _PointsPartWidgetState extends State { .read() .selectedBookableSpaces .forEach( - (e) => e.spaceConfig!.cost = 0, + (e) => e.spaceConfig!.copyWith(cost: 0), ); context .read() @@ -118,10 +118,10 @@ class _PointsPartWidgetState extends State { .read() .selectedBookableSpaces .forEach( - (e) => e.spaceConfig!.cost = int.parse( - widget.pointsController.text.isEmpty + (e) => e.spaceConfig!.copyWith( + cost: int.parse(widget.pointsController.text.isEmpty ? '0' - : widget.pointsController.text, + : widget.pointsController.text), ), ); context diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart index 28f3b4c0..cd6af711 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart @@ -60,7 +60,7 @@ class _WeekDaysCheckboxRowState extends State { for (var space in context .read() .selectedBookableSpaces) { - space.spaceConfig!.bookableDays = selectedDays; + space.spaceConfig!.copyWith(bookableDays: selectedDays); } }); From 327be5aa547a7a9d652d2b22693206946b12004c Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 14:36:08 +0300 Subject: [PATCH 42/71] use final for bookablespacesModel --- .../domain/models/bookable_space_model.dart | 8 ++++---- .../blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart index 8c6bba7a..70e700be 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart @@ -1,10 +1,10 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart'; class BookableSpacemodel { - String spaceUuid; - String spaceName; - BookableSpaceConfig? spaceConfig; - String spaceVirtualAddress; + final String spaceUuid; + final String spaceName; + final BookableSpaceConfig? spaceConfig; + final String spaceVirtualAddress; BookableSpacemodel({ required this.spaceUuid, diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart index d5a492be..9f7bf2eb 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart @@ -50,7 +50,7 @@ class BookableSpacesBloc bookingStartTime: event.updatedBookableSpaceConfig.bookingStartTime, cost: event.updatedBookableSpaceConfig.cost, ); - editedBookableSpace.spaceConfig = config; + editedBookableSpace.copyWith(spaceConfig: config); final index = event.bookableSpaces.data.indexWhere( (element) => element.spaceUuid == event.bookableSpace.spaceUuid, ); From 9754b3b589b45d47d88f5658aaf27e2bfd0c42a1 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 14:37:41 +0300 Subject: [PATCH 43/71] fix imports and make final params class --- .../manage_bookable_spaces/data/remote_non_bookable_spaces.dart | 1 - .../domain/params/bookable_spaces_params.dart | 2 +- .../domain/params/update_bookable_space_param.dart | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart index b9a499af..47d08963 100644 --- a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart +++ b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:dio/dio.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; import 'package:syncrow_web/services/api/api_exception.dart'; diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart b/lib/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart index 02e60733..160ea60e 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart @@ -1,5 +1,5 @@ class BookableSpacesParams { - int currentPage; + final int currentPage; BookableSpacesParams({ required this.currentPage, }); diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart b/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart index 57962772..200a1794 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/utils/string_utils.dart'; From 518e9c891425aee5cb63458c2943dc7cf9d292f6 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 14:40:10 +0300 Subject: [PATCH 44/71] make params final --- .../domain/params/non_bookable_spaces_params.dart | 2 +- .../params/send_bookable_spaces_to_api_params.dart | 10 +++++----- .../domain/params/update_bookable_space_param.dart | 13 ++++++------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart b/lib/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart index b688e6b1..9f567a6c 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart @@ -1,5 +1,5 @@ class NonBookableSpacesParams { - int currentPage; +final int currentPage; String? searchedWords; NonBookableSpacesParams({ required this.currentPage, diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart b/lib/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart index 38f099fb..3e1251dd 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart @@ -2,11 +2,11 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domai import 'package:syncrow_web/utils/string_utils.dart'; class SendBookableSpacesToApiParams { - List spaceUuids; - List daysAvailable; - String startTime; - String endTime; - int points; + final List spaceUuids; + final List daysAvailable; + final String startTime; + final String endTime; + final int points; SendBookableSpacesToApiParams({ required this.spaceUuids, required this.daysAvailable, diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart b/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart index 200a1794..237f3442 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart @@ -2,13 +2,12 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domai import 'package:syncrow_web/utils/string_utils.dart'; class UpdateBookableSpaceParam { - String spaceUuid; - - List? bookableDays; - String? bookingStartTime; - String? bookingEndTime; - int? cost; - bool? availability; + final String spaceUuid; + final List? bookableDays; + final String? bookingStartTime; + final String? bookingEndTime; + final int? cost; + final bool? availability; UpdateBookableSpaceParam({ required this.spaceUuid, this.bookingStartTime, From c8eb07c166fde95b0ec107f418d372cd1d3f6d46 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 14:43:00 +0300 Subject: [PATCH 45/71] use state instead of variable in cubit --- .../blocs/toggle_cubit/toggle_points_switch_cubit.dart | 4 +--- .../presentation/widgets/points_part_widget.dart | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart index 0b0f11f0..80222d0c 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart @@ -5,14 +5,12 @@ part 'toggle_points_switch_state.dart'; class TogglePointsSwitchCubit extends Cubit { TogglePointsSwitchCubit() : super(TogglePointsSwitchInitial()); - bool switchValue = true; + void activateSwitch() { - switchValue = true; emit(ActivatePointsSwitch()); } void unActivateSwitch() { - switchValue = false; emit(UnActivatePointsSwitch()); } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart index 86b387f5..0ca00205 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart @@ -71,7 +71,7 @@ class _PointsPartWidgetState extends State { (Set states) { return ColorsManager.whiteColors; }), - value: context.watch().switchValue, + value: state is ActivatePointsSwitch, onChanged: (value) { if (value) { context From c473325883fe3bc12d17cb30a7f9b1efe0eed519 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 15:01:38 +0300 Subject: [PATCH 46/71] add decorator layer --- .../data/non_bookable_spaces_decorator.dart | 32 ++++++++ .../data/remote_non_bookable_spaces.dart | 73 +++++++++---------- .../screens/setup_bookable_spaces_dialog.dart | 5 +- 3 files changed, 69 insertions(+), 41 deletions(-) create mode 100644 lib/pages/access_management/manage_bookable_spaces/data/non_bookable_spaces_decorator.dart diff --git a/lib/pages/access_management/manage_bookable_spaces/data/non_bookable_spaces_decorator.dart b/lib/pages/access_management/manage_bookable_spaces/data/non_bookable_spaces_decorator.dart new file mode 100644 index 00000000..af24439f --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/data/non_bookable_spaces_decorator.dart @@ -0,0 +1,32 @@ +import 'dart:async'; + +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart'; + +class NonBookableSpacesDebouncerDecoratorService + implements NonBookableSpacesService { + final NonBookableSpacesService _delegate; + Timer? _debounce; + + NonBookableSpacesDebouncerDecoratorService(this._delegate); + + @override + Future> load( + NonBookableSpacesParams params) { + final completer = Completer>(); + + _debounce?.cancel(); + _debounce = Timer(const Duration(milliseconds: 500), () async { + try { + final result = await _delegate.load(params); + completer.complete(result); + } catch (e) { + completer.completeError(e); + } + }); + + return completer.future; + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart index 47d08963..1db35a8e 100644 --- a/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart +++ b/lib/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart @@ -10,51 +10,44 @@ import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; class RemoteNonBookableSpaces implements NonBookableSpacesService { - Timer? _debounce; - final HTTPService _httpService; + RemoteNonBookableSpaces(this._httpService); + static const _defaultErrorMessage = 'Failed to load Spaces'; @override Future> load( - NonBookableSpacesParams params) { - final completer = Completer>(); - - _debounce?.cancel(); - _debounce = Timer(const Duration(milliseconds: 500), () async { - try { - final response = await _httpService.get( - path: ApiEndpoints.bookableSpaces, - queryParameters: { - 'configured': false, - 'page': params.currentPage, - 'search': params.searchedWords, - }, - expectedResponseModel: (json) { - final result = json as Map; - return PaginatedDataModel.fromJson( - result, - BookableSpacemodel.fromJsonList, - ); - }, - ); - completer.complete(response); - } on DioException catch (e) { - final message = e.response?.data as Map?; - final error = message?['error'] as Map?; - final errorMessage = error?['error'] as String? ?? ''; - final formattedErrorMessage = [ - _defaultErrorMessage, - errorMessage, - ].join(': '); - completer.completeError(APIException(formattedErrorMessage)); - } catch (e) { - final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); - completer.completeError(APIException(formattedErrorMessage)); - } - }); - - return completer.future; + NonBookableSpacesParams params) async { + try { + final response = await _httpService.get( + path: ApiEndpoints.bookableSpaces, + queryParameters: { + 'configured': false, + 'page': params.currentPage, + 'search': params.searchedWords, + }, + expectedResponseModel: (json) { + final result = json as Map; + return PaginatedDataModel.fromJson( + result, + BookableSpacemodel.fromJsonList, + ); + }, + ); + return response; + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [ + _defaultErrorMessage, + errorMessage, + ].join(': '); + throw APIException(formattedErrorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart index 77d07e52..a0e8da84 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/non_bookable_spaces_decorator.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_send_bookable_spaces.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; @@ -46,7 +47,9 @@ class _SetupBookableSpacesDialogState extends State { ), BlocProvider( create: (context) => NonBookableSpacesBloc( - RemoteNonBookableSpaces(HTTPService()), + NonBookableSpacesDebouncerDecoratorService( + RemoteNonBookableSpaces(HTTPService()), + ), )..add( LoadUnBookableSpacesEvent( nonBookableSpacesParams: From 7af8887d4f051df9328c04d969a01bad1d3a8df3 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 21 Jul 2025 15:57:10 +0300 Subject: [PATCH 47/71] Add new API endpoint for reordering spaces in the community module. --- lib/utils/constants/api_const.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index e99f4796..ca9bee58 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -41,6 +41,8 @@ abstract class ApiEndpoints { '/projects/{projectId}/communities/{communityId}/spaces/{spaceId}'; static const String getSpaceHierarchy = '/projects/{projectId}/communities/{communityId}/spaces'; + static const String reorderSpaces = + '/projects/{projectUuid}/communities/{communityUuid}/spaces/{parentSpaceUuid}/spaces/order'; // Community Module static const String createCommunity = '/projects/{projectId}/communities'; From c65f4a7fabfaaa7c2e1e4559870c6c700fcf6a98 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 21 Jul 2025 15:57:20 +0300 Subject: [PATCH 48/71] Add ReorderSpacesParam and ReorderSpacesService for managing space reordering functionality. --- .../domain/params/reorder_spaces_param.dart | 17 +++++++++++++++++ .../domain/services/reorder_spaces_service.dart | 5 +++++ 2 files changed, 22 insertions(+) create mode 100644 lib/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart create mode 100644 lib/pages/space_management_v2/modules/reorder_spaces/domain/services/reorder_spaces_service.dart diff --git a/lib/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart b/lib/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart new file mode 100644 index 00000000..fc3dd5ba --- /dev/null +++ b/lib/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart @@ -0,0 +1,17 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; + +class ReorderSpacesParam extends Equatable { + const ReorderSpacesParam({ + required this.spaces, + required this.communityUuid, + required this.parentSpaceUuid, + }); + + final List spaces; + final String communityUuid; + final String parentSpaceUuid; + + @override + List get props => [spaces, communityUuid, parentSpaceUuid]; +} diff --git a/lib/pages/space_management_v2/modules/reorder_spaces/domain/services/reorder_spaces_service.dart b/lib/pages/space_management_v2/modules/reorder_spaces/domain/services/reorder_spaces_service.dart new file mode 100644 index 00000000..46811fae --- /dev/null +++ b/lib/pages/space_management_v2/modules/reorder_spaces/domain/services/reorder_spaces_service.dart @@ -0,0 +1,5 @@ +import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart'; + +abstract interface class ReorderSpacesService { + Future reorderSpaces(ReorderSpacesParam param); +} From 9bf715501baa3383a6a4c232f37fd46c953e6298 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 21 Jul 2025 15:57:31 +0300 Subject: [PATCH 49/71] Implement ReorderSpacesService. --- .../remote_reorder_spaces_service.dart | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 lib/pages/space_management_v2/modules/reorder_spaces/data/services/remote_reorder_spaces_service.dart diff --git a/lib/pages/space_management_v2/modules/reorder_spaces/data/services/remote_reorder_spaces_service.dart b/lib/pages/space_management_v2/modules/reorder_spaces/data/services/remote_reorder_spaces_service.dart new file mode 100644 index 00000000..463b31ce --- /dev/null +++ b/lib/pages/space_management_v2/modules/reorder_spaces/data/services/remote_reorder_spaces_service.dart @@ -0,0 +1,35 @@ +import 'package:dio/dio.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/services/reorder_spaces_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + +final class RemoteReorderSpacesService implements ReorderSpacesService { + RemoteReorderSpacesService(this._httpClient); + + final HTTPService _httpClient; + + @override + Future reorderSpaces(ReorderSpacesParam param) async { + try { + await _httpClient.post( + path: ApiEndpoints.reorderSpaces, + body: param, + expectedResponseModel: (json) => json, + ); + } on DioException catch (e) { + final message = e.response?.data as Map?; + throw APIException(_getErrorMessageFromBody(message)); + } catch (e) { + throw APIException(e.toString()); + } + } + + String _getErrorMessageFromBody(Map? body) { + if (body == null) return 'Failed to delete space'; + final error = body['error'] as Map?; + final errorMessage = error?['message'] as String? ?? ''; + return errorMessage; + } +} From a3a7937021636596d7b446f4eace0b583a16b979 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 21 Jul 2025 16:14:03 +0300 Subject: [PATCH 50/71] Implemented `ReorderSpacesBloc`. --- .../bloc/reorder_spaces_bloc.dart | 35 +++++++++++++++++++ .../bloc/reorder_spaces_event.dart | 10 ++++++ .../bloc/reorder_spaces_state.dart | 29 +++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 lib/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_bloc.dart create mode 100644 lib/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_event.dart create mode 100644 lib/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_state.dart diff --git a/lib/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_bloc.dart b/lib/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_bloc.dart new file mode 100644 index 00000000..ecd15898 --- /dev/null +++ b/lib/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_bloc.dart @@ -0,0 +1,35 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/services/reorder_spaces_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; + +part 'reorder_spaces_event.dart'; +part 'reorder_spaces_state.dart'; + +class ReorderSpacesBloc extends Bloc { + ReorderSpacesBloc( + this._reorderSpacesService, + ) : super(const ReorderSpacesInitial()) { + on(_onReorderSpacesEvent); + } + + final ReorderSpacesService _reorderSpacesService; + + Future _onReorderSpacesEvent( + ReorderSpacesEvent event, + Emitter emit, + ) async { + emit(const ReorderSpacesLoading()); + try { + await _reorderSpacesService.reorderSpaces(event.param); + emit(const ReorderSpacesSuccess()); + } on APIException catch (e) { + emit(ReorderSpacesFailure(e.message)); + } catch (e) { + emit(ReorderSpacesFailure(e.toString())); + } finally { + emit(const ReorderSpacesInitial()); + } + } +} diff --git a/lib/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_event.dart b/lib/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_event.dart new file mode 100644 index 00000000..8cccb4f1 --- /dev/null +++ b/lib/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_event.dart @@ -0,0 +1,10 @@ +part of 'reorder_spaces_bloc.dart'; + +final class ReorderSpacesEvent extends Equatable { + const ReorderSpacesEvent(this.param); + + final ReorderSpacesParam param; + + @override + List get props => [param]; +} diff --git a/lib/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_state.dart b/lib/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_state.dart new file mode 100644 index 00000000..d237d93c --- /dev/null +++ b/lib/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_state.dart @@ -0,0 +1,29 @@ +part of 'reorder_spaces_bloc.dart'; + +sealed class ReorderSpacesState extends Equatable { + const ReorderSpacesState(); + + @override + List get props => []; +} + +final class ReorderSpacesInitial extends ReorderSpacesState { + const ReorderSpacesInitial(); +} + +final class ReorderSpacesLoading extends ReorderSpacesState { + const ReorderSpacesLoading(); +} + +final class ReorderSpacesSuccess extends ReorderSpacesState { + const ReorderSpacesSuccess(); +} + +final class ReorderSpacesFailure extends ReorderSpacesState { + const ReorderSpacesFailure(this.errorMessage); + + final String errorMessage; + + @override + List get props => [errorMessage]; +} From 96f107f97208012b1fe1301758882057dd3fec36 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 21 Jul 2025 16:15:26 +0300 Subject: [PATCH 51/71] Refactor SpaceManagementPage to utilize a shared HTTPService instance for API calls in `Communities`, `SpaceDetails`, `Products`, and `ReorderSpaces` blocs, and injected `ReorderSpacesBloc` into it. --- .../main_module/views/space_management_page.dart | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/views/space_management_page.dart b/lib/pages/space_management_v2/main_module/views/space_management_page.dart index 55e47de1..5c226671 100644 --- a/lib/pages/space_management_v2/main_module/views/space_management_page.dart +++ b/lib/pages/space_management_v2/main_module/views/space_management_page.dart @@ -9,6 +9,8 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/presen import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/products/data/services/remote_products_service.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/products/presentation/bloc/products_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/data/services/remote_reorder_spaces_service.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/data/services/unique_space_details_spaces_decorator_service.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart'; @@ -25,15 +27,16 @@ class SpaceManagementPage extends StatefulWidget { class _SpaceManagementPageState extends State { late final CommunitiesBloc communitiesBloc; + late final HTTPService _httpService; @override void initState() { communitiesBloc = CommunitiesBloc( communitiesService: DebouncedCommunitiesService( - RemoteCommunitiesService(HTTPService()), + RemoteCommunitiesService(_httpService), ), )..add(const LoadCommunities(LoadCommunitiesParam())); - + _httpService = HTTPService(); super.initState(); } @@ -50,13 +53,18 @@ class _SpaceManagementPageState extends State { BlocProvider( create: (context) => SpaceDetailsBloc( UniqueSpaceDetailsSpacesDecoratorService( - RemoteSpaceDetailsService(httpService: HTTPService()), + RemoteSpaceDetailsService(httpService: _httpService), ), ), ), BlocProvider( create: (context) => ProductsBloc( - RemoteProductsService(HTTPService()), + RemoteProductsService(_httpService), + ), + ), + BlocProvider( + create: (context) => ReorderSpacesBloc( + RemoteReorderSpacesService(_httpService), ), ), ], From 35c8a73156414f15835bded948ceb8f8697656c2 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 21 Jul 2025 16:24:11 +0300 Subject: [PATCH 52/71] Refactor SpaceManagementPage's initState to ensure HTTPService is initialized before use in CommunitiesBloc. --- .../main_module/views/space_management_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/space_management_v2/main_module/views/space_management_page.dart b/lib/pages/space_management_v2/main_module/views/space_management_page.dart index 5c226671..47a67c36 100644 --- a/lib/pages/space_management_v2/main_module/views/space_management_page.dart +++ b/lib/pages/space_management_v2/main_module/views/space_management_page.dart @@ -31,12 +31,12 @@ class _SpaceManagementPageState extends State { @override void initState() { + _httpService = HTTPService(); communitiesBloc = CommunitiesBloc( communitiesService: DebouncedCommunitiesService( RemoteCommunitiesService(_httpService), ), )..add(const LoadCommunities(LoadCommunitiesParam())); - _httpService = HTTPService(); super.initState(); } From 983040135fa9dd9a86d360f65c4e8dfadd4e54fd Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 16:30:28 +0300 Subject: [PATCH 53/71] fix using copywith --- .../view/widgets/icon_text_button.dart | 11 ++- .../bookable_spaces_bloc.dart | 29 ++++---- .../widgets/booking_period_widget.dart | 38 +++++++--- .../top_part_widget.dart | 3 +- .../widgets/points_part_widget.dart | 70 ++++++++++--------- .../widgets/week_checkbox_title_widget.dart | 33 +++++---- 6 files changed, 107 insertions(+), 77 deletions(-) diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart index 32201ed5..9844e3d8 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart @@ -4,8 +4,7 @@ import 'package:syncrow_web/utils/color_manager.dart'; class SvgTextButton extends StatelessWidget { final String svgAsset; - final double? horizontalPadding; - final double? verticalPadding; + final EdgeInsets? padding; final String label; final VoidCallback onPressed; final Color backgroundColor; @@ -27,8 +26,7 @@ class SvgTextButton extends StatelessWidget { this.svgColor = const Color(0xFF496EFF), this.labelColor = Colors.black, this.borderRadius = 10.0, - this.horizontalPadding, - this.verticalPadding, + this.padding, this.boxShadow = const [ BoxShadow( color: ColorsManager.lightGrayColor, @@ -47,9 +45,8 @@ class SvgTextButton extends StatelessWidget { borderRadius: BorderRadius.circular(borderRadius), onTap: onPressed, child: Container( - padding: EdgeInsets.symmetric( - horizontal: horizontalPadding ?? 24, - vertical: verticalPadding ?? 12), + padding: padding ?? + const EdgeInsets.symmetric(horizontal: 24, vertical: 12), decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(borderRadius), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart index 9f7bf2eb..c947cd69 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart @@ -40,22 +40,25 @@ class BookableSpacesBloc if (event.bookableSpace.spaceConfig!.configUuid == event.updatedBookableSpaceConfig.configUuid) { - final editedBookableSpace = event.bookableSpaces.data.firstWhere( - (element) => element.spaceUuid == event.bookableSpace.spaceUuid, - ); - final config = editedBookableSpace.spaceConfig!.copyWith( - availability: event.updatedBookableSpaceConfig.availability, - bookableDays: event.updatedBookableSpaceConfig.bookableDays, - bookingEndTime: event.updatedBookableSpaceConfig.bookingEndTime, - bookingStartTime: event.updatedBookableSpaceConfig.bookingStartTime, - cost: event.updatedBookableSpaceConfig.cost, - ); - editedBookableSpace.copyWith(spaceConfig: config); final index = event.bookableSpaces.data.indexWhere( (element) => element.spaceUuid == event.bookableSpace.spaceUuid, ); - event.bookableSpaces.data.removeAt(index); - event.bookableSpaces.data.insert(index, editedBookableSpace); + + if (index != -1) { + final original = event.bookableSpaces.data[index]; + + final updatedConfig = original.spaceConfig!.copyWith( + availability: event.updatedBookableSpaceConfig.availability, + bookableDays: event.updatedBookableSpaceConfig.bookableDays, + bookingEndTime: event.updatedBookableSpaceConfig.bookingEndTime, + bookingStartTime: event.updatedBookableSpaceConfig.bookingStartTime, + cost: event.updatedBookableSpaceConfig.cost, + ); + + final updatedSpace = original.copyWith(spaceConfig: updatedConfig); + + event.bookableSpaces.data[index] = updatedSpace; + } } emit(BookableSpacesLoaded(bookableSpacesList: event.bookableSpaces)); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart index 75cf34d7..439bda04 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart @@ -65,10 +65,21 @@ class BookingPeriodWidget extends StatelessWidget { )); throw Exception(); } else { - setupBookableSpacesBloc.selectedBookableSpaces.forEach( - (e) => - e.spaceConfig!.copyWith(bookingStartTime: timePicked), - ); + for (int i = 0; + i < + setupBookableSpacesBloc + .selectedBookableSpaces.length; + i++) { + final space = + setupBookableSpacesBloc.selectedBookableSpaces[i]; + final updatedConfig = space.spaceConfig + ?.copyWith(bookingStartTime: timePicked); + final updatedSpace = + space.copyWith(spaceConfig: updatedConfig); + + setupBookableSpacesBloc.selectedBookableSpaces[i] = + updatedSpace; + } } }, ), @@ -99,10 +110,21 @@ class BookingPeriodWidget extends StatelessWidget { )); throw Exception(); } else { - setupBookableSpacesBloc.selectedBookableSpaces.forEach( - (e) => - e.spaceConfig!.copyWith(bookingEndTime: timePicked), - ); + for (int i = 0; + i < + setupBookableSpacesBloc + .selectedBookableSpaces.length; + i++) { + final space = + setupBookableSpacesBloc.selectedBookableSpaces[i]; + final updatedConfig = space.spaceConfig + ?.copyWith(bookingEndTime: timePicked); + final updatedSpace = + space.copyWith(spaceConfig: updatedConfig); + + setupBookableSpacesBloc.selectedBookableSpaces[i] = + updatedSpace; + } } }, ), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart index 2460cda0..3c6694af 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart @@ -53,8 +53,7 @@ class RowOfButtonsTitleWidget extends StatelessWidget { ], ), SvgTextButton( - verticalPadding: 10, - horizontalPadding: 10, + padding: const EdgeInsets.all(10), svgSize: 15, fontSize: 10, fontWeight: FontWeight.bold, diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart index 0ca00205..1106f214 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart @@ -73,34 +73,31 @@ class _PointsPartWidgetState extends State { }), value: state is ActivatePointsSwitch, onChanged: (value) { + final bloc = context.read(); + final updatedCost = value ? -1 : 0; + + final switchCubit = + context.read(); if (value) { - context - .read() - .activateSwitch(); - context - .read() - .selectedBookableSpaces - .forEach( - (e) => e.spaceConfig!.copyWith(cost: -1), - ); - context - .read() - .add(CheckConfigurValidityEvent()); + switchCubit.activateSwitch(); } else { - context - .read() - .unActivateSwitch(); + switchCubit.unActivateSwitch(); widget.pointsController.clear(); - context - .read() - .selectedBookableSpaces - .forEach( - (e) => e.spaceConfig!.copyWith(cost: 0), - ); - context - .read() - .add(CheckConfigurValidityEvent()); } + + for (int i = 0; + i < bloc.selectedBookableSpaces.length; + i++) { + final space = bloc.selectedBookableSpaces[i]; + final updatedConfig = + space.spaceConfig?.copyWith(cost: updatedCost); + final updatedSpace = + space.copyWith(spaceConfig: updatedConfig); + + bloc.selectedBookableSpaces[i] = updatedSpace; + } + + bloc.add(CheckConfigurValidityEvent()); }, ), ) @@ -114,16 +111,21 @@ class _PointsPartWidgetState extends State { title: 'Ex: 0', height: 40, onChanged: (p0) { - context - .read() - .selectedBookableSpaces - .forEach( - (e) => e.spaceConfig!.copyWith( - cost: int.parse(widget.pointsController.text.isEmpty - ? '0' - : widget.pointsController.text), - ), - ); + final updatedCost = + int.tryParse(widget.pointsController.text) ?? 0; + + final bloc = context.read(); + + for (var i = 0; i < bloc.selectedBookableSpaces.length; i++) { + final space = bloc.selectedBookableSpaces[i]; + final updatedConfig = + space.spaceConfig?.copyWith(cost: updatedCost); + final updatedSpace = + space.copyWith(spaceConfig: updatedConfig); + + bloc.selectedBookableSpaces[i] = updatedSpace; + } + context .read() .add(CheckConfigurValidityEvent()); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart index cd6af711..5d689748 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart @@ -52,21 +52,28 @@ class _WeekDaysCheckboxRowState extends State { onChanged: (newValue) { setState(() { _daysChecked[entry.key] = newValue ?? false; - final selectedDays = _daysChecked.entries - .where((e) => e.value) - .map((e) => e.key) - .toList(); - - for (var space in context - .read() - .selectedBookableSpaces) { - space.spaceConfig!.copyWith(bookableDays: selectedDays); - } }); - context - .read() - .add(CheckConfigurValidityEvent()); + final selectedDays = _daysChecked.entries + .where((e) => e.value) + .map((e) => e.key) + .toList(); + + final bloc = context.read(); + + for (int i = 0; + i < bloc.selectedBookableSpaces.length; + i++) { + final space = bloc.selectedBookableSpaces[i]; + final updatedConfig = space.spaceConfig + ?.copyWith(bookableDays: selectedDays); + final updatedSpace = + space.copyWith(spaceConfig: updatedConfig); + + bloc.selectedBookableSpaces[i] = updatedSpace; + } + + bloc.add(CheckConfigurValidityEvent()); }, ), ), From fb506e16c1286d177e0a18562b0fc7bb78bf4ed4 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 16:37:46 +0300 Subject: [PATCH 54/71] requested cubits --- .../presentation/blocs/steps_cubit/cubit/steps_cubit.dart | 2 +- .../presentation/blocs/steps_cubit/cubit/steps_state.dart | 3 --- .../blocs/toggle_cubit/toggle_points_switch_cubit.dart | 2 +- .../blocs/toggle_cubit/toggle_points_switch_state.dart | 1 - .../presentation/widgets/details_steps_widget.dart | 1 - 5 files changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart index 974a2b96..4258f5aa 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart @@ -4,7 +4,7 @@ import 'package:equatable/equatable.dart'; part 'steps_state.dart'; class StepsCubit extends Cubit { - StepsCubit() : super(StepsInitial()); + StepsCubit() : super(StepOneState()); void initDialogValue() { emit(StepOneState()); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_state.dart index 707d0a41..183fdb88 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_state.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_state.dart @@ -7,9 +7,6 @@ sealed class StepsState extends Equatable { List get props => []; } -final class StepsInitial extends StepsState {} - final class StepOneState extends StepsState {} final class StepTwoState extends StepsState {} - diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart index 80222d0c..09a11e5e 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart @@ -4,7 +4,7 @@ import 'package:equatable/equatable.dart'; part 'toggle_points_switch_state.dart'; class TogglePointsSwitchCubit extends Cubit { - TogglePointsSwitchCubit() : super(TogglePointsSwitchInitial()); + TogglePointsSwitchCubit() : super(UnActivatePointsSwitch()); void activateSwitch() { emit(ActivatePointsSwitch()); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_state.dart index 872ae8dc..be078660 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_state.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_state.dart @@ -7,7 +7,6 @@ sealed class TogglePointsSwitchState extends Equatable { List get props => []; } -final class TogglePointsSwitchInitial extends TogglePointsSwitchState {} class ActivatePointsSwitch extends TogglePointsSwitchState {} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart index 32913c89..bbcc5293 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart @@ -25,7 +25,6 @@ class DetailsStepsWidget extends StatelessWidget { pointsController: pointsController, editingBookableSpace: editingBookableSpace, ), - StepsInitial() => const SizedBox(), }; }), ); From dd425236f436f1c5bce1905993efb90da67ab8cb Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 16:39:07 +0300 Subject: [PATCH 55/71] no need for (S) cuz it is not List --- .../update_bookable_spaces/update_bookable_spaces_bloc.dart | 4 ++-- .../update_bookable_spaces/update_bookable_spaces_event.dart | 4 ++-- .../widgets/bookable_space_switch_activation_widget.dart | 2 +- .../presentation/widgets/save_second_step_button.dart | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart index 9f99b3ea..39e42b5b 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart @@ -18,10 +18,10 @@ class UpdateBookableSpacesBloc Future _onUpdateBookableSpace(UpdateBookableSpace event, Emitter emit) async { - emit(UpdateBookableSpaceLoading(event.updatedParams.spaceUuid)); + emit(UpdateBookableSpaceLoading(event.updatedParam.spaceUuid)); try { final updatedSpace = - await updateBookableSpaceService.update(event.updatedParams); + await updateBookableSpaceService.update(event.updatedParam); emit(UpdateBookableSpaceSuccess(bookableSpaceConfig: updatedSpace)); event.onSuccess?.call(); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_event.dart index 6047d41a..1abcea0b 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_event.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_event.dart @@ -9,9 +9,9 @@ sealed class UpdateBookableSpaceEvent extends Equatable { class UpdateBookableSpace extends UpdateBookableSpaceEvent { final void Function()? onSuccess; - final UpdateBookableSpaceParam updatedParams; + final UpdateBookableSpaceParam updatedParam; const UpdateBookableSpace({ - required this.updatedParams, + required this.updatedParam, this.onSuccess, }); } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart index 4c88fe87..d819ca26 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart @@ -56,7 +56,7 @@ class BookableSpaceSwitchActivationWidget extends StatelessWidget { onChanged: (value) { context.read().add( UpdateBookableSpace( - updatedParams: UpdateBookableSpaceParam( + updatedParam: UpdateBookableSpaceParam( spaceUuid: space.spaceUuid, availability: value, )), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart index 957bab1d..e9fc0289 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart @@ -65,7 +65,7 @@ class SaveSecondStepButton extends StatelessWidget { UpdateBookableSpace( onSuccess: () => context.read().add(CallInitStateEvent()), - updatedParams: UpdateBookableSpaceParam.fromBookableModel( + updatedParam: UpdateBookableSpaceParam.fromBookableModel( context .read() .selectedBookableSpaces From 1323bceca12a82b1a7b1f19e677d4786ddf44294 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 21 Jul 2025 16:39:31 +0300 Subject: [PATCH 56/71] Update ReorderSpacesParam to make parentSpaceUuid optional and add toJson method for serialization. --- .../domain/params/reorder_spaces_param.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart b/lib/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart index fc3dd5ba..e18544a8 100644 --- a/lib/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart +++ b/lib/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart @@ -1,17 +1,21 @@ import 'package:equatable/equatable.dart'; -import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; class ReorderSpacesParam extends Equatable { const ReorderSpacesParam({ required this.spaces, required this.communityUuid, - required this.parentSpaceUuid, + this.parentSpaceUuid, }); final List spaces; final String communityUuid; - final String parentSpaceUuid; + final String? parentSpaceUuid; @override List get props => [spaces, communityUuid, parentSpaceUuid]; + + Map toJson() => { + 'spacesUuids': spaces.map((space) => space.uuid).toList(), + }; } From 0c0f26bec7955b3d105dd0ed774703b70c1d1003 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 16:39:59 +0300 Subject: [PATCH 57/71] param not params --- .../data/remote_bookable_spaces_service.dart | 2 +- .../domain/params/bookable_spaces_params.dart | 4 ++-- .../domain/service/bookable_spaces_service.dart | 2 +- .../bookable_spaces_bloc/bookable_spaces_event.dart | 2 +- .../screens/manage_bookable_spaces_screen.dart | 2 +- .../widgets/buttons_divider_bottom_dialog_widget.dart | 5 ++--- .../bottom_pagination_part_widget.dart | 10 +++++----- .../table_part_widget.dart | 2 +- 8 files changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart b/lib/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart index 44656482..ceebf262 100644 --- a/lib/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart +++ b/lib/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart @@ -13,7 +13,7 @@ class RemoteBookableSpacesService implements BookableSpacesService { static const _defaultErrorMessage = 'Failed to load Bookable Spaces'; @override Future> load( - BookableSpacesParams param) async { + BookableSpacesParam param) async { try { final response = await _httpService.get( path: ApiEndpoints.bookableSpaces, diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart b/lib/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart index 160ea60e..6007424b 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart @@ -1,6 +1,6 @@ -class BookableSpacesParams { +class BookableSpacesParam { final int currentPage; - BookableSpacesParams({ + BookableSpacesParam({ required this.currentPage, }); diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart b/lib/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart index 5929a935..208589ad 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart @@ -4,5 +4,5 @@ import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/ abstract class BookableSpacesService { Future> load( - BookableSpacesParams param); + BookableSpacesParam param); } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart index 7c5344c8..c73f08dc 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_event.dart @@ -8,7 +8,7 @@ sealed class BookableSpacesEvent extends Equatable { } class LoadBookableSpacesEvent extends BookableSpacesEvent { - final BookableSpacesParams param; + final BookableSpacesParam param; const LoadBookableSpacesEvent(this.param); } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart index 11c43cdb..155049e4 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart @@ -33,7 +33,7 @@ class _ManageBookableSpacesPageState extends State { RemoteBookableSpacesService(HTTPService()), )..add( LoadBookableSpacesEvent( - BookableSpacesParams(currentPage: 1), + BookableSpacesParam(currentPage: 1), ), ), ), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart index 80981897..0e13370f 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart @@ -71,7 +71,7 @@ class ButtonsDividerBottomDialogWidget extends StatelessWidget { ); context.read().add( LoadBookableSpacesEvent( - BookableSpacesParams(currentPage: 1), + BookableSpacesParam(currentPage: 1), ), ); } else if (nonBookableState is NonBookableSpacesError) { @@ -79,8 +79,7 @@ class ButtonsDividerBottomDialogWidget extends StatelessWidget { SnackBar( content: Text( nonBookableState.error, - style: - const TextStyle(color: ColorsManager.red), + style: const TextStyle(color: ColorsManager.red), ), duration: const Duration(seconds: 2), behavior: SnackBarBehavior.floating, diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart index 104425c3..d48518a9 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart @@ -25,7 +25,7 @@ class PaginationButtonsWidget extends StatelessWidget { onTap: () { context.read().add( LoadBookableSpacesEvent( - BookableSpacesParams(currentPage: currentPage - 2), + BookableSpacesParam(currentPage: currentPage - 2), ), ); }, @@ -41,7 +41,7 @@ class PaginationButtonsWidget extends StatelessWidget { onTap: () { context.read().add( LoadBookableSpacesEvent( - BookableSpacesParams(currentPage: currentPage - 1), + BookableSpacesParam(currentPage: currentPage - 1), ), ); }, @@ -59,7 +59,7 @@ class PaginationButtonsWidget extends StatelessWidget { if (i != currentPage) { context.read().add( LoadBookableSpacesEvent( - BookableSpacesParams(currentPage: i), + BookableSpacesParam(currentPage: i), ), ); } @@ -97,7 +97,7 @@ class PaginationButtonsWidget extends StatelessWidget { onTap: () { context.read().add( LoadBookableSpacesEvent( - BookableSpacesParams(currentPage: currentPage + 1), + BookableSpacesParam(currentPage: currentPage + 1), ), ); }, @@ -113,7 +113,7 @@ class PaginationButtonsWidget extends StatelessWidget { onTap: () { context.read().add( LoadBookableSpacesEvent( - BookableSpacesParams(currentPage: currentPage + 2), + BookableSpacesParam(currentPage: currentPage + 2), ), ); }, diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart index a0ce8ace..35305163 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart @@ -28,7 +28,7 @@ class TableOfBookableSpacesWidget extends StatelessWidget { onPressed: () => context .read() .add(LoadBookableSpacesEvent( - BookableSpacesParams(currentPage: 1), + BookableSpacesParam(currentPage: 1), )), child: const Text('Try Again')) ]); From e8576c8dbe1f91571373b96ca8e9abdd700693c2 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 16:40:46 +0300 Subject: [PATCH 58/71] remove comments --- .../bottom_pagination_part_widget.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart index d48518a9..cb7bcc33 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart @@ -17,7 +17,6 @@ class PaginationButtonsWidget extends StatelessWidget { List paginationItems = []; - // « Two pages back if (currentPage > 2) { paginationItems.add( _buildArrowButton( @@ -33,7 +32,6 @@ class PaginationButtonsWidget extends StatelessWidget { ); } - // < One page back if (currentPage > 1) { paginationItems.add( _buildArrowButton( @@ -49,7 +47,6 @@ class PaginationButtonsWidget extends StatelessWidget { ); } - // Page numbers for (int i = 1; i <= totalPages; i++) { paginationItems.add( Padding( @@ -89,7 +86,6 @@ class PaginationButtonsWidget extends StatelessWidget { ); } - // > One page forward if (currentPage < totalPages) { paginationItems.add( _buildArrowButton( @@ -105,7 +101,6 @@ class PaginationButtonsWidget extends StatelessWidget { ); } - // » Two pages forward if (currentPage + 1 < totalPages) { paginationItems.add( _buildArrowButton( From b1b72fa8aa1388f5c70ac647183c2d7f26cde6b6 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 16:42:57 +0300 Subject: [PATCH 59/71] use DataCellWidget --- .../main_manage_bookable_widgets/table_part_widget.dart | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart index 35305163..a6e206fc 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart @@ -41,12 +41,9 @@ class TableOfBookableSpacesWidget extends StatelessWidget { title: space.spaceName, ), ), - DataCell(Padding( - padding: const EdgeInsetsGeometry.only(left: 10), - child: Text( - space.spaceVirtualAddress, - style: const TextStyle(fontSize: 11), - ))), + DataCell(DataCellWidget( + title: space.spaceVirtualAddress, + )), DataCell(Container( padding: const EdgeInsetsGeometry.only(left: 10), width: 200, From 40251b846b79eca48f42eb774e85b8ad64ed69f2 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 21 Jul 2025 16:43:26 +0300 Subject: [PATCH 60/71] Integrate ReorderSpaces functionality into CommunityStructureCanvas and enhance RemoteReorderSpacesService with dynamic URL generation. Update ReorderSpacesParam to require parentSpaceUuid and spaces for improved validation and serialization. --- .../widgets/community_structure_canvas.dart | 12 +++++++++ .../remote_reorder_spaces_service.dart | 27 +++++++++++++++++-- .../domain/params/reorder_spaces_param.dart | 8 +++--- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart b/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart index 692ffc0a..e07bd4a4 100644 --- a/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart +++ b/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart @@ -11,6 +11,8 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; @@ -164,6 +166,16 @@ class _CommunityStructureCanvasState extends State context.read().add( CommunitiesUpdateCommunity(newCommunity), ); + + context.read().add( + ReorderSpacesEvent( + ReorderSpacesParam( + communityUuid: widget.community.uuid, + parentSpaceUuid: data.parent?.uuid ?? '', + spaces: children, + ), + ), + ); } void _onSpaceTapped(SpaceModel? space) { diff --git a/lib/pages/space_management_v2/modules/reorder_spaces/data/services/remote_reorder_spaces_service.dart b/lib/pages/space_management_v2/modules/reorder_spaces/data/services/remote_reorder_spaces_service.dart index 463b31ce..c2494c09 100644 --- a/lib/pages/space_management_v2/modules/reorder_spaces/data/services/remote_reorder_spaces_service.dart +++ b/lib/pages/space_management_v2/modules/reorder_spaces/data/services/remote_reorder_spaces_service.dart @@ -1,4 +1,5 @@ import 'package:dio/dio.dart'; +import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/services/reorder_spaces_service.dart'; import 'package:syncrow_web/services/api/api_exception.dart'; @@ -14,8 +15,8 @@ final class RemoteReorderSpacesService implements ReorderSpacesService { Future reorderSpaces(ReorderSpacesParam param) async { try { await _httpClient.post( - path: ApiEndpoints.reorderSpaces, - body: param, + path: await _makeUrl(param), + body: param.toJson(), expectedResponseModel: (json) => json, ); } on DioException catch (e) { @@ -32,4 +33,26 @@ final class RemoteReorderSpacesService implements ReorderSpacesService { final errorMessage = error?['message'] as String? ?? ''; return errorMessage; } + + Future _makeUrl(ReorderSpacesParam param) async { + final projectUuid = await ProjectManager.getProjectUUID(); + final communityUuid = param.communityUuid; + + if (projectUuid == null || projectUuid.isEmpty) { + throw APIException('Project UUID is not set'); + } + + if (communityUuid.isEmpty) { + throw APIException('Community UUID is not set'); + } + + if (param.parentSpaceUuid.isEmpty) { + throw APIException('Parent Space UUID is not set'); + } + + return ApiEndpoints.reorderSpaces + .replaceAll('{projectUuid}', projectUuid) + .replaceAll('{communityUuid}', communityUuid) + .replaceAll('{parentSpaceUuid}', param.parentSpaceUuid); + } } diff --git a/lib/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart b/lib/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart index e18544a8..05316006 100644 --- a/lib/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart +++ b/lib/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart @@ -3,14 +3,14 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain class ReorderSpacesParam extends Equatable { const ReorderSpacesParam({ - required this.spaces, required this.communityUuid, - this.parentSpaceUuid, + required this.parentSpaceUuid, + required this.spaces, }); - final List spaces; final String communityUuid; - final String? parentSpaceUuid; + final String parentSpaceUuid; + final List spaces; @override List get props => [spaces, communityUuid, parentSpaceUuid]; From 9d60f913ebbbf85805813310fff4b8bfd3a73833 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 22 Jul 2025 09:32:57 +0300 Subject: [PATCH 61/71] Refactor CommunityStructureCanvas to simplify DragTarget logic by replacing SizedBox with SizedBox.shrink() for better performance and readability. --- .../main_module/widgets/community_structure_canvas.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart b/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart index e07bd4a4..a4d941bc 100644 --- a/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart +++ b/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart @@ -425,18 +425,14 @@ class _CommunityStructureCanvasState extends State height: _cardHeight, child: DragTarget( builder: (context, candidateData, rejectedData) { - if (_draggedData == null) { - return const SizedBox(); - } + if (_draggedData == null) return const SizedBox.shrink(); final isTargetForDragged = (_draggedData?.parent?.uuid == parent?.uuid && _draggedData?.community == null) || (_draggedData?.community?.uuid == community?.uuid && _draggedData?.parent == null); - if (!isTargetForDragged) { - return const SizedBox(); - } + if (!isTargetForDragged) return const SizedBox.shrink(); return Container( width: 40, From 60b8ee8b5036623fd57ecc95d7b312a50bc371d8 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 22 Jul 2025 09:37:31 +0300 Subject: [PATCH 62/71] Enhance DragTarget logic in CommunityStructureCanvas by refining conditions for rendering and improving readability. Ensure proper handling of dragged data and its parent/community relationships. --- .../widgets/community_structure_canvas.dart | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart b/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart index a4d941bc..37fa027b 100644 --- a/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart +++ b/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart @@ -425,17 +425,29 @@ class _CommunityStructureCanvasState extends State height: _cardHeight, child: DragTarget( builder: (context, candidateData, rejectedData) { - if (_draggedData == null) return const SizedBox.shrink(); + if (_draggedData == null) { + return const SizedBox.shrink(); + } - final isTargetForDragged = (_draggedData?.parent?.uuid == parent?.uuid && - _draggedData?.community == null) || - (_draggedData?.community?.uuid == community?.uuid && - _draggedData?.parent == null); + final children = parent?.children ?? community?.spaces ?? []; + final isSameParent = (_draggedData!.parent?.uuid == parent?.uuid && + _draggedData!.community == null) || + (_draggedData!.community?.uuid == community?.uuid && + _draggedData!.parent == null); - if (!isTargetForDragged) return const SizedBox.shrink(); + if (!isSameParent) { + return const SizedBox.shrink(); + } + + final oldIndex = + children.indexWhere((s) => s.uuid == _draggedData!.space.uuid); + if (oldIndex != -1 && (oldIndex == index || oldIndex == index - 1)) { + return const SizedBox.shrink(); + } return Container( width: 40, + alignment: Alignment.center, height: _cardHeight, decoration: BoxDecoration( color: context.theme.colorScheme.primary.withValues( @@ -462,6 +474,9 @@ class _CommunityStructureCanvasState extends State final oldIndex = children.indexWhere((s) => s.uuid == data.data.space.uuid); + if (oldIndex == -1) { + return true; + } if (oldIndex == index || oldIndex == index - 1) { return false; } From dfd8c5fa317c4e9230df4e69db4cf6817781b382 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 22 Jul 2025 09:46:56 +0300 Subject: [PATCH 63/71] Replace Container with AnimatedContainer in CommunityStructureCanvas to enhance visual feedback during state changes. Adjust alpha value for improved visibility based on candidate data presence. --- .../main_module/widgets/community_structure_canvas.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart b/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart index 37fa027b..1d4b3e10 100644 --- a/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart +++ b/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart @@ -445,13 +445,14 @@ class _CommunityStructureCanvasState extends State return const SizedBox.shrink(); } - return Container( + return AnimatedContainer( + duration: const Duration(milliseconds: 150), width: 40, alignment: Alignment.center, height: _cardHeight, decoration: BoxDecoration( color: context.theme.colorScheme.primary.withValues( - alpha: candidateData.isNotEmpty ? 0.7 : 0.3, + alpha: candidateData.isNotEmpty ? 0.9 : 0.3, ), borderRadius: BorderRadius.circular(8), ), From c0b74162e95fdca1e5293b4a1b3d00e2c8b34f8b Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 22 Jul 2025 09:56:32 +0300 Subject: [PATCH 64/71] design fixes --- assets/icons/search_icon.svg | 4 + ...okable_space_switch_activation_widget.dart | 2 +- .../widgets/booking_period_widget.dart | 29 ++++- .../widgets/check_box_space_widget.dart | 15 ++- .../widgets/custom_checkbox_widget.dart | 53 +++++++++ .../edit_bookable_space_button_widget.dart | 4 +- .../top_part_widget.dart | 1 + .../widgets/points_part_widget.dart | 7 +- .../search_unbookable_spaces_widget.dart | 39 +++++-- .../widgets/step_two_details_widget.dart | 2 +- .../widgets/stepper_part_widget.dart | 4 +- .../widgets/time_picker_widget.dart | 1 - .../widgets/week_checkbox_title_widget.dart | 105 +++++++++++------- lib/utils/color_manager.dart | 3 + 14 files changed, 198 insertions(+), 71 deletions(-) create mode 100644 assets/icons/search_icon.svg create mode 100644 lib/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_checkbox_widget.dart diff --git a/assets/icons/search_icon.svg b/assets/icons/search_icon.svg new file mode 100644 index 00000000..009efd91 --- /dev/null +++ b/assets/icons/search_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart index d819ca26..738a2ecc 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart @@ -47,7 +47,7 @@ class BookableSpaceSwitchActivationWidget extends StatelessWidget { return ColorsManager.whiteColors; }), value: space.spaceConfig!.availability, - activeTrackColor: ColorsManager.blueColor, + activeTrackColor: ColorsManager.dialogBlueTitle, inactiveTrackColor: ColorsManager.grayBorder, thumbColor: WidgetStateProperty.resolveWith( (Set states) { diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart index 439bda04..f81e1c8c 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart @@ -32,12 +32,22 @@ class BookingPeriodWidget extends StatelessWidget { const Text('Booking Period'), ], ), + const SizedBox( + height: 5, + ), Container( - width: 300, + width: 230, decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: ColorsManager.graysColor, - ), + borderRadius: BorderRadius.circular(8), + color: ColorsManager.circleRolesBackground, + boxShadow: [ + BoxShadow( + offset: Offset.zero, + blurRadius: 4, + spreadRadius: 0, + color: ColorsManager.timePickerColor.withValues(alpha: 0.15), + ) + ]), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -83,9 +93,13 @@ class BookingPeriodWidget extends StatelessWidget { } }, ), + const SizedBox( + width: 10, + ), const Icon( Icons.arrow_right_alt, color: ColorsManager.grayColor, + size: 13, ), TimePickerWidget( title: editingBookableSpace == null @@ -128,8 +142,11 @@ class BookingPeriodWidget extends StatelessWidget { } }, ), + const SizedBox( + width: 15, + ), Container( - width: 50, + width: 30, height: 32, decoration: const BoxDecoration( borderRadius: BorderRadius.only( @@ -140,7 +157,7 @@ class BookingPeriodWidget extends StatelessWidget { alignment: Alignment.center, child: SvgPicture.asset( Assets.clockIcon, - height: 15, + height: 18, color: ColorsManager.blackColor.withValues(alpha: 0.4), ), ) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart index e37c8e0e..6fdb0b0d 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_checkbox_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class CheckBoxSpaceWidget extends StatelessWidget { @@ -32,7 +33,7 @@ class CheckBoxSpaceWidget extends StatelessWidget { _ => false, }; - return Checkbox( + return CustomCheckboxWidget( value: isChecked, onChanged: (value) { final bloc = context.read(); @@ -47,7 +48,7 @@ class CheckBoxSpaceWidget extends StatelessWidget { ); }, ), - const SizedBox(width: 5), + const SizedBox(width: 15), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -55,15 +56,17 @@ class CheckBoxSpaceWidget extends StatelessWidget { Text( nonBookableSpace.spaceName, style: const TextStyle( - fontWeight: FontWeight.bold, - color: ColorsManager.textGray, + fontWeight: FontWeight.w700, + fontSize: 12, + color: ColorsManager.titleGray, ), ), Text( nonBookableSpace.spaceVirtualAddress, style: const TextStyle( - fontSize: 12, - color: ColorsManager.textGray, + fontWeight: FontWeight.w400, + fontSize: 10, + color: ColorsManager.titleGray, ), ), ], diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_checkbox_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_checkbox_widget.dart new file mode 100644 index 00000000..35e600e8 --- /dev/null +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_checkbox_widget.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class CustomCheckboxWidget extends StatelessWidget { + final bool value; + final ValueChanged onChanged; + final double? outHeight; + final double? outWidth; + final double? iconSize; + const CustomCheckboxWidget({ + super.key, + required this.value, + required this.onChanged, + this.outWidth, + this.outHeight, + this.iconSize, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => onChanged(!value), + child: Container( + width: outWidth ?? 17, + height: outHeight ?? 17, + decoration: BoxDecoration( + color: value ? Colors.white : ColorsManager.checkBoxFillColor, + border: value + ? Border.all(color: ColorsManager.secondaryColor, width: 1) + : Border.all(color: ColorsManager.checkBoxBorderGray, width: 1), + borderRadius: BorderRadius.circular(4), + ), + child: value + ? Center( + child: Container( + width: outWidth != null ? outWidth! - 4 : 13, + height: outHeight != null ? outHeight! - 4 : 13, + decoration: BoxDecoration( + color: ColorsManager.secondaryColor, + borderRadius: BorderRadius.circular(2), + ), + child: const Icon( + Icons.check, + size: 12, + color: Colors.white, + ), + ), + ) + : null, + ), + ); + } +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart index 8f82e08d..b1b0ea43 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart @@ -45,12 +45,12 @@ class EditBookableSpaceButtonWidget extends StatelessWidget { }, style: ElevatedButton.styleFrom( padding: EdgeInsets.zero, - fixedSize: const Size(50, 30), + minimumSize: const Size(40, 30), elevation: 1, ), child: SvgPicture.asset( Assets.settings, - height: 15, + height: 13, color: ColorsManager.blue1, ), ), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart index 3c6694af..c0c27b1e 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart @@ -27,6 +27,7 @@ class RowOfButtonsTitleWidget extends StatelessWidget { ElevatedButton( style: ElevatedButton.styleFrom( padding: EdgeInsets.zero, + minimumSize: const Size(50, 40), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart index 1106f214..a716628e 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart @@ -65,7 +65,7 @@ class _PointsPartWidgetState extends State { (Set states) { return ColorsManager.whiteColors; }), - activeTrackColor: ColorsManager.blueColor, + activeTrackColor: ColorsManager.dialogBlueTitle, inactiveTrackColor: ColorsManager.grayBorder, thumbColor: WidgetStateProperty.resolveWith( (Set states) { @@ -109,7 +109,10 @@ class _PointsPartWidgetState extends State { if (state is ActivatePointsSwitch) SearchUnbookableSpacesWidget( title: 'Ex: 0', - height: 40, + topPadding: 0, + blur: 1, + raduis: 10, + height: 34, onChanged: (p0) { final updatedCost = int.tryParse(widget.pointsController.text) ?? 0; diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart index f2266874..ba7cea57 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart @@ -1,22 +1,30 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; class SearchUnbookableSpacesWidget extends StatelessWidget { final String title; final Widget? suffix; final double? height; final double? width; + final double? blur; + final double? raduis; + final double? topPadding; final TextEditingController? controller; final List? inputFormatters; final void Function(String)? onChanged; const SearchUnbookableSpacesWidget({ required this.title, this.controller, + this.blur, this.onChanged, this.suffix, this.height, this.width, + this.topPadding, + this.raduis, this.inputFormatters, super.key, }); @@ -25,16 +33,18 @@ class SearchUnbookableSpacesWidget extends StatelessWidget { Widget build(BuildContext context) { return Container( width: width ?? 480, - height: height ?? 30, + height: height ?? 40, padding: const EdgeInsets.only(top: 4), decoration: BoxDecoration( color: ColorsManager.whiteColors, - borderRadius: BorderRadius.circular(12), - boxShadow: const [ + borderRadius: BorderRadius.circular(raduis ?? 15), + boxShadow: [ BoxShadow( - color: ColorsManager.shadowOfSearchTextfield, - offset: Offset(0, 4), - blurRadius: 5, + color: + ColorsManager.shadowOfSearchTextfield.withValues(alpha: 0.15), + offset: Offset.zero, + blurRadius: blur ?? 5, + spreadRadius: 0, ), ], ), @@ -43,14 +53,21 @@ class SearchUnbookableSpacesWidget extends StatelessWidget { inputFormatters: inputFormatters, onChanged: onChanged, decoration: InputDecoration( - contentPadding: - const EdgeInsets.symmetric(vertical: 5, horizontal: 15), + contentPadding: EdgeInsets.symmetric( + vertical: topPadding ?? 5, + horizontal: 15, + ), hintText: title, hintStyle: const TextStyle(color: ColorsManager.hintTextGrey), border: InputBorder.none, - suffixIcon: suffix ?? - const Icon(Icons.search, - size: 20, color: ColorsManager.hintTextGrey), + suffixIcon: Padding( + padding: const EdgeInsets.all(10), + child: suffix ?? + SvgPicture.asset( + Assets.searchIcon, + height: 12, + ), + ), ), style: const TextStyle( fontSize: 14, diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart index bd5263fe..a1db9694 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart @@ -26,7 +26,7 @@ class StepTwoDetailsWidget extends StatelessWidget { editingBookableSpace: editingBookableSpace, ), const SizedBox( - height: 20, + height: 30, ), BookingPeriodWidget( editingBookableSpace: editingBookableSpace, diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart index 29e8ba42..89f9d1c5 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart @@ -26,7 +26,7 @@ class StepperPartWidget extends StatelessWidget { Container( padding: const EdgeInsets.only(left: 3), alignment: Alignment.centerLeft, - height: 50, + height: 40, child: const VerticalDivider( width: 8, )), @@ -59,7 +59,7 @@ class StepperPartWidget extends StatelessWidget { Container( padding: const EdgeInsets.only(left: 3), alignment: Alignment.centerLeft, - height: 50, + height: 40, child: const VerticalDivider( width: 8, )), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart index da94341b..b72a714d 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart @@ -53,7 +53,6 @@ class _TimePickerWidgetState extends State { setState(() {}); }, child: Container( - width: 100, height: 32, decoration: const BoxDecoration( borderRadius: BorderRadius.only( diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart index 5d689748..3673ef1c 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; +import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_checkbox_widget.dart'; class WeekDaysCheckboxRow extends StatefulWidget { final BookableSpacemodel? editingBookableSpace; @@ -40,52 +41,78 @@ class _WeekDaysCheckboxRowState extends State { @override Widget build(BuildContext context) { - return Row( + return Column( crossAxisAlignment: CrossAxisAlignment.start, - children: _daysChecked.entries.map((entry) { - return Expanded( - child: Row( - children: [ - Expanded( - child: Checkbox( - value: entry.value, - onChanged: (newValue) { - setState(() { - _daysChecked[entry.key] = newValue ?? false; - }); + children: [ + Row( + children: [ + Text( + '* ', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: Colors.red), + ), + const Text('Days'), + ], + ), + const SizedBox( + height: 20, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: _daysChecked.entries.map((entry) { + return Expanded( + child: Row( + children: [ + CustomCheckboxWidget( + outHeight: 16, + outWidth: 16, + value: entry.value, + onChanged: (newValue) { + setState(() { + _daysChecked[entry.key] = newValue ?? false; + }); - final selectedDays = _daysChecked.entries - .where((e) => e.value) - .map((e) => e.key) - .toList(); + final selectedDays = _daysChecked.entries + .where((e) => e.value) + .map((e) => e.key) + .toList(); - final bloc = context.read(); + final bloc = context.read(); - for (int i = 0; - i < bloc.selectedBookableSpaces.length; - i++) { - final space = bloc.selectedBookableSpaces[i]; - final updatedConfig = space.spaceConfig - ?.copyWith(bookableDays: selectedDays); - final updatedSpace = - space.copyWith(spaceConfig: updatedConfig); + for (int i = 0; + i < bloc.selectedBookableSpaces.length; + i++) { + final space = bloc.selectedBookableSpaces[i]; + final updatedConfig = space.spaceConfig + ?.copyWith(bookableDays: selectedDays); + final updatedSpace = + space.copyWith(spaceConfig: updatedConfig); - bloc.selectedBookableSpaces[i] = updatedSpace; - } + bloc.selectedBookableSpaces[i] = updatedSpace; + } - bloc.add(CheckConfigurValidityEvent()); - }, - ), + bloc.add(CheckConfigurValidityEvent()); + }, + ), + const SizedBox( + width: 8, + ), + Expanded( + child: Text( + entry.key, + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w400, + ), + )), + ], ), - Expanded( - child: Text( - entry.key, - style: const TextStyle(fontSize: 10), - )), - ], - ), - ); - }).toList(), + ); + }).toList(), + ), + ], ); } } diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 20f129c1..75c2d671 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -38,6 +38,7 @@ abstract class ColorsManager { static const Color lightGrayColor = Color(0xB2999999); static const Color grayBorder = Color(0xFFCFCFCF); static const Color textGray = Color(0xffD5D5D5); + static const Color titleGray = Color(0xB2999999); static const Color btnColor = Color(0xFF00008B); static const Color blueColor = Color(0xFF0036E6); static const Color boxColor = Color(0xFFF5F6F7); @@ -90,4 +91,6 @@ abstract class ColorsManager { static const Color shadowOfSearchTextfield = Color(0x26000000); static const Color hintTextGrey = Colors.grey; static const Color shadowOfDetailsContainer = Color(0x40000000); + static const Color checkBoxBorderGray = Color(0xffD0D0D0); + static const Color timePickerColor = Color(0xff000000); } From 94f9c1beea313d707d40d88a9fa82540f452d14f Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 22 Jul 2025 10:04:38 +0300 Subject: [PATCH 65/71] Adjust layout in CommunityStructureCanvas by adding horizontal padding to positions and refining target position calculations for improved spacing and alignment. Enhance Stack widget behavior by allowing overflow clipping. --- .../widgets/community_structure_canvas.dart | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart b/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart index 1d4b3e10..6614aa88 100644 --- a/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart +++ b/lib/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart @@ -257,6 +257,13 @@ class _CommunityStructureCanvasState extends State final levelXOffset = {}; _calculateLayout(community.spaces, 0, levelXOffset); + const horizontalCanvasPadding = 100.0; + final originalPositions = Map.of(_positions); + _positions.clear(); + for (final entry in originalPositions.entries) { + _positions[entry.key] = entry.value.translate(horizontalCanvasPadding, 0); + } + final selectedSpace = widget.selectedSpace; final highlightedUuids = {}; if (selectedSpace != null) { @@ -274,7 +281,7 @@ class _CommunityStructureCanvasState extends State community: widget.community, ); - final createButtonX = levelXOffset[0] ?? 0.0; + final createButtonX = (levelXOffset[0] ?? 0.0) + horizontalCanvasPadding; const createButtonY = 0.0; widgets.add( @@ -306,10 +313,12 @@ class _CommunityStructureCanvasState extends State CommunityModel? community, SpaceModel? parent, }) { + const targetWidth = 40.0; + final padding = (_horizontalSpacing - targetWidth) / 2; if (spaces.isNotEmpty) { final firstChildPos = _positions[spaces.first.uuid]!; final targetPos = Offset( - firstChildPos.dx - (_horizontalSpacing / 4), + firstChildPos.dx - padding - targetWidth, firstChildPos.dy, ); widgets.add(_buildDropTarget(parent, community, 0, targetPos)); @@ -391,7 +400,7 @@ class _CommunityStructureCanvasState extends State ); final targetPos = Offset( - position.dx + cardWidth + (_horizontalSpacing / 4) - 20, + position.dx + cardWidth + padding, position.dy, ); widgets.add(_buildDropTarget(parent, community, i + 1, targetPos)); @@ -505,7 +514,7 @@ class _CommunityStructureCanvasState extends State child: SizedBox( width: context.screenWidth * 5, height: context.screenHeight * 5, - child: Stack(children: treeWidgets), + child: Stack(clipBehavior: Clip.none, children: treeWidgets), ), ), ); From a64a9f1d12ecc713ca07976c0b618c9504dc0233 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 22 Jul 2025 10:12:02 +0300 Subject: [PATCH 66/71] fix unactive color --- .../widgets/points_part_widget.dart | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart index a716628e..0dcad1f2 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart @@ -61,16 +61,19 @@ class _PointsPartWidgetState extends State { Transform.scale( scale: 0.7, child: Switch( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, trackOutlineColor: WidgetStateProperty.resolveWith( - (Set states) { - return ColorsManager.whiteColors; - }), + (Set states) { + return ColorsManager.whiteColors; + }, + ), activeTrackColor: ColorsManager.dialogBlueTitle, - inactiveTrackColor: ColorsManager.grayBorder, + inactiveTrackColor: ColorsManager.lightGrayBorderColor, thumbColor: WidgetStateProperty.resolveWith( - (Set states) { - return ColorsManager.whiteColors; - }), + (Set states) { + return ColorsManager.whiteColors; + }, + ), value: state is ActivatePointsSwitch, onChanged: (value) { final bloc = context.read(); From 066fe4bc95c29d9d05523959b4476fa412934f0c Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 22 Jul 2025 10:35:06 +0300 Subject: [PATCH 67/71] make variable final --- .../domain/params/non_bookable_spaces_params.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart b/lib/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart index 9f567a6c..a081ce04 100644 --- a/lib/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart +++ b/lib/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart @@ -1,6 +1,6 @@ class NonBookableSpacesParams { final int currentPage; - String? searchedWords; +final String? searchedWords; NonBookableSpacesParams({ required this.currentPage, this.searchedWords, From 2b110b7c91aedfe3975524dc0aa7dfbc66c634db Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 22 Jul 2025 10:50:00 +0300 Subject: [PATCH 68/71] fix pagination UI --- .../bottom_pagination_part_widget.dart | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart index cb7bcc33..b41fe106 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart @@ -66,11 +66,13 @@ class PaginationButtonsWidget extends StatelessWidget { height: 30, alignment: Alignment.center, decoration: BoxDecoration( - color: i == currentPage - ? ColorsManager.dialogBlueTitle - : Colors.grey[300], - borderRadius: BorderRadius.circular(8), - ), + color: i == currentPage + ? ColorsManager.dialogBlueTitle + : ColorsManager.whiteColors, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: ColorsManager.lightGrayBorderColor, + )), child: Text( '$i', style: TextStyle( @@ -134,11 +136,15 @@ class PaginationButtonsWidget extends StatelessWidget { child: GestureDetector( onTap: onTap, child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + width: 30, + height: 30, + alignment: Alignment.center, decoration: BoxDecoration( - color: Colors.grey[300], - borderRadius: BorderRadius.circular(8), - ), + color: ColorsManager.whiteColors, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: ColorsManager.lightGrayBorderColor, + )), child: Text( label, style: const TextStyle( From b6d4084ca7a9dd680016e9eba3cd97d9dd616e99 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 22 Jul 2025 10:58:30 +0300 Subject: [PATCH 69/71] fix settings and back button to meet the UI --- .../edit_bookable_space_button_widget.dart | 75 +++++++++++-------- .../top_part_widget.dart | 44 +++++++---- 2 files changed, 75 insertions(+), 44 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart index b1b0ea43..2a014f75 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart @@ -20,38 +20,53 @@ class EditBookableSpaceButtonWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Center( - child: ElevatedButton( - onPressed: () { - final bookableBloc = context.read(); - - showDialog( - context: context, - builder: (context) => MultiBlocProvider( - providers: [ - BlocProvider.value( - value: bookableBloc, - ), - BlocProvider( - create: (context) => UpdateBookableSpacesBloc( - RemoteUpdateBookableSpaceService(HTTPService()), - ), - ), - ], - child: SetupBookableSpacesDialog( - editingBookableSpace: space, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + offset: Offset.zero, + blurRadius: 3, + spreadRadius: 0, + color: ColorsManager.timePickerColor.withValues( + alpha: 0.3, ), - ), - ); - }, - style: ElevatedButton.styleFrom( - padding: EdgeInsets.zero, - minimumSize: const Size(40, 30), - elevation: 1, + ) + ], ), - child: SvgPicture.asset( - Assets.settings, - height: 13, - color: ColorsManager.blue1, + child: ElevatedButton( + onPressed: () { + final bookableBloc = context.read(); + + showDialog( + context: context, + builder: (context) => MultiBlocProvider( + providers: [ + BlocProvider.value( + value: bookableBloc, + ), + BlocProvider( + create: (context) => UpdateBookableSpacesBloc( + RemoteUpdateBookableSpaceService(HTTPService()), + ), + ), + ], + child: SetupBookableSpacesDialog( + editingBookableSpace: space, + ), + ), + ); + }, + style: ElevatedButton.styleFrom( + padding: EdgeInsets.zero, + minimumSize: const Size(45, 30), + elevation: 0, + ), + child: SvgPicture.asset( + Assets.settings, + height: 13, + color: ColorsManager.blue1, + ), ), ), ); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart index c0c27b1e..65a647a5 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart @@ -24,21 +24,37 @@ class RowOfButtonsTitleWidget extends StatelessWidget { children: [ Row( children: [ - ElevatedButton( - style: ElevatedButton.styleFrom( - padding: EdgeInsets.zero, - minimumSize: const Size(50, 40), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + offset: Offset.zero, + blurRadius: 3, + spreadRadius: 0, + color: ColorsManager.timePickerColor.withValues( + alpha: 0.3, + ), + ) + ], + ), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + padding: EdgeInsets.zero, + minimumSize: const Size(50, 40), + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), ), - ), - child: SvgPicture.asset( - Assets.backButtonIcon, - height: 15, - ), - onPressed: () { - pageController.jumpToPage(1); - }), + child: SvgPicture.asset( + Assets.backButtonIcon, + height: 15, + ), + onPressed: () { + pageController.jumpToPage(1); + }), + ), const SizedBox( width: 10, ), From 6a7174b929393786c94c992b66b10b34c692ac29 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 22 Jul 2025 16:47:50 +0300 Subject: [PATCH 70/71] no need for selectedbookableSpaces list anymore instead i am using state list --- .../setup_bookable_spaces_bloc.dart | 112 ++++++++++--- .../setup_bookable_spaces_event.dart | 28 ++++ .../setup_bookable_spaces_state.dart | 33 ++-- .../screens/setup_bookable_spaces_dialog.dart | 17 +- .../widgets/booking_period_widget.dart | 155 +++++++----------- .../widgets/check_box_space_widget.dart | 9 +- .../widgets/next_first_step_button.dart | 40 +++-- .../widgets/points_part_widget.dart | 91 ++++------ .../widgets/save_second_step_button.dart | 74 ++++----- .../widgets/time_picker_widget.dart | 7 +- .../widgets/unbookable_list_widget.dart | 4 +- .../widgets/week_checkbox_title_widget.dart | 43 ++--- 12 files changed, 331 insertions(+), 282 deletions(-) diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart index 2212da77..7ba4eb0a 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart @@ -10,51 +10,123 @@ part 'setup_bookable_spaces_state.dart'; class SetupBookableSpacesBloc extends Bloc { NonBookableSpacesService nonBookableSpacesService; - List selectedBookableSpaces = []; + SetupBookableSpacesBloc(this.nonBookableSpacesService) - : super(SetupBookableSpacesInitial()) { + : super(const SetupBookableSpacesInitial(bookableSpaces: [])) { on(_onAddToBookableSpaceEvent); on(_onRemoveFromBookableSpaceEvent); + on(_onAddBookableDays); + on(_onChangeStartTimeEvent); + on(_onChangeEndTimeEvent); + on(_onChangeCostEvent); on(_onCheckConfigurValidityEvent); on(_onEditModeSelected); } - TimeOfDay? get endTime => - selectedBookableSpaces.first.spaceConfig!.bookingEndTime; - - TimeOfDay? get startTime => - selectedBookableSpaces.first.spaceConfig!.bookingStartTime; + List get currentBookableSpaces { + return switch (state) { + AddNonBookableSpaceIntoBookableState(:final bookableSpaces) => + bookableSpaces, + RemoveBookableSpaceIntoNonBookableState(:final bookableSpaces) => + bookableSpaces, + SetupBookableSpacesInitial(:final bookableSpaces) => bookableSpaces, + InProgressState(:final bookableSpaces) => bookableSpaces, + ValidSaveButtonState(:final bookableSpaces) => bookableSpaces, + UnValidSaveButtonState(:final bookableSpaces) => bookableSpaces, + }; + } void _onAddToBookableSpaceEvent( AddToBookableSpaceEvent event, Emitter emit, ) { - emit(InProgressState()); - selectedBookableSpaces.add(event.nonBookableSpace); - emit(AddNonBookableSpaceIntoBookableState( - bookableSpaces: selectedBookableSpaces)); + emit(InProgressState(bookableSpaces: state.bookableSpaces)); + final updatedSpaces = List.from(state.bookableSpaces); + + updatedSpaces.add(event.nonBookableSpace); + + emit(AddNonBookableSpaceIntoBookableState(bookableSpaces: updatedSpaces)); } void _onRemoveFromBookableSpaceEvent(RemoveFromBookableSpaceEvent event, Emitter emit) { - emit(InProgressState()); - selectedBookableSpaces.remove(event.bookableSpace); + emit(InProgressState(bookableSpaces: state.bookableSpaces)); + state.bookableSpaces.remove(event.bookableSpace); emit(RemoveBookableSpaceIntoNonBookableState( - bookableSpaces: selectedBookableSpaces)); + bookableSpaces: state.bookableSpaces)); + } + + void _onAddBookableDays( + AddBookableDaysEvent event, Emitter emit) { + final updatedSpaces = state.bookableSpaces.map((space) { + final updatedConfig = space.spaceConfig?.copyWith( + bookableDays: event.bookableDays, + ); + + return space.copyWith(spaceConfig: updatedConfig); + }).toList(); + + emit(SetupBookableSpacesInitial(bookableSpaces: updatedSpaces)); + } + + void _onChangeStartTimeEvent( + ChangeStartTimeEvent event, Emitter emit) { + final updatedSpaces = state.bookableSpaces.map((space) { + final updatedConfig = space.spaceConfig?.copyWith( + bookingStartTime: event.startTime, + ); + + return space.copyWith(spaceConfig: updatedConfig); + }).toList(); + + emit(SetupBookableSpacesInitial(bookableSpaces: updatedSpaces)); + } + + void _onChangeEndTimeEvent( + ChangeEndTimeEvent event, Emitter emit) { + final updatedSpaces = state.bookableSpaces.map((space) { + final updatedConfig = space.spaceConfig?.copyWith( + bookingEndTime: event.endTime, + ); + + return space.copyWith(spaceConfig: updatedConfig); + }).toList(); + + emit(SetupBookableSpacesInitial(bookableSpaces: updatedSpaces)); + } + + void _onChangeCostEvent( + ChangeCostEvent event, Emitter emit) { + final updatedSpaces = state.bookableSpaces.map((space) { + final updatedConfig = space.spaceConfig?.copyWith( + cost: event.cost, + ); + + return space.copyWith(spaceConfig: updatedConfig); + }).toList(); + + emit(SetupBookableSpacesInitial(bookableSpaces: updatedSpaces)); } void _onCheckConfigurValidityEvent(CheckConfigurValidityEvent event, Emitter emit) { - if (selectedBookableSpaces.first.spaceConfig!.isValid) { - emit(ValidSaveButtonState()); + if (state.bookableSpaces.first.spaceConfig!.isValid) { + emit(ValidSaveButtonState( + bookableSpaces: state.bookableSpaces, + )); } else { - emit(UnValidSaveButtonState()); + emit(UnValidSaveButtonState( + bookableSpaces: state.bookableSpaces, + )); } } void _onEditModeSelected( - EditModeSelected event, Emitter emit) { - selectedBookableSpaces.clear(); - selectedBookableSpaces.add(event.editingBookableSpace); + EditModeSelected event, + Emitter emit, + ) { + final updatedList = [event.editingBookableSpace]; + + emit(SetupBookableSpacesInitial(bookableSpaces: updatedList)); } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_event.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_event.dart index 68deee73..8bc66f95 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_event.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_event.dart @@ -21,6 +21,34 @@ class RemoveFromBookableSpaceEvent extends SetupBookableSpacesEvent { }); } +class AddBookableDaysEvent extends SetupBookableSpacesEvent { + final List bookableDays; + const AddBookableDaysEvent({ + required this.bookableDays, + }); +} + +class ChangeCostEvent extends SetupBookableSpacesEvent { + final int cost; + const ChangeCostEvent({ + required this.cost, + }); +} + +class ChangeStartTimeEvent extends SetupBookableSpacesEvent { + final TimeOfDay startTime; + const ChangeStartTimeEvent({ + required this.startTime, + }); +} + +class ChangeEndTimeEvent extends SetupBookableSpacesEvent { + final TimeOfDay endTime; + const ChangeEndTimeEvent({ + required this.endTime, + }); +} + class CheckConfigurValidityEvent extends SetupBookableSpacesEvent {} class EditModeSelected extends SetupBookableSpacesEvent { diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_state.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_state.dart index b6dbad70..5b04f075 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_state.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_state.dart @@ -1,32 +1,37 @@ part of 'setup_bookable_spaces_bloc.dart'; sealed class SetupBookableSpacesState extends Equatable { - const SetupBookableSpacesState(); - + final List bookableSpaces; + const SetupBookableSpacesState({required this.bookableSpaces}); + TimeOfDay? get startTime => + bookableSpaces.first.spaceConfig!.bookingStartTime; + TimeOfDay? get endTime => bookableSpaces.first.spaceConfig!.bookingEndTime; @override List get props => []; } -final class SetupBookableSpacesInitial extends SetupBookableSpacesState {} +final class SetupBookableSpacesInitial extends SetupBookableSpacesState { + const SetupBookableSpacesInitial({required super.bookableSpaces}); +} class AddNonBookableSpaceIntoBookableState extends SetupBookableSpacesState { - final List bookableSpaces; - const AddNonBookableSpaceIntoBookableState({ - required this.bookableSpaces, - }); + const AddNonBookableSpaceIntoBookableState({required super.bookableSpaces}); } -class InProgressState extends SetupBookableSpacesState {} +class InProgressState extends SetupBookableSpacesState { + const InProgressState({required super.bookableSpaces}); +} class RemoveBookableSpaceIntoNonBookableState extends SetupBookableSpacesState { - final List bookableSpaces; const RemoveBookableSpaceIntoNonBookableState({ - required this.bookableSpaces, + required super.bookableSpaces, }); } -class ValidSaveButtonState extends SetupBookableSpacesState {} - -class UnValidSaveButtonState extends SetupBookableSpacesState {} - +class ValidSaveButtonState extends SetupBookableSpacesState { + const ValidSaveButtonState({required super.bookableSpaces}); +} +class UnValidSaveButtonState extends SetupBookableSpacesState { + const UnValidSaveButtonState({required super.bookableSpaces}); +} diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart index a0e8da84..07be929f 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart @@ -63,9 +63,10 @@ class _SetupBookableSpacesDialogState extends State { RemoteNonBookableSpaces(HTTPService())) : (context) => SetupBookableSpacesBloc( RemoteNonBookableSpaces(HTTPService())) - ..add(EditModeSelected( - editingBookableSpace: widget.editingBookableSpace!, - )), + ..add( + EditModeSelected( + editingBookableSpace: widget.editingBookableSpace!), + ), ), BlocProvider( create: (context) => SendBookableSpacesBloc( @@ -115,16 +116,14 @@ class _SetupBookableSpacesDialogState extends State { ), Builder(builder: (context) { final stepsState = context.watch().state; - final setupBookableSpacesBloc = - context.watch(); - final selectedSpaces = - setupBookableSpacesBloc.selectedBookableSpaces; + final bookableSpaces = + context.watch().state.bookableSpaces; return stepsState is StepOneState - ? NextFirstStepButton(selectedSpaces: selectedSpaces) + ? const NextFirstStepButton() : SaveSecondStepButton( - selectedSpaces: selectedSpaces, pointsController: pointsController, isEditingMode: widget.editingBookableSpace != null, + bookableSpaces: bookableSpaces, ); }), ], diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart index f81e1c8c..c434fc3d 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart @@ -17,6 +17,8 @@ class BookingPeriodWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final state = context.watch().state; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -32,139 +34,108 @@ class BookingPeriodWidget extends StatelessWidget { const Text('Booking Period'), ], ), - const SizedBox( - height: 5, - ), + const SizedBox(height: 5), Container( width: 230, decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - color: ColorsManager.circleRolesBackground, - boxShadow: [ - BoxShadow( - offset: Offset.zero, - blurRadius: 4, - spreadRadius: 0, - color: ColorsManager.timePickerColor.withValues(alpha: 0.15), - ) - ]), + borderRadius: BorderRadius.circular(8), + color: ColorsManager.circleRolesBackground, + boxShadow: [ + BoxShadow( + offset: Offset.zero, + blurRadius: 4, + spreadRadius: 0, + color: ColorsManager.timePickerColor.withValues(alpha: 0.15), + ) + ], + ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ TimePickerWidget( - title: editingBookableSpace == null - ? 'Start Time' - : editingBookableSpace!.spaceConfig!.bookingStartTime! - .format(context), - onTimePicked: (timePicked) { - if (timePicked == null) { + title: editingBookableSpace?.spaceConfig?.bookingStartTime + ?.format(context) ?? + 'Start Time', + onTimePicked: (pickedStartTime) { + if (pickedStartTime == null) return; + + if (state.endTime != null && + isEndTimeAfterStartTime( + pickedStartTime, state.endTime!)) { + _showInvalidSnackBar( + context, "You can't choose Start Time after End Time"); return; } - final setupBookableSpacesBloc = - context.read(); - if (setupBookableSpacesBloc.endTime != null && - isEndTimeAfterStartTime( - timePicked, setupBookableSpacesBloc.endTime!)) { - ScaffoldMessenger.of(context).clearSnackBars(); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: - Text("You can't choose start Time Before End time"), - duration: Duration(seconds: 2), - backgroundColor: ColorsManager.red, - )); - throw Exception(); - } else { - for (int i = 0; - i < - setupBookableSpacesBloc - .selectedBookableSpaces.length; - i++) { - final space = - setupBookableSpacesBloc.selectedBookableSpaces[i]; - final updatedConfig = space.spaceConfig - ?.copyWith(bookingStartTime: timePicked); - final updatedSpace = - space.copyWith(spaceConfig: updatedConfig); - - setupBookableSpacesBloc.selectedBookableSpaces[i] = - updatedSpace; - } - } + context.read().add( + ChangeStartTimeEvent(startTime: pickedStartTime), + ); + context.read().add( + CheckConfigurValidityEvent(), + ); }, ), - const SizedBox( - width: 10, - ), + const SizedBox(width: 10), const Icon( Icons.arrow_right_alt, color: ColorsManager.grayColor, size: 13, ), TimePickerWidget( - title: editingBookableSpace == null - ? 'End Time' - : editingBookableSpace!.spaceConfig!.bookingEndTime! - .format(context), - onTimePicked: (timePicked) { - if (timePicked == null) { + title: editingBookableSpace?.spaceConfig?.bookingEndTime + ?.format(context) ?? + 'End Time', + onTimePicked: (pickedEndTime) { + if (pickedEndTime == null) return; + + if (state.startTime != null && + isEndTimeAfterStartTime( + state.startTime!, pickedEndTime)) { + _showInvalidSnackBar( + context, "You can't choose End Time before Start Time"); return; } - final setupBookableSpacesBloc = - context.read(); - if (setupBookableSpacesBloc.startTime != null && - isEndTimeAfterStartTime( - setupBookableSpacesBloc.startTime!, timePicked)) { - ScaffoldMessenger.of(context).clearSnackBars(); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: - Text("You can't choose End Time After Start time"), - duration: Duration(seconds: 2), - backgroundColor: ColorsManager.red, - )); - throw Exception(); - } else { - for (int i = 0; - i < - setupBookableSpacesBloc - .selectedBookableSpaces.length; - i++) { - final space = - setupBookableSpacesBloc.selectedBookableSpaces[i]; - final updatedConfig = space.spaceConfig - ?.copyWith(bookingEndTime: timePicked); - final updatedSpace = - space.copyWith(spaceConfig: updatedConfig); - setupBookableSpacesBloc.selectedBookableSpaces[i] = - updatedSpace; - } - } + context.read().add( + ChangeEndTimeEvent(endTime: pickedEndTime), + ); + context.read().add( + CheckConfigurValidityEvent(), + ); }, ), - const SizedBox( - width: 15, - ), + const SizedBox(width: 15), Container( width: 30, height: 32, + alignment: Alignment.center, decoration: const BoxDecoration( borderRadius: BorderRadius.only( topLeft: Radius.circular(10), bottomLeft: Radius.circular(10), ), ), - alignment: Alignment.center, child: SvgPicture.asset( Assets.clockIcon, height: 18, color: ColorsManager.blackColor.withValues(alpha: 0.4), ), - ) + ), ], ), ), ], ); } + + void _showInvalidSnackBar(BuildContext context, String message) { + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + duration: const Duration(seconds: 2), + backgroundColor: ColorsManager.red, + ), + ); + } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart index 6fdb0b0d..862b11fa 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart @@ -7,12 +7,10 @@ import 'package:syncrow_web/utils/color_manager.dart'; class CheckBoxSpaceWidget extends StatelessWidget { final BookableSpacemodel nonBookableSpace; - final List selectedSpaces; const CheckBoxSpaceWidget({ super.key, required this.nonBookableSpace, - required this.selectedSpaces, }); @override @@ -37,12 +35,15 @@ class CheckBoxSpaceWidget extends StatelessWidget { value: isChecked, onChanged: (value) { final bloc = context.read(); + if (value ?? false) { bloc.add(AddToBookableSpaceEvent( - nonBookableSpace: nonBookableSpace)); + nonBookableSpace: nonBookableSpace, + )); } else { bloc.add(RemoveFromBookableSpaceEvent( - bookableSpace: nonBookableSpace)); + bookableSpace: nonBookableSpace, + )); } }, ); diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart index 23c759a8..12e6bb2e 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart @@ -1,32 +1,42 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; -import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart'; import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart'; class NextFirstStepButton extends StatelessWidget { - final List selectedSpaces; - const NextFirstStepButton({ super.key, - required this.selectedSpaces, }); @override Widget build(BuildContext context) { - return ButtonsDividerBottomDialogWidget( - title: 'Next', - onNextPressed: selectedSpaces.isEmpty - ? null - : () { - context.read().goToNextStep(); - context - .read() - .add(CheckConfigurValidityEvent()); - }, - onCancelPressed: () => context.pop(), + return BlocBuilder( + builder: (context, state) { + return switch (state) { + SetupBookableSpacesInitial() => ButtonsDividerBottomDialogWidget( + title: 'Next', + onNextPressed: null, + onCancelPressed: () => context.pop(), + ), + AddNonBookableSpaceIntoBookableState(:final bookableSpaces) || + RemoveBookableSpaceIntoNonBookableState(:final bookableSpaces) => + ButtonsDividerBottomDialogWidget( + title: 'Next', + onNextPressed: bookableSpaces.isEmpty + ? null + : () { + context.read().goToNextStep(); + context.read().add( + CheckConfigurValidityEvent(), + ); + }, + onCancelPressed: () => context.pop(), + ), + _ => const SizedBox(), + }; + }, ); } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart index 0dcad1f2..3df5343d 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart @@ -9,14 +9,14 @@ import 'package:syncrow_web/utils/color_manager.dart'; class PointsPartWidget extends StatefulWidget { final BookableSpacemodel? editingBookableSpace; + final TextEditingController pointsController; + const PointsPartWidget({ super.key, required this.pointsController, this.editingBookableSpace, }); - final TextEditingController pointsController; - @override State createState() => _PointsPartWidgetState(); } @@ -24,26 +24,28 @@ class PointsPartWidget extends StatefulWidget { class _PointsPartWidgetState extends State { @override void initState() { + super.initState(); + if (widget.editingBookableSpace != null) { widget.pointsController.text = widget.editingBookableSpace!.spaceConfig!.cost.toString(); } - super.initState(); } @override Widget build(BuildContext context) { return BlocBuilder( - builder: (context, state) { + builder: (context, switchState) { + final isSwitchOn = switchState is ActivatePointsSwitch; + return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( - mainAxisAlignment: MainAxisAlignment.start, children: [ - if (state is ActivatePointsSwitch) + if (isSwitchOn) Text( '* ', style: Theme.of(context) @@ -52,9 +54,7 @@ class _PointsPartWidgetState extends State { .copyWith(color: Colors.red), ) else - const SizedBox( - width: 11, - ), + const SizedBox(width: 11), const Text('Points/hrs'), ], ), @@ -62,83 +62,54 @@ class _PointsPartWidgetState extends State { scale: 0.7, child: Switch( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - trackOutlineColor: WidgetStateProperty.resolveWith( - (Set states) { - return ColorsManager.whiteColors; - }, - ), + trackOutlineColor: + WidgetStateProperty.all(ColorsManager.whiteColors), activeTrackColor: ColorsManager.dialogBlueTitle, inactiveTrackColor: ColorsManager.lightGrayBorderColor, - thumbColor: WidgetStateProperty.resolveWith( - (Set states) { - return ColorsManager.whiteColors; - }, - ), - value: state is ActivatePointsSwitch, + thumbColor: + WidgetStateProperty.all(ColorsManager.whiteColors), + value: isSwitchOn, onChanged: (value) { + final toggleCubit = + context.read(); final bloc = context.read(); + final updatedCost = value ? -1 : 0; - final switchCubit = - context.read(); if (value) { - switchCubit.activateSwitch(); + toggleCubit.activateSwitch(); } else { - switchCubit.unActivateSwitch(); + toggleCubit.unActivateSwitch(); widget.pointsController.clear(); } - - for (int i = 0; - i < bloc.selectedBookableSpaces.length; - i++) { - final space = bloc.selectedBookableSpaces[i]; - final updatedConfig = - space.spaceConfig?.copyWith(cost: updatedCost); - final updatedSpace = - space.copyWith(spaceConfig: updatedConfig); - - bloc.selectedBookableSpaces[i] = updatedSpace; - } - + bloc.add(ChangeCostEvent(cost: updatedCost)); bloc.add(CheckConfigurValidityEvent()); }, ), - ) + ), ], ), - const SizedBox( - height: 5, - ), - if (state is ActivatePointsSwitch) + const SizedBox(height: 5), + if (isSwitchOn) SearchUnbookableSpacesWidget( title: 'Ex: 0', topPadding: 0, blur: 1, raduis: 10, height: 34, - onChanged: (p0) { + controller: widget.pointsController, + suffix: const SizedBox(), + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + onChanged: (_) { final updatedCost = int.tryParse(widget.pointsController.text) ?? 0; - - final bloc = context.read(); - - for (var i = 0; i < bloc.selectedBookableSpaces.length; i++) { - final space = bloc.selectedBookableSpaces[i]; - final updatedConfig = - space.spaceConfig?.copyWith(cost: updatedCost); - final updatedSpace = - space.copyWith(spaceConfig: updatedConfig); - - bloc.selectedBookableSpaces[i] = updatedSpace; - } - context .read() - .add(CheckConfigurValidityEvent()); + .add(ChangeCostEvent(cost: updatedCost)); + context.read().add( + CheckConfigurValidityEvent(), + ); }, - controller: widget.pointsController, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - suffix: const SizedBox(), ) else const SizedBox(), diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart index e9fc0289..de35a38a 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart @@ -10,68 +10,68 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/prese import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart'; class SaveSecondStepButton extends StatelessWidget { - final List selectedSpaces; final TextEditingController pointsController; final bool isEditingMode; + final List bookableSpaces; const SaveSecondStepButton({ super.key, - required this.selectedSpaces, required this.pointsController, required this.isEditingMode, + required this.bookableSpaces, }); @override Widget build(BuildContext context) { - return BlocConsumer( + return BlocListener( listener: (context, state) { if (state is SendBookableSpacesSuccess) { context.read().add(CallInitStateEvent()); } else if (state is SendBookableSpacesError) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(state.error), - ), + SnackBar(content: Text(state.error)), ); } }, - builder: (context, state) { - return ButtonsDividerBottomDialogWidget( - title: 'Save', - onNextPressed: state is UnValidSaveButtonState - ? null - : () { - if (selectedSpaces.any( - (element) => element.isValid, - )) { - isEditingMode - ? callEditLogic(context) - : context.read().add( + child: BlocBuilder( + builder: (context, state) { + return ButtonsDividerBottomDialogWidget( + title: 'Save', + onNextPressed: state is UnValidSaveButtonState + ? null + : () { + if (bookableSpaces.any((e) => e.isValid)) { + if (isEditingMode) { + callEditLogic(context); + } else { + context.read().add( SendBookableSpacesToApi( - selectedBookableSpaces: context - .read() - .selectedBookableSpaces), + selectedBookableSpaces: bookableSpaces, + ), ); - } - }, - onCancelPressed: () => context.pop(), - ); - }, + } + } + }, + onCancelPressed: () => context.pop(), + ); + }, + ), ); } void callEditLogic(BuildContext context) { - context.read().add( - UpdateBookableSpace( - onSuccess: () => - context.read().add(CallInitStateEvent()), - updatedParam: UpdateBookableSpaceParam.fromBookableModel( - context - .read() - .selectedBookableSpaces - .first, + print(bookableSpaces.first.spaceConfig!.cost); + if (bookableSpaces.isNotEmpty) { + context.read().add( + UpdateBookableSpace( + onSuccess: () => context + .read() + .add(CallInitStateEvent()), + updatedParam: UpdateBookableSpaceParam.fromBookableModel( + bookableSpaces.first, + ), ), - ), - ); + ); + } } } diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart index b72a714d..c215079e 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart @@ -12,7 +12,7 @@ class TimePickerWidget extends StatefulWidget { required this.onTimePicked, required this.title, }); - late SetupBookableSpacesBloc setupBookableSpacesBloc; + late final SetupBookableSpacesBloc setupBookableSpacesBloc; final void Function(TimeOfDay? timePicked) onTimePicked; @override State createState() => _TimePickerWidgetState(); @@ -47,9 +47,14 @@ class _TimePickerWidgetState extends State { ); }, ); + + if (tempTime == null) return; + widget.onTimePicked(tempTime); timePicked = tempTime; + widget.setupBookableSpacesBloc.add(CheckConfigurValidityEvent()); + setState(() {}); }, child: Container( diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart index 225514bb..2aa09b24 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart @@ -36,9 +36,7 @@ class UnbookableListWidget extends StatelessWidget { if (index < nonBookableSpaces.data.length) { return CheckBoxSpaceWidget( nonBookableSpace: nonBookableSpaces.data[index], - selectedSpaces: context - .read() - .selectedBookableSpaces, + ); } else { return const Padding( diff --git a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart index 3673ef1c..c01dc714 100644 --- a/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart +++ b/lib/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart @@ -25,6 +25,7 @@ class _WeekDaysCheckboxRowState extends State { 'Sat': false, 'Sun': false, }; + @override void initState() { super.initState(); @@ -56,9 +57,7 @@ class _WeekDaysCheckboxRowState extends State { const Text('Days'), ], ), - const SizedBox( - height: 20, - ), + const SizedBox(height: 20), Row( crossAxisAlignment: CrossAxisAlignment.start, children: _daysChecked.entries.map((entry) { @@ -79,34 +78,24 @@ class _WeekDaysCheckboxRowState extends State { .map((e) => e.key) .toList(); - final bloc = context.read(); - - for (int i = 0; - i < bloc.selectedBookableSpaces.length; - i++) { - final space = bloc.selectedBookableSpaces[i]; - final updatedConfig = space.spaceConfig - ?.copyWith(bookableDays: selectedDays); - final updatedSpace = - space.copyWith(spaceConfig: updatedConfig); - - bloc.selectedBookableSpaces[i] = updatedSpace; - } - - bloc.add(CheckConfigurValidityEvent()); + context.read().add( + AddBookableDaysEvent(bookableDays: selectedDays), + ); + context.read().add( + CheckConfigurValidityEvent(), + ); }, ), - const SizedBox( - width: 8, - ), + const SizedBox(width: 8), Expanded( - child: Text( - entry.key, - style: const TextStyle( - fontSize: 13, - fontWeight: FontWeight.w400, + child: Text( + entry.key, + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w400, + ), ), - )), + ), ], ), ); From 68153e41ed079a90ad2ac1b5771967fdca07dab2 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Wed, 23 Jul 2025 09:02:30 +0300 Subject: [PATCH 71/71] remove print statement --- lib/pages/device_managment/gateway/view/gateway_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/device_managment/gateway/view/gateway_view.dart b/lib/pages/device_managment/gateway/view/gateway_view.dart index a2476cb8..372f190a 100644 --- a/lib/pages/device_managment/gateway/view/gateway_view.dart +++ b/lib/pages/device_managment/gateway/view/gateway_view.dart @@ -100,7 +100,7 @@ class _DeviceItem extends StatelessWidget { @override Widget build(BuildContext context) { - print(device.icon); + return DeviceControlsContainer( child: Column( crossAxisAlignment: CrossAxisAlignment.start,