From e2d4e48875c5f2e2eb85a80b60cd8137eb78e98a Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 7 Jul 2025 15:36:35 +0300 Subject: [PATCH 001/118] 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 002/118] 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 003/118] 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 004/118] 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 005/118] 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 006/118] 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 007/118] 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 008/118] 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 009/118] 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 010/118] 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 011/118] 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 012/118] 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 013/118] 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 014/118] 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 015/118] 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 016/118] 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 017/118] 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 018/118] 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 019/118] 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 020/118] 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 021/118] 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 022/118] 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 e0980b324c778815825fd7a9790b173085bef733 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Jul 2025 10:54:07 +0300 Subject: [PATCH 023/118] Add DeleteSpaceParam and DeleteSpaceService for space deletion functionality --- .../delete_space/domain/params/delete_space_param.dart | 9 +++++++++ .../domain/services/delete_space_service.dart | 5 +++++ 2 files changed, 14 insertions(+) create mode 100644 lib/pages/space_management_v2/modules/delete_space/domain/params/delete_space_param.dart create mode 100644 lib/pages/space_management_v2/modules/delete_space/domain/services/delete_space_service.dart diff --git a/lib/pages/space_management_v2/modules/delete_space/domain/params/delete_space_param.dart b/lib/pages/space_management_v2/modules/delete_space/domain/params/delete_space_param.dart new file mode 100644 index 00000000..d6781876 --- /dev/null +++ b/lib/pages/space_management_v2/modules/delete_space/domain/params/delete_space_param.dart @@ -0,0 +1,9 @@ +class DeleteSpaceParam { + const DeleteSpaceParam({ + required this.spaceUuid, + required this.communityUuid, + }); + + final String spaceUuid; + final String communityUuid; +} diff --git a/lib/pages/space_management_v2/modules/delete_space/domain/services/delete_space_service.dart b/lib/pages/space_management_v2/modules/delete_space/domain/services/delete_space_service.dart new file mode 100644 index 00000000..a537645c --- /dev/null +++ b/lib/pages/space_management_v2/modules/delete_space/domain/services/delete_space_service.dart @@ -0,0 +1,5 @@ +import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/domain/params/delete_space_param.dart'; + +abstract interface class DeleteSpaceService { + Future delete(DeleteSpaceParam param); +} From 3cd01253109e25219155b261223006210dfae82a Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Jul 2025 10:54:13 +0300 Subject: [PATCH 024/118] Refactor space deletion: Introduce DeleteSpaceParam and DeleteSpaceService for enhanced space management functionality --- .../data/remote_delete_space_service.dart | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 lib/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart diff --git a/lib/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart b/lib/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart new file mode 100644 index 00000000..48d65934 --- /dev/null +++ b/lib/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart @@ -0,0 +1,63 @@ +import 'package:dio/dio.dart'; +import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/domain/params/delete_space_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/domain/services/delete_space_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; + +final class RemoteDeleteSpaceService implements DeleteSpaceService { + RemoteDeleteSpaceService({ + required this.httpService, + }); + + final HTTPService httpService; + + @override + Future delete(DeleteSpaceParam param) async { + try { + await httpService.delete( + path: await _makeUrl(param), + expectedResponseModel: (json) { + final response = json as Map; + final data = response['data'] as Map?; + final hasSuccessfullyDeletedSpace = data?['success'] as bool? ?? false; + + if (!hasSuccessfullyDeletedSpace) { + throw APIException('Failed to delete space'); + } + + return hasSuccessfullyDeletedSpace; + }, + ); + } on DioException catch (e) { + final message = e.response?.data as Map?; + throw APIException(_getErrorMessageFromBody(message)); + } catch (e) { + final formattedErrorMessage = ['Failed to delete space', '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } + + 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; + } + + Future _makeUrl(DeleteSpaceParam param) async { + final projectUuid = await ProjectManager.getProjectUUID(); + if (projectUuid == null) { + throw APIException('Project UUID is not set'); + } + + if (param.communityUuid.isEmpty) { + throw APIException('Community UUID is not set'); + } + + if (param.spaceUuid.isEmpty) { + throw APIException('Space UUID is not set'); + } + return '/projects/$projectUuid/communities/${param.communityUuid}/spaces/${param.spaceUuid}'; + } +} From 5663e2084eb752b17229fa1701d56c0f26518d7d Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Jul 2025 10:57:34 +0300 Subject: [PATCH 025/118] Created `DeleteSpaceBloc`. --- .../presentation/bloc/delete_space_bloc.dart | 31 +++++++++++++++++++ .../presentation/bloc/delete_space_event.dart | 17 ++++++++++ .../presentation/bloc/delete_space_state.dart | 30 ++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_bloc.dart create mode 100644 lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_event.dart create mode 100644 lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_state.dart diff --git a/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_bloc.dart b/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_bloc.dart new file mode 100644 index 00000000..f515fc09 --- /dev/null +++ b/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_bloc.dart @@ -0,0 +1,31 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/domain/params/delete_space_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/domain/services/delete_space_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; + +part 'delete_space_event.dart'; +part 'delete_space_state.dart'; + +class DeleteSpaceBloc extends Bloc { + DeleteSpaceBloc(this._deleteSpaceService) : super(DeleteSpaceInitial()) { + on(_onDeleteSpace); + } + + final DeleteSpaceService _deleteSpaceService; + + Future _onDeleteSpace( + DeleteSpace event, + Emitter emit + ) async { + emit(DeleteSpaceLoading()); + try { + await _deleteSpaceService.delete(event.param); + emit(const DeleteSpaceSuccess('Space deleted successfully')); + } on APIException catch (e) { + emit(DeleteSpaceFailure(e.message)); + } catch (e) { + emit(const DeleteSpaceFailure('Failed to delete space')); + } + } +} diff --git a/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_event.dart b/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_event.dart new file mode 100644 index 00000000..c80346e8 --- /dev/null +++ b/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_event.dart @@ -0,0 +1,17 @@ +part of 'delete_space_bloc.dart'; + +sealed class DeleteSpaceEvent extends Equatable { + const DeleteSpaceEvent(); + + @override + List get props => []; +} + +final class DeleteSpace extends DeleteSpaceEvent { + const DeleteSpace(this.param); + + final DeleteSpaceParam param; + + @override + List get props => [param]; +} diff --git a/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_state.dart b/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_state.dart new file mode 100644 index 00000000..96b6d5b7 --- /dev/null +++ b/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_state.dart @@ -0,0 +1,30 @@ +part of 'delete_space_bloc.dart'; + +sealed class DeleteSpaceState extends Equatable { + const DeleteSpaceState(); + + @override + List get props => []; +} + +final class DeleteSpaceInitial extends DeleteSpaceState {} + +final class DeleteSpaceLoading extends DeleteSpaceState {} + +final class DeleteSpaceSuccess extends DeleteSpaceState { + const DeleteSpaceSuccess(this.successMessage); + + final String successMessage; + + @override + List get props => [successMessage]; +} + +final class DeleteSpaceFailure extends DeleteSpaceState { + const DeleteSpaceFailure(this.errorMessage); + + final String errorMessage; + + @override + List get props => [errorMessage]; +} From cf1b34ee0ac4e5d1d552f9c135bbd282d16a4d34 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Jul 2025 12:17:07 +0300 Subject: [PATCH 026/118] Add x_delete icon asset. --- assets/icons/x_delete.svg | 5 +++++ lib/utils/constants/assets.dart | 1 + 2 files changed, 6 insertions(+) create mode 100644 assets/icons/x_delete.svg diff --git a/assets/icons/x_delete.svg b/assets/icons/x_delete.svg new file mode 100644 index 00000000..637f2e72 --- /dev/null +++ b/assets/icons/x_delete.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index f92975f3..b7ad15b5 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -517,4 +517,5 @@ class Assets { static const String emptyRangeOfAqi = 'assets/icons/empty_range_of_aqi.svg'; static const String homeIcon = 'assets/icons/home_icon.svg'; static const String groupIcon = 'assets/icons/group_icon.svg'; + static const String xDelete = 'assets/icons/x_delete.svg'; } From 035c03c6b2b0239844178b55e6b7420fa6ab5ae4 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Jul 2025 14:17:01 +0300 Subject: [PATCH 027/118] Fix error handling in DeleteSpaceBloc: update failure message to include exception details --- .../delete_space/presentation/bloc/delete_space_bloc.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_bloc.dart b/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_bloc.dart index f515fc09..6334bb33 100644 --- a/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_bloc.dart +++ b/lib/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_bloc.dart @@ -16,7 +16,7 @@ class DeleteSpaceBloc extends Bloc { Future _onDeleteSpace( DeleteSpace event, - Emitter emit + Emitter emit, ) async { emit(DeleteSpaceLoading()); try { @@ -25,7 +25,7 @@ class DeleteSpaceBloc extends Bloc { } on APIException catch (e) { emit(DeleteSpaceFailure(e.message)); } catch (e) { - emit(const DeleteSpaceFailure('Failed to delete space')); + emit(DeleteSpaceFailure(e.toString())); } } } From 086f3cedf824f77af40eef6f828984cdcb6fd622 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Jul 2025 14:30:17 +0300 Subject: [PATCH 028/118] Refactor RemoteDeleteSpaceService: Simplify success response handling and improve error message formatting --- .../delete_space/data/remote_delete_space_service.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart b/lib/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart index 48d65934..74724644 100644 --- a/lib/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart +++ b/lib/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart @@ -19,8 +19,7 @@ final class RemoteDeleteSpaceService implements DeleteSpaceService { path: await _makeUrl(param), expectedResponseModel: (json) { final response = json as Map; - final data = response['data'] as Map?; - final hasSuccessfullyDeletedSpace = data?['success'] as bool? ?? false; + final hasSuccessfullyDeletedSpace = response['success'] as bool? ?? false; if (!hasSuccessfullyDeletedSpace) { throw APIException('Failed to delete space'); @@ -33,8 +32,7 @@ final class RemoteDeleteSpaceService implements DeleteSpaceService { final message = e.response?.data as Map?; throw APIException(_getErrorMessageFromBody(message)); } catch (e) { - final formattedErrorMessage = ['Failed to delete space', '$e'].join(': '); - throw APIException(formattedErrorMessage); + throw APIException(e.toString()); } } From f4b5c6fb52917d4fabf1ee7d3483c2c72ea09e42 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Jul 2025 14:44:08 +0300 Subject: [PATCH 029/118] Implement delete space functionality in CommunityStructureHeader: Integrate DeleteSpaceDialog for space deletion confirmation and update routing for space management page. --- .../widgets/community_structure_header.dart | 15 ++- .../widgets/delete_space_dialog.dart | 118 ++++++++++++++++++ .../widgets/delete_space_dialog_form.dart | 106 ++++++++++++++++ 3 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart create mode 100644 lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog_form.dart diff --git a/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart b/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart index 4f71075b..b8380bb7 100644 --- a/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart +++ b/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart @@ -4,6 +4,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.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/delete_space/presentation/widgets/delete_space_dialog.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -91,7 +92,19 @@ class CommunityStructureHeader extends StatelessWidget { ), const SizedBox(width: 8), CommunityStructureHeaderActionButtons( - onDelete: (space) {}, + onDelete: (space) => showDialog( + context: context, + barrierDismissible: false, + builder: (_) => DeleteSpaceDialog( + space: space, + community: selectedCommunity, + onSuccess: () { + context.read().add( + SelectCommunityEvent(community: selectedCommunity), + ); + }, + ), + ), onDuplicate: (space) {}, onEdit: (space) { SpaceDetailsDialogHelper.showEdit( diff --git a/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart b/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart new file mode 100644 index 00000000..c8356e32 --- /dev/null +++ b/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog_form.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class DeleteSpaceDialog extends StatelessWidget { + const DeleteSpaceDialog({ + required this.space, + required this.community, + required this.onSuccess, + super.key, + }); + + final SpaceModel space; + final CommunityModel community; + final void Function() onSuccess; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => DeleteSpaceBloc( + RemoteDeleteSpaceService(httpService: HTTPService()), + ), + child: Builder( + builder: (context) => Dialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Container( + padding: const EdgeInsetsDirectional.all(32), + constraints: BoxConstraints( + maxWidth: context.screenWidth * 0.2, + ), + child: BlocConsumer( + listener: (context, state) { + if (state case DeleteSpaceSuccess()) onSuccess(); + }, + builder: (context, state) => switch (state) { + DeleteSpaceInitial() => DeleteSpaceDialogForm( + space: space, + communityUuid: community.uuid, + ), + DeleteSpaceLoading() => const DeleteSpaceLoadingWidget(), + DeleteSpaceSuccess(:final successMessage) => DeleteSpaceStatusWidget( + message: successMessage, + icon: const Icon( + Icons.check_circle, + size: 92, + color: ColorsManager.goodGreen, + ), + ), + DeleteSpaceFailure() => DeleteSpaceStatusWidget( + message: state.errorMessage, + icon: const Icon( + Icons.error, + size: 92, + color: ColorsManager.red, + ), + ), + }, + ), + ), + ), + ), + ); + } +} + +class DeleteSpaceLoadingWidget extends StatelessWidget { + const DeleteSpaceLoadingWidget({super.key}); + + @override + Widget build(BuildContext context) { + return const SizedBox.square( + dimension: 32, + child: Center(child: CircularProgressIndicator()), + ); + } +} + +class DeleteSpaceStatusWidget extends StatelessWidget { + const DeleteSpaceStatusWidget({ + required this.message, + required this.icon, + super.key, + }); + + final String message; + final Widget icon; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 16, + children: [ + icon, + SelectableText( + message, + style: context.textTheme.bodyMedium?.copyWith( + color: ColorsManager.blackColor, + fontSize: 22, + ), + textAlign: TextAlign.center, + ), + FilledButton( + onPressed: Navigator.of(context).pop, + child: const Text('Close'), + ), + ], + ); + } +} diff --git a/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog_form.dart b/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog_form.dart new file mode 100644 index 00000000..055b67b8 --- /dev/null +++ b/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog_form.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/domain/params/delete_space_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_bloc.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'; + +class DeleteSpaceDialogForm extends StatelessWidget { + const DeleteSpaceDialogForm({ + required this.space, + required this.communityUuid, + super.key, + }); + + final SpaceModel space; + final String communityUuid; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset(Assets.xDelete, width: 36, height: 36), + const SizedBox(height: 16), + SelectableText( + 'Delete Space', + textAlign: TextAlign.center, + style: context.textTheme.titleLarge?.copyWith( + color: ColorsManager.blackColor, + fontWeight: FontWeight.w400, + fontSize: 24, + ), + ), + const SizedBox(height: 8), + SelectableText( + 'Are you sure you want to delete this space? This action is irreversible', + textAlign: TextAlign.center, + style: context.textTheme.bodyLarge?.copyWith( + color: ColorsManager.lightGreyColor, + fontWeight: FontWeight.w400, + fontSize: 14, + ), + ), + const SizedBox(height: 24), + Row( + children: [ + Expanded( + child: FilledButton( + style: _buildButtonStyle( + context, + color: ColorsManager.grey25, + textColor: ColorsManager.blackColor, + ), + onPressed: Navigator.of(context).pop, + child: const Text('Cancel'), + ), + ), + const SizedBox(width: 16), + Expanded( + child: FilledButton( + style: _buildButtonStyle( + context, + color: ColorsManager.semiTransparentRed, + textColor: ColorsManager.whiteColors, + ), + onPressed: () { + context.read().add( + DeleteSpace( + DeleteSpaceParam( + spaceUuid: space.uuid, + communityUuid: communityUuid, + ), + ), + ); + }, + child: const Text('Delete'), + ), + ), + ], + ), + ], + ); + } + + ButtonStyle _buildButtonStyle( + BuildContext context, { + required Color color, + required Color textColor, + }) { + return FilledButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 12), + backgroundColor: color, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + foregroundColor: textColor, + textStyle: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w400, + fontSize: 14, + ), + ); + } +} From beb33e37fa10b72cc546a3343db9f8cd3190dafe Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Jul 2025 14:59:34 +0300 Subject: [PATCH 030/118] Add SpacesRecursiveHelper for recursive space updates and deletions; refactor CommunityStructureHeader to use CommunityStructureHeaderActionButtonsComposer for improved action handling. --- .../helpers/spaces_recursive_helper.dart | 41 +++++++++++ .../widgets/community_structure_header.dart | 73 ++----------------- ...ucture_header_action_buttons_composer.dart | 69 ++++++++++++++++++ 3 files changed, 118 insertions(+), 65 deletions(-) create mode 100644 lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart create mode 100644 lib/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons_composer.dart diff --git a/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart b/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart new file mode 100644 index 00000000..e751e8d8 --- /dev/null +++ b/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart @@ -0,0 +1,41 @@ +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; + +abstract final class SpacesRecursiveHelper { + static List recusrivelyUpdate( + List spaces, + SpaceDetailsModel updatedSpace, + ) { + return spaces.map((space) { + if (space.uuid == updatedSpace.uuid) { + return space.copyWith( + spaceName: updatedSpace.spaceName, + icon: updatedSpace.icon, + ); + } + if (space.children.isNotEmpty) { + return space.copyWith( + children: recusrivelyUpdate(space.children, updatedSpace), + ); + } + return space; + }).toList(); + } + + static List recusrivelyDelete( + List spaces, + String spaceUuid, + ) { + final s = spaces.map((space) { + if (space.uuid == spaceUuid) return null; + if (space.children.isNotEmpty) { + return space.copyWith( + children: recusrivelyDelete(space.children, spaceUuid), + ); + } + return space; + }).toList(); + + return s.whereType().toList(); + } +} diff --git a/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart b/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart index de148ecd..432b3ce4 100644 --- a/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart +++ b/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart @@ -2,42 +2,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart'; -import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.dart'; -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/main_module/widgets/community_structure_header_action_buttons_composer.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/delete_space/presentation/widgets/delete_space_dialog.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.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'; class CommunityStructureHeader extends StatelessWidget { const CommunityStructureHeader({super.key}); - List _updateRecursive( - List spaces, - SpaceDetailsModel updatedSpace, - ) { - return spaces.map((space) { - if (space.uuid == updatedSpace.uuid) { - return space.copyWith( - spaceName: updatedSpace.spaceName, - icon: updatedSpace.icon, - ); - } - if (space.children.isNotEmpty) { - return space.copyWith( - children: _updateRecursive(space.children, updatedSpace), - ); - } - return space; - }).toList(); - } - @override Widget build(BuildContext context) { - final theme = Theme.of(context); final screenWidth = MediaQuery.of(context).size.width; return Container( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), @@ -58,7 +33,7 @@ class CommunityStructureHeader extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: _buildCommunityInfo(context, theme, screenWidth), + child: _buildCommunityInfo(context, screenWidth), ), const SizedBox(width: 16), ], @@ -68,8 +43,7 @@ class CommunityStructureHeader extends StatelessWidget { ); } - Widget _buildCommunityInfo( - BuildContext context, ThemeData theme, double screenWidth) { + Widget _buildCommunityInfo(BuildContext context, double screenWidth) { final selectedCommunity = context.watch().state.selectedCommunity; final selectedSpace = @@ -79,7 +53,7 @@ class CommunityStructureHeader extends StatelessWidget { children: [ Text( 'Community Structure', - style: theme.textTheme.headlineLarge?.copyWith( + style: context.textTheme.headlineLarge?.copyWith( color: ColorsManager.blackColor, ), ), @@ -92,7 +66,7 @@ class CommunityStructureHeader extends StatelessWidget { Flexible( child: SelectableText( selectedCommunity.name, - style: theme.textTheme.bodyLarge?.copyWith( + style: context.textTheme.bodyLarge?.copyWith( color: ColorsManager.blackColor, ), maxLines: 1, @@ -116,39 +90,8 @@ class CommunityStructureHeader extends StatelessWidget { ), ), const SizedBox(width: 8), - CommunityStructureHeaderActionButtons( - onDelete: (space) => showDialog( - context: context, - barrierDismissible: false, - builder: (_) => DeleteSpaceDialog( - space: space, - community: selectedCommunity, - onSuccess: () { - context.read().add( - SelectCommunityEvent(community: selectedCommunity), - ); - }, - ), - ), - onDuplicate: (space) {}, - onEdit: (space) => SpaceDetailsDialogHelper.showEdit( - context, - spaceModel: selectedSpace!, - communityUuid: selectedCommunity.uuid, - onSuccess: (updatedSpaceDetails) { - final communitiesBloc = context.read(); - final updatedSpaces = _updateRecursive( - selectedCommunity.spaces, - updatedSpaceDetails, - ); - - final community = selectedCommunity.copyWith( - spaces: updatedSpaces, - ); - - communitiesBloc.add(CommunitiesUpdateCommunity(community)); - }, - ), + CommunityStructureHeaderActionButtonsComposer( + selectedCommunity: selectedCommunity, selectedSpace: selectedSpace, ), ], diff --git a/lib/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons_composer.dart b/lib/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons_composer.dart new file mode 100644 index 00000000..d7403588 --- /dev/null +++ b/lib/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons_composer.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; +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/delete_space/presentation/widgets/delete_space_dialog.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart'; + +class CommunityStructureHeaderActionButtonsComposer extends StatelessWidget { + const CommunityStructureHeaderActionButtonsComposer({ + required this.selectedCommunity, + required this.selectedSpace, + super.key, + }); + final CommunityModel selectedCommunity; + final SpaceModel? selectedSpace; + + @override + Widget build(BuildContext context) { + return CommunityStructureHeaderActionButtons( + onDelete: (space) => showDialog( + context: context, + barrierDismissible: false, + builder: (_) => DeleteSpaceDialog( + space: space, + community: selectedCommunity, + onSuccess: () { + final updatedSpaces = SpacesRecursiveHelper.recusrivelyDelete( + selectedCommunity.spaces, + space.uuid, + ); + final community = selectedCommunity.copyWith( + spaces: updatedSpaces, + ); + context.read().add( + CommunitiesUpdateCommunity(community), + ); + context.read().add( + SelectCommunityEvent(community: selectedCommunity), + ); + }, + ), + ), + onDuplicate: (space) {}, + onEdit: (space) => SpaceDetailsDialogHelper.showEdit( + context, + spaceModel: selectedSpace!, + communityUuid: selectedCommunity.uuid, + onSuccess: (updatedSpaceDetails) { + final communitiesBloc = context.read(); + final updatedSpaces = SpacesRecursiveHelper.recusrivelyUpdate( + selectedCommunity.spaces, + updatedSpaceDetails, + ); + + final community = selectedCommunity.copyWith( + spaces: updatedSpaces, + ); + + communitiesBloc.add(CommunitiesUpdateCommunity(community)); + }, + ), + selectedSpace: selectedSpace, + ); + } +} From ab6a6851f2b732e38b71e6441f50ea308b15b62b Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Jul 2025 15:23:01 +0300 Subject: [PATCH 031/118] Update control points in SpacesConnectionsArrowPainter for smoother arrow rendering --- .../painters/spaces_connections_arrow_painter.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart b/lib/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart index e9fa0a15..5200cfff 100644 --- a/lib/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart +++ b/lib/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart @@ -37,8 +37,8 @@ class SpacesConnectionsArrowPainter extends CustomPainter { final path = Path()..moveTo(startPoint.dx, startPoint.dy); - final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 20); - final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 60); + final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 100); + final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 100); path.cubicTo(controlPoint1.dx, controlPoint1.dy, controlPoint2.dx, controlPoint2.dy, endPoint.dx, endPoint.dy); From 52186417058a846e265242e823c5d1c1d63d8942 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Jul 2025 16:05:44 +0300 Subject: [PATCH 032/118] Refactor didUpdateWidget in CommunityStructureCanvas to ensure proper widget lifecycle management --- .../main_module/widgets/community_structure_canvas.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3cf761ad..67ccf380 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 @@ -51,7 +51,6 @@ class _CommunityStructureCanvasState extends State @override void didUpdateWidget(covariant CommunityStructureCanvas oldWidget) { - super.didUpdateWidget(oldWidget); if (widget.selectedSpace?.uuid != oldWidget.selectedSpace?.uuid) { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { @@ -59,6 +58,7 @@ class _CommunityStructureCanvasState extends State } }); } + super.didUpdateWidget(oldWidget); } @override From de5d8df01c184b36866d18e27e0df651687eb330 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Jul 2025 16:19:57 +0300 Subject: [PATCH 033/118] Update SpaceCell widget shadow properties for improved visual appearance --- .../space_management_v2/main_module/widgets/space_cell.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/widgets/space_cell.dart b/lib/pages/space_management_v2/main_module/widgets/space_cell.dart index 80b18526..1f7bd808 100644 --- a/lib/pages/space_management_v2/main_module/widgets/space_cell.dart +++ b/lib/pages/space_management_v2/main_module/widgets/space_cell.dart @@ -74,9 +74,8 @@ class SpaceCell extends StatelessWidget { borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( - color: ColorsManager.lightGrayColor.withValues(alpha: 0.5), - spreadRadius: 2, - blurRadius: 5, + color: ColorsManager.lightGrayColor.withValues(alpha: 0.25), + blurRadius: 7, offset: const Offset(0, 3), ), ], From 466f5b89c7ac621f216d1d9ded017b9a97550af9 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 14 Jul 2025 16:56:51 +0300 Subject: [PATCH 034/118] Enhanced SpacesConnectionsArrowPainter and CommunityStructureCanvas to support dynamic card widths; enhance SpaceCell widget layout and shadow properties for improved UI consistency. --- .../spaces_connections_arrow_painter.dart | 30 +++++--- .../widgets/community_structure_canvas.dart | 68 ++++++++++++++----- .../main_module/widgets/space_cell.dart | 23 +++---- 3 files changed, 83 insertions(+), 38 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart b/lib/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart index 5200cfff..ed797c74 100644 --- a/lib/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart +++ b/lib/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart @@ -5,13 +5,14 @@ import 'package:syncrow_web/utils/color_manager.dart'; class SpacesConnectionsArrowPainter extends CustomPainter { final List connections; final Map positions; - final double cardWidth = 150.0; + final Map cardWidths; final double cardHeight = 90.0; final Set highlightedUuids; SpacesConnectionsArrowPainter({ required this.connections, required this.positions, + required this.cardWidths, required this.highlightedUuids, }); @@ -29,19 +30,30 @@ class SpacesConnectionsArrowPainter extends CustomPainter { final from = positions[connection.from]; final to = positions[connection.to]; + final fromWidth = cardWidths[connection.from] ?? 150.0; + final toWidth = cardWidths[connection.to] ?? 150.0; if (from != null && to != null) { final startPoint = - Offset(from.dx + cardWidth / 2, from.dy + cardHeight - 10); - final endPoint = Offset(to.dx + cardWidth / 2, to.dy); + Offset(from.dx + fromWidth / 2, from.dy + cardHeight - 10); + final endPoint = Offset(to.dx + toWidth / 2, to.dy); final path = Path()..moveTo(startPoint.dx, startPoint.dy); - final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 100); - final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 100); - - path.cubicTo(controlPoint1.dx, controlPoint1.dy, controlPoint2.dx, - controlPoint2.dy, endPoint.dx, endPoint.dy); + if ((startPoint.dx - endPoint.dx).abs() < 1.0) { + path.lineTo(endPoint.dx, endPoint.dy); + } else { + final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 100); + final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 100); + path.cubicTo( + controlPoint1.dx, + controlPoint1.dy, + controlPoint2.dx, + controlPoint2.dy, + endPoint.dx, + endPoint.dy, + ); + } canvas.drawPath(path, paint); @@ -51,7 +63,7 @@ class SpacesConnectionsArrowPainter extends CustomPainter { : ColorsManager.blackColor.withValues(alpha: 0.5) ..style = PaintingStyle.fill ..blendMode = BlendMode.srcIn; - canvas.drawCircle(endPoint, 4, circlePaint); + canvas.drawCircle(endPoint, 6, circlePaint); } } } 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 67ccf380..65c0b95f 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 @@ -30,10 +30,11 @@ class CommunityStructureCanvas extends StatefulWidget { class _CommunityStructureCanvasState extends State with SingleTickerProviderStateMixin { final Map _positions = {}; - final double _cardWidth = 150.0; + final Map _cardWidths = {}; final double _cardHeight = 90.0; final double _horizontalSpacing = 150.0; final double _verticalSpacing = 120.0; + static const double _minCardWidth = 150.0; late final TransformationController _transformationController; late final AnimationController _animationController; @@ -51,6 +52,7 @@ class _CommunityStructureCanvasState extends State @override void didUpdateWidget(covariant CommunityStructureCanvas oldWidget) { + super.didUpdateWidget(oldWidget); if (widget.selectedSpace?.uuid != oldWidget.selectedSpace?.uuid) { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { @@ -58,7 +60,6 @@ class _CommunityStructureCanvasState extends State } }); } - super.didUpdateWidget(oldWidget); } @override @@ -68,6 +69,34 @@ class _CommunityStructureCanvasState extends State super.dispose(); } + double _calculateCardWidth(String text) { + final style = context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.bold, + ); + final textPainter = TextPainter( + text: TextSpan(text: text, style: style), + maxLines: 1, + textDirection: TextDirection.ltr, + )..layout(); + + const iconWidth = 40.0; + const horizontalPadding = 10.0; + const contentPadding = 10.0; + final calculatedWidth = + iconWidth + horizontalPadding + textPainter.width + contentPadding; + + return calculatedWidth.clamp(_minCardWidth, double.infinity); + } + + void _calculateAllCardWidths(List spaces) { + for (final space in spaces) { + _cardWidths[space.uuid] = _calculateCardWidth(space.spaceName); + if (space.children.isNotEmpty) { + _calculateAllCardWidths(space.children); + } + } + } + Set _getAllDescendantUuids(SpaceModel space) { final uuids = {}; for (final child in space.children) { @@ -102,11 +131,12 @@ class _CommunityStructureCanvasState extends State final position = _positions[space.uuid]; if (position == null) return; + final cardWidth = _cardWidths[space.uuid] ?? _minCardWidth; const scale = 1; final viewSize = context.size; if (viewSize == null) return; - final x = -position.dx * scale + (viewSize.width / 2) - (_cardWidth * scale / 2); + final x = -position.dx * scale + (viewSize.width / 2) - (cardWidth * scale / 2); final y = -position.dy * scale + (viewSize.height / 2) - (_cardHeight * scale / 2); @@ -155,13 +185,16 @@ class _CommunityStructureCanvasState extends State Map levelXOffset, ) { for (final space in spaces) { + final cardWidth = _cardWidths[space.uuid] ?? _minCardWidth; double childSubtreeWidth = 0; if (space.children.isNotEmpty) { _calculateLayout(space.children, depth + 1, levelXOffset); final firstChildPos = _positions[space.children.first.uuid]; final lastChildPos = _positions[space.children.last.uuid]; if (firstChildPos != null && lastChildPos != null) { - childSubtreeWidth = (lastChildPos.dx + _cardWidth) - firstChildPos.dx; + final lastChildWidth = + _cardWidths[space.children.last.uuid] ?? _minCardWidth; + childSubtreeWidth = (lastChildPos.dx + lastChildWidth) - firstChildPos.dx; } } @@ -170,7 +203,7 @@ class _CommunityStructureCanvasState extends State if (space.children.isNotEmpty) { final firstChildPos = _positions[space.children.first.uuid]!; - x = firstChildPos.dx + (childSubtreeWidth - _cardWidth) / 2; + x = firstChildPos.dx + (childSubtreeWidth - cardWidth) / 2; } else { x = currentX; } @@ -187,7 +220,7 @@ class _CommunityStructureCanvasState extends State final y = depth * (_verticalSpacing + _cardHeight); _positions[space.uuid] = Offset(x, y); - levelXOffset[depth] = x + _cardWidth + _horizontalSpacing; + levelXOffset[depth] = x + cardWidth + _horizontalSpacing; } } @@ -202,8 +235,11 @@ class _CommunityStructureCanvasState extends State List _buildTreeWidgets() { _positions.clear(); + _cardWidths.clear(); final community = widget.community; + _calculateAllCardWidths(community.spaces); + final levelXOffset = {}; _calculateLayout(community.spaces, 0, levelXOffset); @@ -240,6 +276,7 @@ class _CommunityStructureCanvasState extends State painter: SpacesConnectionsArrowPainter( connections: connections, positions: _positions, + cardWidths: _cardWidths, highlightedUuids: highlightedUuids, ), child: Stack(alignment: AlignmentDirectional.center, children: widgets), @@ -271,6 +308,7 @@ class _CommunityStructureCanvasState extends State continue; } + final cardWidth = _cardWidths[space.uuid] ?? _minCardWidth; final isHighlighted = highlightedUuids.contains(space.uuid); final hasNoSelectedSpace = widget.selectedSpace == null; @@ -278,14 +316,10 @@ class _CommunityStructureCanvasState extends State buildSpaceContainer: () { return Opacity( opacity: hasNoSelectedSpace || isHighlighted ? 1.0 : 0.5, - child: Tooltip( - message: space.spaceName, - preferBelow: false, - child: SpaceCell( - onTap: () => _onSpaceTapped(space), - icon: space.icon, - name: space.spaceName, - ), + child: SpaceCell( + onTap: () => _onSpaceTapped(space), + icon: space.icon, + name: space.spaceName, ), ); }, @@ -305,7 +339,7 @@ class _CommunityStructureCanvasState extends State Positioned( left: position.dx, top: position.dy, - width: _cardWidth, + width: cardWidth, height: _cardHeight, child: Draggable( data: reorderData, @@ -314,7 +348,7 @@ class _CommunityStructureCanvasState extends State child: Opacity( opacity: 0.2, child: SizedBox( - width: _cardWidth, + width: cardWidth, height: _cardHeight, child: spaceCard, ), @@ -330,7 +364,7 @@ class _CommunityStructureCanvasState extends State ); final targetPos = Offset( - position.dx + _cardWidth + (_horizontalSpacing / 4) - 20, + position.dx + cardWidth + (_horizontalSpacing / 4) - 20, position.dy, ); widgets.add(_buildDropTarget(parent, community, i + 1, targetPos)); diff --git a/lib/pages/space_management_v2/main_module/widgets/space_cell.dart b/lib/pages/space_management_v2/main_module/widgets/space_cell.dart index 1f7bd808..3eb6d5df 100644 --- a/lib/pages/space_management_v2/main_module/widgets/space_cell.dart +++ b/lib/pages/space_management_v2/main_module/widgets/space_cell.dart @@ -20,21 +20,19 @@ class SpaceCell extends StatelessWidget { return InkWell( onTap: onTap, child: Container( - width: 150, + padding: const EdgeInsetsDirectional.only(end: 10), height: 70, decoration: _containerDecoration(), child: Row( + spacing: 10, + mainAxisSize: MainAxisSize.min, children: [ _buildIconContainer(), - const SizedBox(width: 10), - Expanded( - child: Text( - name, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - color: ColorsManager.blackColor, - ), - overflow: TextOverflow.ellipsis, + Text( + name, + style: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.bold, + color: ColorsManager.blackColor, ), ), ], @@ -74,8 +72,9 @@ class SpaceCell extends StatelessWidget { borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( - color: ColorsManager.lightGrayColor.withValues(alpha: 0.25), - blurRadius: 7, + color: ColorsManager.lightGrayColor.withValues(alpha: 0.5), + spreadRadius: 2, + blurRadius: 5, offset: const Offset(0, 3), ), ], From 428c81effffe37b70a6c3bc171db9b0911a0ea5c Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 15 Jul 2025 10:05:07 +0300 Subject: [PATCH 035/118] Adjust table scroll behavior and modify height for improved layout --- lib/pages/common/custom_table.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pages/common/custom_table.dart b/lib/pages/common/custom_table.dart index 93f8998e..6c02e889 100644 --- a/lib/pages/common/custom_table.dart +++ b/lib/pages/common/custom_table.dart @@ -132,6 +132,8 @@ class _DynamicTableState extends State { child: SingleChildScrollView( controller: _horizontalScrollController, scrollDirection: Axis.horizontal, + physics: + widget.isEmpty ? const NeverScrollableScrollPhysics() : null, child: SizedBox( width: _totalTableWidth, child: Column( @@ -164,7 +166,6 @@ class _DynamicTableState extends State { ], ), ), - Expanded( child: widget.isEmpty ? _buildEmptyState() @@ -265,7 +266,7 @@ class _DynamicTableState extends State { ), ], ), - SizedBox(height: widget.size.height * 0.5), + SizedBox(height: widget.size.height * 0.2), ], ), ); From b2231949506223f69333ff702c57422d067bc6a3 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Jul 2025 10:07:12 +0300 Subject: [PATCH 036/118] Add loading and status widgets for delete space dialog; refactor dialog to utilize new components for improved user feedback during space deletion process. --- .../widgets/delete_space_dialog.dart | 53 ++----------------- .../widgets/delete_space_loading_widget.dart | 13 +++++ .../widgets/delete_space_status_widget.dart | 38 +++++++++++++ 3 files changed, 55 insertions(+), 49 deletions(-) create mode 100644 lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_loading_widget.dart create mode 100644 lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_status_widget.dart diff --git a/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart b/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart index c8356e32..b8734eee 100644 --- a/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart +++ b/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart @@ -5,6 +5,8 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/presentation/bloc/delete_space_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog_form.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_loading_widget.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_status_widget.dart'; import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; @@ -45,8 +47,8 @@ class DeleteSpaceDialog extends StatelessWidget { communityUuid: community.uuid, ), DeleteSpaceLoading() => const DeleteSpaceLoadingWidget(), - DeleteSpaceSuccess(:final successMessage) => DeleteSpaceStatusWidget( - message: successMessage, + DeleteSpaceSuccess() => DeleteSpaceStatusWidget( + message: state.successMessage, icon: const Icon( Icons.check_circle, size: 92, @@ -69,50 +71,3 @@ class DeleteSpaceDialog extends StatelessWidget { ); } } - -class DeleteSpaceLoadingWidget extends StatelessWidget { - const DeleteSpaceLoadingWidget({super.key}); - - @override - Widget build(BuildContext context) { - return const SizedBox.square( - dimension: 32, - child: Center(child: CircularProgressIndicator()), - ); - } -} - -class DeleteSpaceStatusWidget extends StatelessWidget { - const DeleteSpaceStatusWidget({ - required this.message, - required this.icon, - super.key, - }); - - final String message; - final Widget icon; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 16, - children: [ - icon, - SelectableText( - message, - style: context.textTheme.bodyMedium?.copyWith( - color: ColorsManager.blackColor, - fontSize: 22, - ), - textAlign: TextAlign.center, - ), - FilledButton( - onPressed: Navigator.of(context).pop, - child: const Text('Close'), - ), - ], - ); - } -} diff --git a/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_loading_widget.dart b/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_loading_widget.dart new file mode 100644 index 00000000..b658b3b3 --- /dev/null +++ b/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_loading_widget.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +class DeleteSpaceLoadingWidget extends StatelessWidget { + const DeleteSpaceLoadingWidget({super.key}); + + @override + Widget build(BuildContext context) { + return const SizedBox.square( + dimension: 32, + child: Center(child: CircularProgressIndicator()), + ); + } +} diff --git a/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_status_widget.dart b/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_status_widget.dart new file mode 100644 index 00000000..d597a451 --- /dev/null +++ b/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_status_widget.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class DeleteSpaceStatusWidget extends StatelessWidget { + const DeleteSpaceStatusWidget({ + required this.message, + required this.icon, + super.key, + }); + + final String message; + final Widget icon; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 16, + children: [ + icon, + SelectableText( + message, + style: context.textTheme.bodyMedium?.copyWith( + color: ColorsManager.blackColor, + fontSize: 22, + ), + textAlign: TextAlign.center, + ), + FilledButton( + onPressed: Navigator.of(context).pop, + child: const Text('Close'), + ), + ], + ); + } +} From acefe7b35510d9db30fc5c6c9f1aa5899beef61e Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Jul 2025 11:09:04 +0300 Subject: [PATCH 037/118] Refactor RemoteDeleteSpaceService to use a private HTTPService instance and update URL construction with ApiEndpoints for improved maintainability. Update DeleteSpaceDialog to reflect changes in service initialization. --- .../data/remote_delete_space_service.dart | 15 +++++++++------ .../presentation/widgets/delete_space_dialog.dart | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart b/lib/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart index 74724644..5320f625 100644 --- a/lib/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart +++ b/lib/pages/space_management_v2/modules/delete_space/data/remote_delete_space_service.dart @@ -4,18 +4,17 @@ import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/domai import 'package:syncrow_web/pages/space_management_v2/modules/delete_space/domain/services/delete_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'; final class RemoteDeleteSpaceService implements DeleteSpaceService { - RemoteDeleteSpaceService({ - required this.httpService, - }); + const RemoteDeleteSpaceService(this._httpService); - final HTTPService httpService; + final HTTPService _httpService; @override Future delete(DeleteSpaceParam param) async { try { - await httpService.delete( + await _httpService.delete( path: await _makeUrl(param), expectedResponseModel: (json) { final response = json as Map; @@ -56,6 +55,10 @@ final class RemoteDeleteSpaceService implements DeleteSpaceService { if (param.spaceUuid.isEmpty) { throw APIException('Space UUID is not set'); } - return '/projects/$projectUuid/communities/${param.communityUuid}/spaces/${param.spaceUuid}'; + + return ApiEndpoints.deleteSpace + .replaceAll('{projectId}', projectUuid) + .replaceAll('{communityId}', param.communityUuid) + .replaceAll('{spaceId}', param.spaceUuid); } } diff --git a/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart b/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart index b8734eee..f2ddf24a 100644 --- a/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart +++ b/lib/pages/space_management_v2/modules/delete_space/presentation/widgets/delete_space_dialog.dart @@ -27,7 +27,7 @@ class DeleteSpaceDialog extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => DeleteSpaceBloc( - RemoteDeleteSpaceService(httpService: HTTPService()), + RemoteDeleteSpaceService(HTTPService()), ), child: Builder( builder: (context) => Dialog( From fe4063ef8f287b780e6fdaf3c6d0d587167f052a Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 15 Jul 2025 11:20:13 +0300 Subject: [PATCH 038/118] changed the title from Routine to Workflow Automation --- .../all_devices/view/device_managment_page.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/device_managment/all_devices/view/device_managment_page.dart b/lib/pages/device_managment/all_devices/view/device_managment_page.dart index 8210fb2f..17650f36 100644 --- a/lib/pages/device_managment/all_devices/view/device_managment_page.dart +++ b/lib/pages/device_managment/all_devices/view/device_managment_page.dart @@ -24,12 +24,12 @@ class DeviceManagementPage extends StatefulWidget with HelperResponsiveLayout { } class _DeviceManagementPageState extends State { - -@override + @override void initState() { context.read().add(InitialEvent()); super.initState(); } + @override Widget build(BuildContext context) { return MultiBlocProvider( @@ -90,7 +90,7 @@ class _DeviceManagementPageState extends State { const TriggerSwitchTabsEvent(isRoutineTab: true)); }, child: Text( - 'Routines', + 'Workflow Automation', style: context.textTheme.titleMedium?.copyWith( color: state.routineTab ? ColorsManager.whiteColors From fa930571dc45cfa97ad3493fd911732d186f3581 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Jul 2025 12:27:45 +0300 Subject: [PATCH 039/118] Ensure proper handling of null selectedSpace in CommunityStructureCanvas during widget updates to prevent unnecessary processing. --- .../main_module/widgets/community_structure_canvas.dart | 1 + 1 file changed, 1 insertion(+) 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 65c0b95f..02a49d8c 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 @@ -53,6 +53,7 @@ class _CommunityStructureCanvasState extends State @override void didUpdateWidget(covariant CommunityStructureCanvas oldWidget) { super.didUpdateWidget(oldWidget); + if (widget.selectedSpace == null) return; if (widget.selectedSpace?.uuid != oldWidget.selectedSpace?.uuid) { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { From f832c5d884ea950225a57a8910aa00f80cc85ce1 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Jul 2025 12:30:24 +0300 Subject: [PATCH 040/118] Refactor SpaceManagementCommunityStructure to improve widget structure and visibility handling. Introduce separate methods for building the canvas and empty state, enhancing readability and maintainability. --- .../space_management_community_structure.dart | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/widgets/space_management_community_structure.dart b/lib/pages/space_management_v2/main_module/widgets/space_management_community_structure.dart index 050eac87..b325c1ec 100644 --- a/lib/pages/space_management_v2/main_module/widgets/space_management_community_structure.dart +++ b/lib/pages/space_management_v2/main_module/widgets/space_management_community_structure.dart @@ -3,6 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/create_space_button.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; +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/communities_tree_selection_bloc/communities_tree_selection_bloc.dart'; class SpaceManagementCommunityStructure extends StatelessWidget { @@ -13,28 +15,44 @@ class SpaceManagementCommunityStructure extends StatelessWidget { final selectionBloc = context.watch().state; final selectedCommunity = selectionBloc.selectedCommunity; final selectedSpace = selectionBloc.selectedSpace; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const CommunityStructureHeader(), + Visibility( + visible: selectedCommunity!.spaces.isNotEmpty, + replacement: _buildEmptyWidget(selectedCommunity), + child: _buildCanvas(selectedCommunity, selectedSpace), + ), + ], + ); + } + + Widget _buildCanvas( + CommunityModel selectedCommunity, + SpaceModel? selectedSpace, + ) { + return Expanded( + child: CommunityStructureCanvas( + community: selectedCommunity, + selectedSpace: selectedSpace, + ), + ); + } + + Widget _buildEmptyWidget(CommunityModel selectedCommunity) { const spacer = Spacer(flex: 6); - return Visibility( - visible: selectedCommunity!.spaces.isNotEmpty, - replacement: Row( + + return Expanded( + child: Row( children: [ spacer, Expanded( - child: CreateSpaceButton(communityUuid: selectedCommunity.uuid), - ), - spacer - ], - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const CommunityStructureHeader(), - Expanded( - child: CommunityStructureCanvas( - community: selectedCommunity, - selectedSpace: selectedSpace, + child: CreateSpaceButton( + communityUuid: selectedCommunity.uuid, ), ), + spacer, ], ), ); From df39fca050099f000d6d977c431cab242ceacc84 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Jul 2025 12:49:37 +0300 Subject: [PATCH 041/118] Refactor CommunityStructureHeaderActionButtons to simplify null handling for selectedSpace and improve widget structure. Ensure buttons are always displayed when selectedSpace is not null, enhancing readability and maintainability. --- ...unity_structure_header_action_buttons.dart | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.dart b/lib/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.dart index a965c866..edeb4d8e 100644 --- a/lib/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.dart +++ b/lib/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.dart @@ -19,27 +19,27 @@ class CommunityStructureHeaderActionButtons extends StatelessWidget { @override Widget build(BuildContext context) { + if (selectedSpace == null) return const SizedBox.shrink(); + return Wrap( alignment: WrapAlignment.end, spacing: 10, children: [ - if (selectedSpace != null) ...[ - CommunityStructureHeaderButton( - label: 'Edit', - svgAsset: Assets.editSpace, - onPressed: () => onEdit(selectedSpace!), - ), - CommunityStructureHeaderButton( - label: 'Duplicate', - svgAsset: Assets.duplicate, - onPressed: () => onDuplicate(selectedSpace!), - ), - CommunityStructureHeaderButton( - label: 'Delete', - svgAsset: Assets.spaceDelete, - onPressed: () => onDelete(selectedSpace!), - ), - ], + CommunityStructureHeaderButton( + label: 'Edit', + svgAsset: Assets.editSpace, + onPressed: () => onEdit(selectedSpace!), + ), + CommunityStructureHeaderButton( + label: 'Duplicate', + svgAsset: Assets.duplicate, + onPressed: () => onDuplicate(selectedSpace!), + ), + CommunityStructureHeaderButton( + label: 'Delete', + svgAsset: Assets.spaceDelete, + onPressed: () => onDelete(selectedSpace!), + ), ], ); } From 903c5dd29b28b9143901f280fdc6c1610672e860 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Jul 2025 12:55:26 +0300 Subject: [PATCH 042/118] Refactor SpacesRecursiveHelper to improve variable naming and enhance readability. Update mapping logic to clarify the distinction between updated and non-null spaces, ensuring better maintainability of the recursive space handling. --- .../main_module/helpers/spaces_recursive_helper.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart b/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart index e751e8d8..b725bf31 100644 --- a/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart +++ b/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart @@ -7,13 +7,15 @@ abstract final class SpacesRecursiveHelper { SpaceDetailsModel updatedSpace, ) { return spaces.map((space) { - if (space.uuid == updatedSpace.uuid) { + final isUpdatedSpace = space.uuid == updatedSpace.uuid; + if (isUpdatedSpace) { return space.copyWith( spaceName: updatedSpace.spaceName, icon: updatedSpace.icon, ); } - if (space.children.isNotEmpty) { + final hasChildren = space.children.isNotEmpty; + if (hasChildren) { return space.copyWith( children: recusrivelyUpdate(space.children, updatedSpace), ); @@ -26,7 +28,7 @@ abstract final class SpacesRecursiveHelper { List spaces, String spaceUuid, ) { - final s = spaces.map((space) { + final updatedSpaces = spaces.map((space) { if (space.uuid == spaceUuid) return null; if (space.children.isNotEmpty) { return space.copyWith( @@ -35,7 +37,7 @@ abstract final class SpacesRecursiveHelper { } return space; }).toList(); - - return s.whereType().toList(); + final nonNullSpaces = updatedSpaces.whereType().toList(); + return nonNullSpaces; } } From c60078c96ab7aac6d4fedc8c576b96aff0c1a515 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Jul 2025 13:04:37 +0300 Subject: [PATCH 043/118] Refactor CommunityStructureCanvas to improve widget structure by rearranging the InteractiveViewer and GestureDetector hierarchy. This change enhances readability and maintainability while ensuring proper interaction handling. --- .../widgets/community_structure_canvas.dart | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 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 02a49d8c..0fa5630b 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 @@ -453,17 +453,17 @@ class _CommunityStructureCanvasState extends State @override Widget build(BuildContext context) { final treeWidgets = _buildTreeWidgets(); - return InteractiveViewer( - transformationController: _transformationController, - boundaryMargin: EdgeInsets.symmetric( - horizontal: context.screenWidth * 0.3, - vertical: context.screenHeight * 0.3, - ), - minScale: 0.5, - maxScale: 3.0, - constrained: false, - child: GestureDetector( - onTap: _resetSelectionAndZoom, + return GestureDetector( + onTap: _resetSelectionAndZoom, + child: InteractiveViewer( + transformationController: _transformationController, + boundaryMargin: EdgeInsets.symmetric( + horizontal: context.screenWidth * 0.3, + vertical: context.screenHeight * 0.3, + ), + minScale: 0.5, + maxScale: 3.0, + constrained: false, child: SizedBox( width: context.screenWidth * 5, height: context.screenHeight * 5, From e74065250796fbdb69b8f562da355b771b091dc9 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Jul 2025 13:11:22 +0300 Subject: [PATCH 044/118] Refactor PlusButtonWidget and SpaceCardWidget to improve widget structure and interaction handling. Replace GestureDetector with IconButton for better usability and update positioning logic for the PlusButtonWidget, enhancing maintainability and readability. --- .../widgets/plus_button_widget.dart | 27 +++++++------------ .../widgets/space_card_widget.dart | 5 ++-- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/widgets/plus_button_widget.dart b/lib/pages/space_management_v2/main_module/widgets/plus_button_widget.dart index 68169861..236b73c9 100644 --- a/lib/pages/space_management_v2/main_module/widgets/plus_button_widget.dart +++ b/lib/pages/space_management_v2/main_module/widgets/plus_button_widget.dart @@ -2,31 +2,22 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class PlusButtonWidget extends StatelessWidget { - final Offset offset; - final void Function() onButtonTap; + final void Function() onTap; const PlusButtonWidget({ + required this.onTap, super.key, - required this.offset, - required this.onButtonTap, }); @override Widget build(BuildContext context) { - return GestureDetector( - onTap: onButtonTap, - child: Container( - width: 30, - height: 30, - decoration: const BoxDecoration( - color: ColorsManager.spaceColor, - shape: BoxShape.circle, - ), - child: const Icon( - Icons.add, - color: ColorsManager.whiteColors, - size: 20, - ), + return IconButton.filled( + onPressed: onTap, + style: IconButton.styleFrom(backgroundColor: ColorsManager.spaceColor), + icon: const Icon( + Icons.add, + color: ColorsManager.whiteColors, + size: 20, ), ); } diff --git a/lib/pages/space_management_v2/main_module/widgets/space_card_widget.dart b/lib/pages/space_management_v2/main_module/widgets/space_card_widget.dart index 54902280..da79b817 100644 --- a/lib/pages/space_management_v2/main_module/widgets/space_card_widget.dart +++ b/lib/pages/space_management_v2/main_module/widgets/space_card_widget.dart @@ -29,10 +29,9 @@ class _SpaceCardWidgetState extends State { widget.buildSpaceContainer(), if (isHovered) Positioned( - bottom: 0, + bottom: -5, child: PlusButtonWidget( - offset: Offset.zero, - onButtonTap: widget.onTap, + onTap: widget.onTap, ), ), ], From 5a3cf93748d7143414c711649b906bdba6fb3094 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Jul 2025 14:37:16 +0300 Subject: [PATCH 045/118] Improved UniqueSubspacesDecorator implementation to improve handling of duplicate subspace names. --- .../data/services/unique_subspaces_decorator.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pages/space_management_v2/modules/space_details/data/services/unique_subspaces_decorator.dart b/lib/pages/space_management_v2/modules/space_details/data/services/unique_subspaces_decorator.dart index 8309c545..df0ab727 100644 --- a/lib/pages/space_management_v2/modules/space_details/data/services/unique_subspaces_decorator.dart +++ b/lib/pages/space_management_v2/modules/space_details/data/services/unique_subspaces_decorator.dart @@ -12,13 +12,17 @@ class UniqueSubspacesDecorator implements SpaceDetailsService { final response = await _decoratee.getSpaceDetails(param); final uniqueSubspaces = {}; + final duplicateNames = {}; for (final subspace in response.subspaces) { final normalizedName = subspace.name.trim().toLowerCase(); - if (!uniqueSubspaces.containsKey(normalizedName)) { + if (uniqueSubspaces.containsKey(normalizedName)) { + duplicateNames.add(normalizedName); + } else { uniqueSubspaces[normalizedName] = subspace; } } + duplicateNames.forEach(uniqueSubspaces.remove); return response.copyWith( subspaces: uniqueSubspaces.values.toList(), From f539b0ac8d559ab2e18758a50e58828e188fbd26 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Jul 2025 14:38:44 +0300 Subject: [PATCH 046/118] Rename `UniqueSubspacesDecorator` to `UniqueSpaceDetailsSpacesDecoratorService` --- .../main_module/views/space_management_page.dart | 4 ++-- ...art => unique_space_details_spaces_decorator_service.dart} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename lib/pages/space_management_v2/modules/space_details/data/services/{unique_subspaces_decorator.dart => unique_space_details_spaces_decorator_service.dart} (88%) 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 40a37891..55e47de1 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 @@ -10,7 +10,7 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/presen 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/space_details/data/services/remote_space_details_service.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/data/services/unique_subspaces_decorator.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'; import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/theme/responsive_text_theme.dart'; @@ -49,7 +49,7 @@ class _SpaceManagementPageState extends State { ), BlocProvider( create: (context) => SpaceDetailsBloc( - UniqueSubspacesDecorator( + UniqueSpaceDetailsSpacesDecoratorService( RemoteSpaceDetailsService(httpService: HTTPService()), ), ), diff --git a/lib/pages/space_management_v2/modules/space_details/data/services/unique_subspaces_decorator.dart b/lib/pages/space_management_v2/modules/space_details/data/services/unique_space_details_spaces_decorator_service.dart similarity index 88% rename from lib/pages/space_management_v2/modules/space_details/data/services/unique_subspaces_decorator.dart rename to lib/pages/space_management_v2/modules/space_details/data/services/unique_space_details_spaces_decorator_service.dart index df0ab727..72cf8b89 100644 --- a/lib/pages/space_management_v2/modules/space_details/data/services/unique_subspaces_decorator.dart +++ b/lib/pages/space_management_v2/modules/space_details/data/services/unique_space_details_spaces_decorator_service.dart @@ -2,10 +2,10 @@ import 'package:syncrow_web/pages/space_management_v2/modules/space_details/doma import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart'; -class UniqueSubspacesDecorator implements SpaceDetailsService { +class UniqueSpaceDetailsSpacesDecoratorService implements SpaceDetailsService { final SpaceDetailsService _decoratee; - const UniqueSubspacesDecorator(this._decoratee); + const UniqueSpaceDetailsSpacesDecoratorService(this._decoratee); @override Future getSpaceDetails(LoadSpaceDetailsParam param) async { From d65f9ceea9a230c2abe0ea374e7e5ef56c87122f Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Tue, 15 Jul 2025 14:53:04 +0300 Subject: [PATCH 047/118] Enhance AddDeviceTypeWidget to support initial product counts and update selection logic. Modify AssignTagsDialog to pass initial products from space allocations, improving user experience and maintainability. --- .../widgets/add_device_type_widget.dart | 42 +++++++++++++++++-- .../widgets/assign_tags_dialog.dart | 9 +++- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/lib/pages/space_management_v2/modules/tags/presentation/widgets/add_device_type_widget.dart b/lib/pages/space_management_v2/modules/tags/presentation/widgets/add_device_type_widget.dart index 4c9990ae..2c100b15 100644 --- a/lib/pages/space_management_v2/modules/tags/presentation/widgets/add_device_type_widget.dart +++ b/lib/pages/space_management_v2/modules/tags/presentation/widgets/add_device_type_widget.dart @@ -10,7 +10,12 @@ import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; class AddDeviceTypeWidget extends StatefulWidget { - const AddDeviceTypeWidget({super.key}); + const AddDeviceTypeWidget({ + super.key, + this.initialProducts = const [], + }); + + final List initialProducts; @override State createState() => _AddDeviceTypeWidgetState(); @@ -18,6 +23,16 @@ class AddDeviceTypeWidget extends StatefulWidget { class _AddDeviceTypeWidgetState extends State { final Map _selectedProducts = {}; + final Map _initialProductCounts = {}; + + @override + void initState() { + super.initState(); + for (final product in widget.initialProducts) { + _initialProductCounts[product] = (_initialProductCounts[product] ?? 0) + 1; + } + _selectedProducts.addAll(_initialProductCounts); + } void _onIncrement(Product product) { setState(() { @@ -27,8 +42,12 @@ class _AddDeviceTypeWidgetState extends State { void _onDecrement(Product product) { setState(() { - if ((_selectedProducts[product] ?? 0) > 0) { - _selectedProducts[product] = _selectedProducts[product]! - 1; + final initialCount = _initialProductCounts[product] ?? 0; + final currentCount = _selectedProducts[product] ?? 0; + if (currentCount > initialCount) { + _selectedProducts[product] = currentCount - 1; + } else if (currentCount > 0 && initialCount == 0) { + _selectedProducts[product] = currentCount - 1; if (_selectedProducts[product] == 0) { _selectedProducts.remove(product); } @@ -63,7 +82,22 @@ class _AddDeviceTypeWidgetState extends State { actions: [ SpaceDetailsActionButtons( onSave: () { - final result = _selectedProducts.entries + final resultMap = {}; + resultMap.addAll(_selectedProducts); + + for (final entry in _initialProductCounts.entries) { + final product = entry.key; + final initialCount = entry.value; + final currentCount = resultMap[product] ?? 0; + + if (currentCount > initialCount) { + resultMap[product] = currentCount - initialCount; + } else { + resultMap.remove(product); + } + } + + final result = resultMap.entries .expand((entry) => List.generate(entry.value, (_) => entry.key)) .toList(); Navigator.of(context).pop(result); diff --git a/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_dialog.dart b/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_dialog.dart index 3f6d42ab..7a1002dc 100644 --- a/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_dialog.dart +++ b/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_dialog.dart @@ -205,7 +205,14 @@ class _AssignTagsDialogState extends State { onCancel: () async { final newProducts = await showDialog>( context: context, - builder: (context) => const AddDeviceTypeWidget(), + builder: (context) => AddDeviceTypeWidget( + initialProducts: [ + ..._space.productAllocations.map((e) => e.product), + ..._space.subspaces + .expand((s) => s.productAllocations) + .map((e) => e.product), + ], + ), ); if (newProducts == null || newProducts.isEmpty) return; From e9b4d35f97e3c08af688191244d94939e81d28dc Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 15 Jul 2025 15:17:43 +0300 Subject: [PATCH 048/118] 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 049/118] 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 050/118] 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 051/118] 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 052/118] 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 308eb65d469141eda5f302bc5a36e798b014a09a Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 16 Jul 2025 09:56:55 +0300 Subject: [PATCH 053/118] Update error handling in RemoteUpdateSpaceService to retrieve error messages from the 'message' field instead of 'error', enhancing clarity in error reporting. --- .../update_space/data/services/remote_update_space_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/space_management_v2/modules/update_space/data/services/remote_update_space_service.dart b/lib/pages/space_management_v2/modules/update_space/data/services/remote_update_space_service.dart index a70d3b85..2585b1e7 100644 --- a/lib/pages/space_management_v2/modules/update_space/data/services/remote_update_space_service.dart +++ b/lib/pages/space_management_v2/modules/update_space/data/services/remote_update_space_service.dart @@ -34,7 +34,7 @@ class RemoteUpdateSpaceService implements UpdateSpaceService { } on DioException catch (e) { final message = e.response?.data as Map?; final error = message?['error'] as Map?; - final errorMessage = error?['error'] as String? ?? ''; + final errorMessage = error?['message'] as String? ?? ''; final formattedErrorMessage = [ _defaultErrorMessage, errorMessage, From 39c5fd1bcaa32f1dd870414bf0e32a59f4cbc124 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 16 Jul 2025 09:57:29 +0300 Subject: [PATCH 054/118] Refactor SpaceDetailsModel to integrate Subspace and ProductAllocation models, enhancing data structure and serialization. Update related widgets to utilize Subspace instead of SpaceDetailsModel for improved clarity and maintainability. --- .../services/remote_create_space_service.dart | 63 +++++++++++++ .../domain/params/create_space_param.dart | 17 ++++ .../domain/services/create_space_service.dart | 6 ++ ...pace_details_spaces_decorator_service.dart | 1 + .../domain/models/product_allocation.dart | 47 ++++++++++ .../domain/models/space_details_model.dart | 91 ++++--------------- .../space_details/domain/models/subspace.dart | 48 ++++++++++ .../widgets/space_sub_spaces_box.dart | 2 +- .../widgets/space_sub_spaces_dialog.dart | 2 +- .../widgets/sub_spaces_input.dart | 2 +- .../presentation/widgets/subspace_chip.dart | 2 +- .../widgets/subspace_name_display_widget.dart | 2 +- .../widgets/assign_tags_dialog.dart | 1 + .../widgets/assign_tags_table.dart | 3 +- .../domain/params/update_space_param.dart | 31 +------ .../space_details_model_bloc.dart | 2 + 16 files changed, 210 insertions(+), 110 deletions(-) create mode 100644 lib/pages/space_management_v2/modules/create_space/data/services/remote_create_space_service.dart create mode 100644 lib/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart create mode 100644 lib/pages/space_management_v2/modules/create_space/domain/services/create_space_service.dart create mode 100644 lib/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart create mode 100644 lib/pages/space_management_v2/modules/space_details/domain/models/subspace.dart diff --git a/lib/pages/space_management_v2/modules/create_space/data/services/remote_create_space_service.dart b/lib/pages/space_management_v2/modules/create_space/data/services/remote_create_space_service.dart new file mode 100644 index 00000000..f8dba7dc --- /dev/null +++ b/lib/pages/space_management_v2/modules/create_space/data/services/remote_create_space_service.dart @@ -0,0 +1,63 @@ +import 'package:dio/dio.dart'; +import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/create_space/domain/services/create_space_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; + +final class RemoteCreateSpaceService implements CreateSpaceService { + const RemoteCreateSpaceService(this._httpService); + + final HTTPService _httpService; + + static const _defaultErrorMessage = 'Failed to create space'; + + @override + Future createSpace(CreateSpaceParam param) async { + try { + final path = await _makeUrl(param); + final response = await _httpService.post( + path: path, + body: param.toJson(), + expectedResponseModel: (data) { + final response = data as Map; + final isSuccess = response['success'] as bool; + if (!isSuccess) { + throw APIException(response['error'] as String); + } + + return SpaceModel.fromJson(response['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); + } + } + + Future _makeUrl(CreateSpaceParam param) async { + final projectUuid = await ProjectManager.getProjectUUID(); + if (projectUuid == null || projectUuid.isEmpty) { + throw APIException('Project UUID is not set'); + } + + final communityUuid = param.communityUuid; + if (communityUuid.isEmpty) { + throw APIException('Community UUID is not set'); + } + + return '/projects/$projectUuid/communities/$communityUuid'; + } +} diff --git a/lib/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart b/lib/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart new file mode 100644 index 00000000..75c6d1d5 --- /dev/null +++ b/lib/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart @@ -0,0 +1,17 @@ +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; + +class CreateSpaceParam { + final String communityUuid; + final SpaceDetailsModel space; + final String? parentUuid; + + const CreateSpaceParam({ + required this.communityUuid, + required this.space, + required this.parentUuid, + }); + + Map toJson() { + return {'spaceModelUuid': parentUuid, ...space.toJson()}; + } +} diff --git a/lib/pages/space_management_v2/modules/create_space/domain/services/create_space_service.dart b/lib/pages/space_management_v2/modules/create_space/domain/services/create_space_service.dart new file mode 100644 index 00000000..553b87e7 --- /dev/null +++ b/lib/pages/space_management_v2/modules/create_space/domain/services/create_space_service.dart @@ -0,0 +1,6 @@ +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart'; + +abstract interface class CreateSpaceService { + Future createSpace(CreateSpaceParam param); +} diff --git a/lib/pages/space_management_v2/modules/space_details/data/services/unique_space_details_spaces_decorator_service.dart b/lib/pages/space_management_v2/modules/space_details/data/services/unique_space_details_spaces_decorator_service.dart index 72cf8b89..da7fd4eb 100644 --- a/lib/pages/space_management_v2/modules/space_details/data/services/unique_space_details_spaces_decorator_service.dart +++ b/lib/pages/space_management_v2/modules/space_details/data/services/unique_space_details_spaces_decorator_service.dart @@ -1,4 +1,5 @@ import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart'; diff --git a/lib/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart b/lib/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart new file mode 100644 index 00000000..250dad5c --- /dev/null +++ b/lib/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart @@ -0,0 +1,47 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/products/domain/models/product.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart'; +import 'package:uuid/uuid.dart'; + +class ProductAllocation extends Equatable { + final String uuid; + final Product product; + final Tag tag; + + const ProductAllocation({ + required this.uuid, + required this.product, + required this.tag, + }); + + factory ProductAllocation.fromJson(Map json) { + return ProductAllocation( + uuid: json['uuid'] as String? ?? const Uuid().v4(), + product: Product.fromJson(json['product'] as Map), + tag: Tag.fromJson(json['tag'] as Map), + ); + } + + ProductAllocation copyWith({ + String? uuid, + Product? product, + Tag? tag, + }) { + return ProductAllocation( + uuid: uuid ?? this.uuid, + product: product ?? this.product, + tag: tag ?? this.tag, + ); + } + + Map toJson() { + final isNewTag = tag.uuid.isEmpty; + return { + if (isNewTag) 'tagName': tag.name else 'tagUuid': tag.uuid, + 'productUuid': product.uuid, + }; + } + + @override + List get props => [uuid, product, tag]; +} diff --git a/lib/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart b/lib/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart index ec3c9f81..bd8ff714 100644 --- a/lib/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart +++ b/lib/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart @@ -1,8 +1,7 @@ import 'package:equatable/equatable.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/products/domain/models/product.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -import 'package:uuid/uuid.dart'; class SpaceDetailsModel extends Equatable { final String uuid; @@ -26,6 +25,7 @@ class SpaceDetailsModel extends Equatable { productAllocations: [], subspaces: [], ); + factory SpaceDetailsModel.fromJson(Map json) { return SpaceDetailsModel( uuid: json['uuid'] as String, @@ -56,78 +56,21 @@ class SpaceDetailsModel extends Equatable { ); } - @override - List get props => [uuid, spaceName, icon, productAllocations, subspaces]; -} - -class ProductAllocation extends Equatable { - final String uuid; - final Product product; - final Tag tag; - - const ProductAllocation({ - required this.uuid, - required this.product, - required this.tag, - }); - - factory ProductAllocation.fromJson(Map json) { - return ProductAllocation( - uuid: json['uuid'] as String? ?? const Uuid().v4(), - product: Product.fromJson(json['product'] as Map), - tag: Tag.fromJson(json['tag'] as Map), - ); - } - - ProductAllocation copyWith({ - String? uuid, - Product? product, - Tag? tag, - }) { - return ProductAllocation( - uuid: uuid ?? this.uuid, - product: product ?? this.product, - tag: tag ?? this.tag, - ); + Map toJson() { + return { + 'spaceName': spaceName, + 'icon': icon, + 'subspaces': subspaces.map((e) => e.toJson()).toList(), + 'productAllocations': productAllocations.map((e) => e.toJson()).toList(), + }; } @override - List get props => [uuid, product, tag]; -} - -class Subspace extends Equatable { - final String uuid; - final String name; - final List productAllocations; - - const Subspace({ - required this.uuid, - required this.name, - required this.productAllocations, - }); - - factory Subspace.fromJson(Map json) { - return Subspace( - uuid: json['uuid'] as String, - name: json['subspaceName'] as String, - productAllocations: (json['productAllocations'] as List) - .map((e) => ProductAllocation.fromJson(e as Map)) - .toList(), - ); - } - - Subspace copyWith({ - String? uuid, - String? name, - List? productAllocations, - }) { - return Subspace( - uuid: uuid ?? this.uuid, - name: name ?? this.name, - productAllocations: productAllocations ?? this.productAllocations, - ); - } - - @override - List get props => [uuid, name, productAllocations]; + List get props => [ + uuid, + spaceName, + icon, + productAllocations, + subspaces, + ]; } diff --git a/lib/pages/space_management_v2/modules/space_details/domain/models/subspace.dart b/lib/pages/space_management_v2/modules/space_details/domain/models/subspace.dart new file mode 100644 index 00000000..4a962eb3 --- /dev/null +++ b/lib/pages/space_management_v2/modules/space_details/domain/models/subspace.dart @@ -0,0 +1,48 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart'; + +class Subspace extends Equatable { + final String uuid; + final String name; + final List productAllocations; + + const Subspace({ + required this.uuid, + required this.name, + required this.productAllocations, + }); + + factory Subspace.fromJson(Map json) { + return Subspace( + uuid: json['uuid'] as String, + name: json['subspaceName'] as String, + productAllocations: (json['productAllocations'] as List) + .map((e) => ProductAllocation.fromJson(e as Map)) + .toList(), + ); + } + + Map toJson() { + final isNewSubspace = uuid.endsWith('-NewTag'); + return { + if (!isNewSubspace) 'uuid': uuid, + 'subspaceName': name, + 'productAllocations': productAllocations.map((e) => e.toJson()).toList(), + }; + } + + Subspace copyWith({ + String? uuid, + String? name, + List? productAllocations, + }) { + return Subspace( + uuid: uuid ?? this.uuid, + name: name ?? this.name, + productAllocations: productAllocations ?? this.productAllocations, + ); + } + + @override + List get props => [uuid, name, productAllocations]; +} diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_box.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_box.dart index 68bf68bd..719988c6 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_box.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_box.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/common/edit_chip.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/button_content_widget.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart'; diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart index 8faac548..587c9ea7 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/sub_spaces_input.dart'; import 'package:uuid/uuid.dart'; diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/sub_spaces_input.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/sub_spaces_input.dart index 854b79bc..591f741c 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/sub_spaces_input.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/sub_spaces_input.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_chip.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_chip.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_chip.dart index a80ddd15..6bc9f6d1 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_chip.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_chip.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart index bf13ffd3..e72bffde 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; diff --git a/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_dialog.dart b/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_dialog.dart index 7a1002dc..d14a3923 100644 --- a/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_dialog.dart +++ b/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_dialog.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/products/domain/models/product.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart'; diff --git a/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_table.dart b/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_table.dart index 6e7e2097..1711e019 100644 --- a/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_table.dart +++ b/lib/pages/space_management_v2/modules/tags/presentation/widgets/assign_tags_table.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/common/dialog_dropdown.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/tags/data/services/remote_tags_service.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/tags/presentation/bloc/tags_bloc.dart'; diff --git a/lib/pages/space_management_v2/modules/update_space/domain/params/update_space_param.dart b/lib/pages/space_management_v2/modules/update_space/domain/params/update_space_param.dart index 5dd9106d..25d54caa 100644 --- a/lib/pages/space_management_v2/modules/update_space/domain/params/update_space_param.dart +++ b/lib/pages/space_management_v2/modules/update_space/domain/params/update_space_param.dart @@ -9,34 +9,5 @@ class UpdateSpaceParam { final SpaceDetailsModel space; final String communityUuid; - Map toJson() { - return { - 'spaceName': space.spaceName, - 'icon': space.icon, - 'subspaces': space.subspaces.map((e) => e._toJson()).toList(), - 'productAllocations': - space.productAllocations.map((e) => e._toJson()).toList(), - }; - } -} - -extension _ProductAllocationToJson on ProductAllocation { - Map _toJson() { - final isNewTag = tag.uuid.isEmpty; - return { - if (isNewTag) 'tagName': tag.name else 'tagUuid': tag.uuid, - 'productUuid': product.uuid, - }; - } -} - -extension _SubspaceToJson on Subspace { - Map _toJson() { - final isNewSubspace = uuid.endsWith('-NewTag'); - return { - if (!isNewSubspace) 'uuid': uuid, - 'subspaceName': name, - 'productAllocations': productAllocations.map((e) => e._toJson()).toList(), - }; - } + Map toJson() => space.toJson(); } diff --git a/lib/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart b/lib/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart index 15a22fda..4c11e694 100644 --- a/lib/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart +++ b/lib/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart @@ -1,6 +1,8 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart'; part 'space_details_model_event.dart'; From 9a203b2fd9de08c531c045611df941f935d6ae4e Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 16 Jul 2025 10:01:51 +0300 Subject: [PATCH 055/118] Add CreateSpaceBloc, CreateSpaceEvent, and CreateSpaceState for managing space creation logic. Implement event handling and state management to enhance user experience during space creation. --- .../presentation/bloc/create_space_bloc.dart | 34 +++++++++++++++++++ .../presentation/bloc/create_space_event.dart | 17 ++++++++++ .../presentation/bloc/create_space_state.dart | 31 +++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 lib/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_bloc.dart create mode 100644 lib/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_event.dart create mode 100644 lib/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_state.dart diff --git a/lib/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_bloc.dart b/lib/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_bloc.dart new file mode 100644 index 00000000..46a8abb8 --- /dev/null +++ b/lib/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_bloc.dart @@ -0,0 +1,34 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/create_space/domain/services/create_space_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; + +part 'create_space_event.dart'; +part 'create_space_state.dart'; + +class CreateSpaceBloc extends Bloc { + CreateSpaceBloc( + this._createSpaceService, + ) : super(const CreateSpaceInitial()) { + on(_onCreateSpace); + } + + final CreateSpaceService _createSpaceService; + + Future _onCreateSpace( + CreateSpace event, + Emitter emit, + ) async { + emit(const CreateSpaceLoading()); + try { + final result = await _createSpaceService.createSpace(event.param); + emit(CreateSpaceSuccess(result)); + } on APIException catch (e) { + emit(CreateSpaceFailure(e.message)); + } catch (e) { + emit(CreateSpaceFailure(e.toString())); + } + } +} diff --git a/lib/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_event.dart b/lib/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_event.dart new file mode 100644 index 00000000..09ef8698 --- /dev/null +++ b/lib/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_event.dart @@ -0,0 +1,17 @@ +part of 'create_space_bloc.dart'; + +sealed class CreateSpaceEvent extends Equatable { + const CreateSpaceEvent(); + + @override + List get props => []; +} + +final class CreateSpace extends CreateSpaceEvent { + const CreateSpace(this.param); + + final CreateSpaceParam param; + + @override + List get props => [param]; +} diff --git a/lib/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_state.dart b/lib/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_state.dart new file mode 100644 index 00000000..c5b035bb --- /dev/null +++ b/lib/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_state.dart @@ -0,0 +1,31 @@ +part of 'create_space_bloc.dart'; + +sealed class CreateSpaceState extends Equatable { + const CreateSpaceState(); + + @override + List get props => []; +} + +final class CreateSpaceInitial extends CreateSpaceState { + const CreateSpaceInitial(); +} + +final class CreateSpaceLoading extends CreateSpaceState { + const CreateSpaceLoading(); +} + +final class CreateSpaceSuccess extends CreateSpaceState { + const CreateSpaceSuccess(this.space); + + final SpaceModel space; + + @override + List get props => [space]; +} + +final class CreateSpaceFailure extends CreateSpaceState { + const CreateSpaceFailure(this.errorMessage); + + final String errorMessage; +} From 4ef4858ceeca7b11e54437daeed0e83bbfac7036 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 16 Jul 2025 10:29:43 +0300 Subject: [PATCH 056/118] Added x, and y to the `toJson` method of `CreateSpaceParam`, since the API still needs them as required properties. Revert the once the BE removes those properties. --- .../create_space/domain/params/create_space_param.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart b/lib/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart index 75c6d1d5..931588d2 100644 --- a/lib/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart +++ b/lib/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart @@ -12,6 +12,11 @@ class CreateSpaceParam { }); Map toJson() { - return {'spaceModelUuid': parentUuid, ...space.toJson()}; + return { + 'spaceModelUuid': parentUuid, + ...space.toJson(), + 'x': 0, + 'y': 0, + }; } } From db157f30c5a86f2c69c1ddf136454fe315f0eb88 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Wed, 16 Jul 2025 10:35:16 +0300 Subject: [PATCH 057/118] 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 8e303af0d73fd908ea6bb2d20a685b9696de1b1d Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 16 Jul 2025 11:01:26 +0300 Subject: [PATCH 058/118] Update RemoteCreateSpaceService to correct API endpoint for space creation, changing the return path to include '/spaces' for accurate resource targeting. --- .../create_space/data/services/remote_create_space_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/space_management_v2/modules/create_space/data/services/remote_create_space_service.dart b/lib/pages/space_management_v2/modules/create_space/data/services/remote_create_space_service.dart index f8dba7dc..768f6438 100644 --- a/lib/pages/space_management_v2/modules/create_space/data/services/remote_create_space_service.dart +++ b/lib/pages/space_management_v2/modules/create_space/data/services/remote_create_space_service.dart @@ -58,6 +58,6 @@ final class RemoteCreateSpaceService implements CreateSpaceService { throw APIException('Community UUID is not set'); } - return '/projects/$projectUuid/communities/$communityUuid'; + return '/projects/$projectUuid/communities/$communityUuid/spaces'; } } From 3e634dc7a2970c4b77f653c07de3425c35feaa84 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Wed, 16 Jul 2025 11:02:32 +0300 Subject: [PATCH 059/118] fix communities filtiring issue --- .../device_managment_bloc.dart | 18 +++++++++++------- .../bloc/routine_bloc/routine_bloc.dart | 8 ++++++-- lib/services/devices_mang_api.dart | 8 ++++++-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart b/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart index d8cd04df..f79528f8 100644 --- a/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart +++ b/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart @@ -53,8 +53,9 @@ class DeviceManagementBloc for (var community in spaceBloc.state.selectedCommunities) { final spacesList = spaceBloc.state.selectedCommunityAndSpaces[community] ?? []; - devices.addAll(await DevicesManagementApi() - .fetchDevices(projectUuid, spacesId: spacesList)); + devices.addAll(await DevicesManagementApi().fetchDevices(projectUuid, + spacesId: spacesList, + communities: spaceBloc.state.selectedCommunities)); } } @@ -158,7 +159,8 @@ class DeviceManagementBloc add(FilterDevices(_getFilterFromIndex(_selectedIndex))); } - void _onSelectDevice(SelectDevice event, Emitter emit) { + void _onSelectDevice( + SelectDevice event, Emitter emit) { final selectedUuid = event.selectedDevice.uuid; if (_selectedDevices.any((device) => device.uuid == selectedUuid)) { @@ -254,7 +256,8 @@ class DeviceManagementBloc _onlineCount = _devices.where((device) => device.online == true).length; _offlineCount = _devices.where((device) => device.online == false).length; _lowBatteryCount = _devices - .where((device) => device.batteryLevel != null && device.batteryLevel! < 20) + .where((device) => + device.batteryLevel != null && device.batteryLevel! < 20) .length; } @@ -271,7 +274,8 @@ class DeviceManagementBloc } } - void _onSearchDevices(SearchDevices event, Emitter emit) { + void _onSearchDevices( + SearchDevices event, Emitter emit) { if ((event.community == null || event.community!.isEmpty) && (event.unitName == null || event.unitName!.isEmpty) && (event.deviceNameOrProductName == null || @@ -435,8 +439,8 @@ class DeviceManagementBloc final selectedDevices = loaded.selectedDevice?.map((device) { if (device.uuid == event.deviceId) { return device.copyWith( - subspace: - device.subspace?.copyWith(subspaceName: event.newSubSpaceName)); + subspace: device.subspace + ?.copyWith(subspaceName: event.newSubSpaceName)); } return device; }).toList(); diff --git a/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart b/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart index 971f4f8c..a26a2715 100644 --- a/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart +++ b/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart @@ -937,13 +937,17 @@ class RoutineBloc extends Bloc { List spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? []; - devices.addAll(await DevicesManagementApi() - .fetchDevices(projectUuid, spacesId: spacesList)); + devices.addAll(await DevicesManagementApi().fetchDevices( + projectUuid, + spacesId: spacesList, + communities: spaceBloc.state.selectedCommunities, + )); } } else { devices.addAll(await DevicesManagementApi().fetchDevices( projectUuid, spacesId: [createRoutineBloc.selectedSpaceId], + communities: spaceBloc.state.selectedCommunities, )); } diff --git a/lib/services/devices_mang_api.dart b/lib/services/devices_mang_api.dart index 8c74dbb1..684165e2 100644 --- a/lib/services/devices_mang_api.dart +++ b/lib/services/devices_mang_api.dart @@ -13,11 +13,15 @@ import 'package:syncrow_web/utils/constants/api_const.dart'; class DevicesManagementApi { Future> fetchDevices(String projectId, - {List? spacesId}) async { + {List? spacesId, List? communities}) async { try { final response = await HTTPService().get( path: ApiEndpoints.getSpaceDevices.replaceAll('{projectId}', projectId), - queryParameters: {if (spacesId != null) 'spaces': spacesId}, + queryParameters: { + if (spacesId != null && spacesId.isNotEmpty) 'spaces': spacesId, + if (communities != null && communities.isNotEmpty) + 'communities': communities, + }, showServerMessage: true, expectedResponseModel: (json) { final List jsonData = json['data'] as List; From 62f67f5a5f8250ff94ad4697fd9ce0c11eb0e08d Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 16 Jul 2025 11:16:41 +0300 Subject: [PATCH 060/118] Refactor CreateSpaceButton and CommunityStructureCanvas to utilize CommunityModel directly, enhancing data handling during space creation. Implement success callback for space creation to update community state in CommunitiesBloc, improving user experience and maintainability. --- .../widgets/community_structure_canvas.dart | 15 +++++- .../widgets/create_space_button.dart | 17 ++++-- .../space_management_community_structure.dart | 49 ++++++++++------- .../helpers/space_details_dialog_helper.dart | 54 ++++++++++++++++--- 4 files changed, 107 insertions(+), 28 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 0fa5630b..63f8e8ec 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 @@ -268,7 +268,7 @@ class _CommunityStructureCanvasState extends State Positioned( left: createButtonX, top: createButtonY, - child: CreateSpaceButton(communityUuid: widget.community.uuid), + child: CreateSpaceButton(community: widget.community), ), ); @@ -327,6 +327,19 @@ class _CommunityStructureCanvasState extends State onTap: () => SpaceDetailsDialogHelper.showCreate( context, communityUuid: widget.community.uuid, + parentUuid: space.uuid, + onSuccess: (updatedSpaceModel) { + // TODO(FarisArmoush): insert the space under the parent, which is the space that was tapped + final newCommunity = widget.community.copyWith(); + final parentIndex = + newCommunity.spaces.indexWhere((s) => s.uuid == space.uuid); + if (parentIndex != -1) { + newCommunity.spaces.insert(parentIndex + 1, updatedSpaceModel); + } + context.read().add( + CommunitiesUpdateCommunity(newCommunity), + ); + }, ), ); diff --git a/lib/pages/space_management_v2/main_module/widgets/create_space_button.dart b/lib/pages/space_management_v2/main_module/widgets/create_space_button.dart index e6dfbb15..610962e5 100644 --- a/lib/pages/space_management_v2/main_module/widgets/create_space_button.dart +++ b/lib/pages/space_management_v2/main_module/widgets/create_space_button.dart @@ -1,14 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_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/space_details/presentation/helpers/space_details_dialog_helper.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class CreateSpaceButton extends StatefulWidget { const CreateSpaceButton({ - required this.communityUuid, + required this.community, super.key, }); - final String communityUuid; + final CommunityModel community; @override State createState() => _CreateSpaceButtonState(); @@ -25,7 +28,15 @@ class _CreateSpaceButtonState extends State { child: InkWell( onTap: () => SpaceDetailsDialogHelper.showCreate( context, - communityUuid: widget.communityUuid, + communityUuid: widget.community.uuid, + onSuccess: (updatedSpaceModel) { + final newCommunity = widget.community.copyWith( + spaces: [updatedSpaceModel, ...widget.community.spaces], + ); + context.read().add( + CommunitiesUpdateCommunity(newCommunity), + ); + }, ), child: MouseRegion( onEnter: (_) => setState(() => _isHovered = true), diff --git a/lib/pages/space_management_v2/main_module/widgets/space_management_community_structure.dart b/lib/pages/space_management_v2/main_module/widgets/space_management_community_structure.dart index b325c1ec..11478fbe 100644 --- a/lib/pages/space_management_v2/main_module/widgets/space_management_community_structure.dart +++ b/lib/pages/space_management_v2/main_module/widgets/space_management_community_structure.dart @@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/commun import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/create_space_button.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; 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'; class SpaceManagementCommunityStructure extends StatelessWidget { @@ -12,19 +13,35 @@ class SpaceManagementCommunityStructure extends StatelessWidget { @override Widget build(BuildContext context) { - final selectionBloc = context.watch().state; - final selectedCommunity = selectionBloc.selectedCommunity; - final selectedSpace = selectionBloc.selectedSpace; - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - const CommunityStructureHeader(), - Visibility( - visible: selectedCommunity!.spaces.isNotEmpty, - replacement: _buildEmptyWidget(selectedCommunity), - child: _buildCanvas(selectedCommunity, selectedSpace), - ), - ], + return BlocBuilder( + builder: (context, state) { + final selectedCommunity = state.selectedCommunity; + final selectedSpace = state.selectedSpace; + + if (selectedCommunity == null) { + return const SizedBox.shrink(); + } + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const CommunityStructureHeader(), + BlocBuilder( + builder: (context, state) { + final community = state.communities.firstWhere( + (element) => element.uuid == selectedCommunity.uuid, + orElse: () => selectedCommunity, + ); + return Visibility( + visible: community.spaces.isNotEmpty, + replacement: _buildEmptyWidget(community), + child: _buildCanvas(community, selectedSpace), + ); + }, + ), + ], + ); + }, ); } @@ -47,11 +64,7 @@ class SpaceManagementCommunityStructure extends StatelessWidget { child: Row( children: [ spacer, - Expanded( - child: CreateSpaceButton( - communityUuid: selectedCommunity.uuid, - ), - ), + Expanded(child: CreateSpaceButton(community: selectedCommunity)), spacer, ], ), diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart b/lib/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart index c5de7dad..45cb0e89 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/create_space/data/services/remote_create_space_service.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_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/domain/models/space_details_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart'; @@ -14,6 +17,8 @@ abstract final class SpaceDetailsDialogHelper { static void showCreate( BuildContext context, { required String communityUuid, + required void Function(SpaceModel updatedSpaceModel)? onSuccess, + String? parentUuid, }) { showDialog( context: context, @@ -24,14 +29,41 @@ abstract final class SpaceDetailsDialogHelper { RemoteSpaceDetailsService(httpService: HTTPService()), ), ), + BlocProvider( + create: (context) => CreateSpaceBloc( + RemoteCreateSpaceService(HTTPService()), + ), + ), ], child: Builder( - builder: (context) => SpaceDetailsDialog( - context: context, - title: const SelectableText('Create Space'), - spaceModel: SpaceModel.empty(), - onSave: (space) {}, - communityUuid: communityUuid, + builder: (context) => BlocListener( + listener: (context, state) => switch (state) { + CreateSpaceInitial() => null, + CreateSpaceLoading() => _onLoading(context), + CreateSpaceSuccess() => _onCreateSuccess( + context, + state.space, + onSuccess, + ), + CreateSpaceFailure() => _onError(context, state.errorMessage), + }, + child: SpaceDetailsDialog( + context: context, + title: const SelectableText('Create Space'), + spaceModel: SpaceModel.empty(), + onSave: (space) { + context.read().add( + CreateSpace( + CreateSpaceParam( + communityUuid: communityUuid, + space: space, + parentUuid: parentUuid, + ), + ), + ); + }, + communityUuid: communityUuid, + ), ), ), ), @@ -135,4 +167,14 @@ abstract final class SpaceDetailsDialogHelper { ), ); } + + static void _onCreateSuccess( + BuildContext context, + SpaceModel space, + void Function(SpaceModel updatedSpaceModel)? onSuccess, + ) { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + onSuccess?.call(space); + } } From 6ec972a5207f993c919ef7884854a3560e548364 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 16 Jul 2025 11:18:24 +0300 Subject: [PATCH 061/118] Integrate CommunitiesTreeSelectionBloc into CreateSpaceButton to handle space selection upon successful space creation. Update community state in CommunitiesBloc to reflect new space addition, enhancing user experience and maintainability. --- .../main_module/widgets/create_space_button.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/pages/space_management_v2/main_module/widgets/create_space_button.dart b/lib/pages/space_management_v2/main_module/widgets/create_space_button.dart index 610962e5..4032c2ab 100644 --- a/lib/pages/space_management_v2/main_module/widgets/create_space_button.dart +++ b/lib/pages/space_management_v2/main_module/widgets/create_space_button.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_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/space_details/presentation/helpers/space_details_dialog_helper.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -31,11 +32,17 @@ class _CreateSpaceButtonState extends State { communityUuid: widget.community.uuid, onSuccess: (updatedSpaceModel) { final newCommunity = widget.community.copyWith( - spaces: [updatedSpaceModel, ...widget.community.spaces], + spaces: [...widget.community.spaces, updatedSpaceModel], ); context.read().add( CommunitiesUpdateCommunity(newCommunity), ); + context.read().add( + SelectSpaceEvent( + space: updatedSpaceModel, + community: newCommunity, + ), + ); }, ), child: MouseRegion( From 739b491bd82bbe74cd9cb3568d42d53540985ea2 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Wed, 16 Jul 2025 11:21:32 +0300 Subject: [PATCH 062/118] 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 063/118] 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 064/118] 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 f03c28f7fdc1bcc11eba3aabea3b05bc8a06da3c Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 16 Jul 2025 11:47:27 +0300 Subject: [PATCH 065/118] Add recursive insertion method in SpacesRecursiveHelper for managing space hierarchy. Update CommunityStructureCanvas to utilize this method for inserting new spaces under the correct parent, enhancing community state management during space creation. --- .../helpers/spaces_recursive_helper.dart | 20 +++++++++++++++++++ .../widgets/community_structure_canvas.dart | 17 ++++++++-------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart b/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart index b725bf31..4322c4e8 100644 --- a/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart +++ b/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart @@ -40,4 +40,24 @@ abstract final class SpacesRecursiveHelper { final nonNullSpaces = updatedSpaces.whereType().toList(); return nonNullSpaces; } + + static List recusrivelyInsert( + List spaces, + SpaceModel newSpace, + String parentUuid, + ) { + return spaces.map((space) { + if (space.uuid == parentUuid) { + return space.copyWith( + children: [...space.children, newSpace], + ); + } + if (space.children.isNotEmpty) { + return space.copyWith( + children: recusrivelyInsert(space.children, newSpace, parentUuid), + ); + } + return space; + }).toList(); + } } 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 63f8e8ec..03efab7d 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 @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/models/space_connection_model.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/models/space_reorder_data_model.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart'; @@ -329,15 +330,15 @@ class _CommunityStructureCanvasState extends State communityUuid: widget.community.uuid, parentUuid: space.uuid, onSuccess: (updatedSpaceModel) { - // TODO(FarisArmoush): insert the space under the parent, which is the space that was tapped - final newCommunity = widget.community.copyWith(); - final parentIndex = - newCommunity.spaces.indexWhere((s) => s.uuid == space.uuid); - if (parentIndex != -1) { - newCommunity.spaces.insert(parentIndex + 1, updatedSpaceModel); - } + final updatedSpaces = SpacesRecursiveHelper.recusrivelyInsert( + spaces, + updatedSpaceModel, + space.uuid, + ); context.read().add( - CommunitiesUpdateCommunity(newCommunity), + CommunitiesUpdateCommunity( + widget.community.copyWith(spaces: updatedSpaces), + ), ); }, ), From bee4e054049ce0c502d9aff8d31355d6031e4d5b Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Wed, 16 Jul 2025 12:10:11 +0300 Subject: [PATCH 066/118] 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 067/118] 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 fc70669f1d2032c5923f69d9d00007e9ca78e131 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 16 Jul 2025 15:27:31 +0300 Subject: [PATCH 068/118] sends correct parentUuid key in create space. --- .../modules/create_space/domain/params/create_space_param.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart b/lib/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart index 931588d2..90a82a6b 100644 --- a/lib/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart +++ b/lib/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart @@ -13,7 +13,7 @@ class CreateSpaceParam { Map toJson() { return { - 'spaceModelUuid': parentUuid, + 'parentUuid': parentUuid, ...space.toJson(), 'x': 0, 'y': 0, From c6729f476f808581823dced0235e595c86a9fe89 Mon Sep 17 00:00:00 2001 From: mohammad Date: Wed, 16 Jul 2025 15:36:49 +0300 Subject: [PATCH 069/118] Enhance booking system: update API endpoints, improve event loading with caching, and refine UI components --- .../services/remote_calendar_service.dart | 144 +-------- .../services/calendar_system_service.dart | 2 + .../bloc/calendar/events_bloc.dart | 73 ++++- .../bloc/calendar/events_state.dart | 8 +- .../presentation/view/booking_page.dart | 8 +- .../view/widgets/booking_sidebar.dart | 7 +- .../view/widgets/event_tile_widget.dart | 98 +++++-- .../view/widgets/room_list_item.dart | 4 + .../view/widgets/weekly_calendar_page.dart | 276 +++++++++--------- lib/utils/constants/api_const.dart | 3 +- 10 files changed, 307 insertions(+), 316 deletions(-) diff --git a/lib/pages/access_management/booking_system/data/services/remote_calendar_service.dart b/lib/pages/access_management/booking_system/data/services/remote_calendar_service.dart index aa3307d3..15fb1fd9 100644 --- a/lib/pages/access_management/booking_system/data/services/remote_calendar_service.dart +++ b/lib/pages/access_management/booking_system/data/services/remote_calendar_service.dart @@ -14,146 +14,20 @@ class RemoteCalendarService implements CalendarSystemService { @override Future getCalendarEvents({ required String spaceId, + required String month, + required String year, }) async { + try { - final response = await _httpService.get( - path: ApiEndpoints.getCalendarEvents, - queryParameters: { - 'spaceId': spaceId, - }, + return await _httpService.get( + path: ApiEndpoints.getBookings + .replaceAll('{mm}', month) + .replaceAll('{yyyy}', year) + .replaceAll('{space}', spaceId), expectedResponseModel: (json) { - return CalendarEventsResponse.fromJson( - json as Map, - ); + return CalendarEventsResponse.fromJson(json as Map); }, ); - - return CalendarEventsResponse.fromJson(response as Map); - } on DioException catch (e) { - final responseData = e.response?.data; - if (responseData is Map) { - final errorMessage = responseData['error']?['message'] as String? ?? - responseData['message'] as String? ?? - _defaultErrorMessage; - throw APIException(errorMessage); - } - throw APIException(_defaultErrorMessage); - } catch (e) { - throw APIException('$_defaultErrorMessage: ${e.toString()}'); - } - } -} - -class FakeRemoteCalendarService implements CalendarSystemService { - const FakeRemoteCalendarService(this._httpService, {this.useDummy = false}); - - final HTTPService _httpService; - final bool useDummy; - static const _defaultErrorMessage = 'Failed to load Calendar'; - - @override - Future getCalendarEvents({ - required String spaceId, - }) async { - if (useDummy) { - final dummyJson = { - 'statusCode': 200, - 'message': 'Successfully fetched all bookings', - 'data': [ - { - 'uuid': 'd4553fa6-a0c9-4f42-81c9-99a13a57bf80', - 'date': '2025-07-11T10:22:00.626Z', - 'startTime': '09:00:00', - 'endTime': '12:00:00', - 'cost': 10, - 'user': { - 'uuid': '784394ff-3197-4c39-9f07-48dc44920b1e', - 'firstName': 'salsabeel', - 'lastName': 'abuzaid', - 'email': 'test@test.com', - 'companyName': null - }, - 'space': { - 'uuid': '000f4d81-43e4-4ad7-865c-0f8b04b7081e', - 'spaceName': '2(1)' - } - }, - { - 'uuid': 'e9b27af0-b963-4d98-9657-454c4ba78561', - 'date': '2025-07-11T10:22:00.626Z', - 'startTime': '12:00:00', - 'endTime': '13:00:00', - 'cost': 10, - 'user': { - 'uuid': '784394ff-3197-4c39-9f07-48dc44920b1e', - 'firstName': 'salsabeel', - 'lastName': 'abuzaid', - 'email': 'test@test.com', - 'companyName': null - }, - 'space': { - 'uuid': '000f4d81-43e4-4ad7-865c-0f8b04b7081e', - 'spaceName': '2(1)' - } - }, - { - 'uuid': 'e9b27af0-b963-4d98-9657-454c4ba78561', - 'date': '2025-07-13T10:22:00.626Z', - 'startTime': '15:30:00', - 'endTime': '19:00:00', - 'cost': 20, - 'user': { - 'uuid': '784394ff-3197-4c39-9f07-48dc44920b1e', - 'firstName': 'salsabeel', - 'lastName': 'abuzaid', - 'email': 'test@test.com', - 'companyName': null - }, - 'space': { - 'uuid': '000f4d81-43e4-4ad7-865c-0f8b04b7081e', - 'spaceName': '2(1)' - } - } - ], - 'success': true - }; - final response = CalendarEventsResponse.fromJson(dummyJson); - - // Filter events by spaceId - final filteredData = response.data.where((event) { - return event.space.uuid == spaceId; - }).toList(); - print('Filtering events for spaceId: $spaceId'); - print('Found ${filteredData.length} matching events'); - return filteredData.isNotEmpty - ? CalendarEventsResponse( - statusCode: response.statusCode, - message: response.message, - data: filteredData, - success: response.success, - ) - : CalendarEventsResponse( - statusCode: 404, - message: 'No events found for spaceId: $spaceId', - data: [], - success: false, - ); - } - - try { - final response = await _httpService.get( - path: ApiEndpoints.getCalendarEvents, - queryParameters: { - 'spaceId': spaceId, - }, - expectedResponseModel: (json) { - return CalendarEventsResponse.fromJson( - json as Map, - ); - }, - ); - - return CalendarEventsResponse.fromJson(response as Map); } on DioException catch (e) { final responseData = e.response?.data; if (responseData is Map) { diff --git a/lib/pages/access_management/booking_system/domain/services/calendar_system_service.dart b/lib/pages/access_management/booking_system/domain/services/calendar_system_service.dart index 9e178040..d999ef1a 100644 --- a/lib/pages/access_management/booking_system/domain/services/calendar_system_service.dart +++ b/lib/pages/access_management/booking_system/domain/services/calendar_system_service.dart @@ -3,5 +3,7 @@ import 'package:syncrow_web/pages/access_management/booking_system/domain/models abstract class CalendarSystemService { Future getCalendarEvents({ required String spaceId, + required String month, + required String year, }); } diff --git a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart index da782d74..1985348e 100644 --- a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart +++ b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart @@ -4,35 +4,70 @@ import 'package:calendar_view/calendar_view.dart'; import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart'; - part 'events_event.dart'; part 'events_state.dart'; class CalendarEventsBloc extends Bloc { final EventController eventController = EventController(); final CalendarSystemService calendarService; + final Map> _eventsCache = {}; + + String? _lastSpaceId; + int? _lastMonth; + int? _lastYear; CalendarEventsBloc({required this.calendarService}) : super(EventsInitial()) { on(_onLoadEvents); on(_onAddEvent); - on(_onStartTimer); on(_onDisposeResources); on(_onGoToWeek); } - Future _onLoadEvents( LoadEvents event, Emitter emit, ) async { + final month = event.weekEnd.month; + final year = event.weekEnd.year; + final cacheKey = + '${event.spaceId}-$year-${month.toString().padLeft(2, '0')}'; + + if (_eventsCache.containsKey(cacheKey)) { + final cachedEvents = _eventsCache[cacheKey]!; + eventController.addAll(cachedEvents); + emit(EventsLoaded( + events: cachedEvents, + spaceId: event.spaceId, + month: month, + year: year, + )); + return; + } + if (_lastSpaceId == event.spaceId && + _lastMonth == month && + _lastYear == year) { + return; + } + emit(EventsLoading()); try { final response = await calendarService.getCalendarEvents( + month: month.toString().padLeft(2, '0'), + year: year.toString(), spaceId: event.spaceId, ); - final events = - response.data.map(_toCalendarEventData).toList(); + + final events = response.data.map(_toCalendarEventData).toList(); + _eventsCache[cacheKey] = events; eventController.addAll(events); - emit(EventsLoaded(events: events)); + _lastSpaceId = event.spaceId; + _lastMonth = month; + _lastYear = year; + emit(EventsLoaded( + events: events, + spaceId: event.spaceId, + month: month, + year: year, + )); } catch (e) { emit(EventsError('Failed to load events')); } @@ -40,16 +75,28 @@ class CalendarEventsBloc extends Bloc { void _onAddEvent(AddEvent event, Emitter emit) { eventController.add(event.event); + if (state is EventsLoaded) { final loaded = state as EventsLoaded; + final cacheKey = + '${loaded.spaceId}-${loaded.year}-${loaded.month.toString().padLeft(2, '0')}'; + + if (_eventsCache.containsKey(cacheKey)) { + final cachedEvents = + List.from(_eventsCache[cacheKey]!); + cachedEvents.add(event.event); + _eventsCache[cacheKey] = cachedEvents; + } + emit(EventsLoaded( events: [...eventController.events], + spaceId: loaded.spaceId, + month: loaded.month, + year: loaded.year, )); } } - void _onStartTimer(StartTimer event, Emitter emit) {} - void _onDisposeResources( DisposeResources event, Emitter emit) { eventController.dispose(); @@ -61,6 +108,9 @@ class CalendarEventsBloc extends Bloc { final newWeekDays = _getWeekDays(event.weekDate); emit(EventsLoaded( events: loaded.events, + spaceId: loaded.spaceId, + month: loaded.month, + year: loaded.year, )); } } @@ -90,14 +140,13 @@ class CalendarEventsBloc extends Bloc { ); return CalendarEventData( - date: startTime, + date: startTime, startTime: startTime, endTime: endTime, - title: - '${booking.space.spaceName} - ${booking.user.firstName} ${booking.user.lastName}', + title: '${booking.user.firstName} ${booking.user.lastName}', description: 'Cost: ${booking.cost}', color: Colors.blue, - event: booking, + event: booking, ); } diff --git a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_state.dart b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_state.dart index bc0c2e31..b98fd2fb 100644 --- a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_state.dart +++ b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_state.dart @@ -7,11 +7,17 @@ class EventsInitial extends CalendarEventState {} class EventsLoading extends CalendarEventState {} -class EventsLoaded extends CalendarEventState { +final class EventsLoaded extends CalendarEventState { final List events; + final String spaceId; + final int month; + final int year; EventsLoaded({ required this.events, + required this.spaceId, + required this.month, + required this.year, }); } diff --git a/lib/pages/access_management/booking_system/presentation/view/booking_page.dart b/lib/pages/access_management/booking_system/presentation/view/booking_page.dart index 0ff9aaf6..8d04f932 100644 --- a/lib/pages/access_management/booking_system/presentation/view/booking_page.dart +++ b/lib/pages/access_management/booking_system/presentation/view/booking_page.dart @@ -62,8 +62,9 @@ class _BookingPageState extends State { BlocProvider(create: (_) => DateSelectionBloc()), BlocProvider( create: (_) => CalendarEventsBloc( - calendarService: - FakeRemoteCalendarService(HTTPService(), useDummy: true), + calendarService: RemoteCalendarService( + HTTPService(), + ), ), ), ], @@ -138,7 +139,7 @@ class _BookingPageState extends State { ), ), Expanded( - flex: 4, + flex: 5, child: Padding( padding: const EdgeInsets.all(20.0), child: Column( @@ -187,6 +188,7 @@ class _BookingPageState extends State { ], ), Expanded( + flex: 5, child: BlocBuilder( builder: (context, roomState) { diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart index e3d84924..666df3bb 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart @@ -72,11 +72,7 @@ class __SidebarContentState extends State<_SidebarContent> { @override Widget build(BuildContext context) { return BlocConsumer( - listener: (context, state) { - if (state.currentPage == 1 && searchController.text.isNotEmpty) { - searchController.clear(); - } - }, + listener: (context, state) {}, builder: (context, state) { return Column( children: [ @@ -147,6 +143,7 @@ class __SidebarContentState extends State<_SidebarContent> { IconButton( icon: const Icon(Icons.close), onPressed: () { + searchController.clear(); context.read().add(ResetSearch()); }, ), diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/event_tile_widget.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/event_tile_widget.dart index 6c0f9cb2..b7e942d6 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/event_tile_widget.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/event_tile_widget.dart @@ -1,16 +1,15 @@ import 'package:calendar_view/calendar_view.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class EventTileWidget extends StatelessWidget { final List> events; - const EventTileWidget({ super.key, required this.events, }); - @override Widget build(BuildContext context) { return Container( @@ -18,39 +17,86 @@ class EventTileWidget extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: events.map((event) { - final bool isEventEnded = + final booking = event.event is CalendarEventBooking + ? event.event! as CalendarEventBooking + : null; + + final companyName = booking?.user.companyName ?? 'Unknown Company'; + final startTime = DateFormat('hh:mm a').format(event.startTime!); + final endTime = DateFormat('hh:mm a').format(event.endTime!); + final isEventEnded = event.endTime != null && event.endTime!.isBefore(DateTime.now()); + + final duration = event.endTime!.difference(event.startTime!); + final bool isLongEnough = duration.inMinutes >= 31; return Expanded( child: Container( width: double.infinity, - padding: const EdgeInsets.all(6), + padding: EdgeInsets.all(5), decoration: BoxDecoration( color: isEventEnded - ? ColorsManager.lightGrayBorderColor - : ColorsManager.blue1.withOpacity(0.25), + ? ColorsManager.grayColor.withOpacity(0.1) + : ColorsManager.blue1.withOpacity(0.1), borderRadius: BorderRadius.circular(6), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - DateFormat('h:mm a').format(event.startTime!), - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 12, - color: Colors.black87, - ), + border: const Border( + left: BorderSide( + color: ColorsManager.grayColor, + width: 4, ), - const SizedBox(height: 2), - Text( - event.title, - style: const TextStyle( - fontSize: 12, - color: ColorsManager.blackColor, - ), - ), - ], + ), ), + child: isLongEnough + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '$startTime - $endTime', + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 12, + color: isEventEnded + ? ColorsManager.grayColor.withOpacity(0.9) + : ColorsManager.blackColor, + fontWeight: FontWeight.w400, + ), + ), + const SizedBox(height: 2), + Text( + event.title, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 14, + color: isEventEnded + ? ColorsManager.grayColor + : ColorsManager.blackColor, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 2), + Text( + companyName, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 14, + color: isEventEnded + ? ColorsManager.grayColor.withOpacity(0.9) + : ColorsManager.blackColor, + fontWeight: FontWeight.w400, + ), + ), + ], + ) + : Text( + event.title, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 14, + color: isEventEnded + ? ColorsManager.grayColor + : ColorsManager.blackColor, + fontWeight: FontWeight.bold, + ), + ), ), ); }).toList(), diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/room_list_item.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/room_list_item.dart index 4a4b608d..83eda16b 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/room_list_item.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/room_list_item.dart @@ -24,17 +24,21 @@ class RoomListItem extends StatelessWidget { activeColor: ColorsManager.primaryColor, title: Text( room.spaceName, + maxLines: 2, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: ColorsManager.lightGrayColor, fontWeight: FontWeight.w700, + overflow: TextOverflow.ellipsis, fontSize: 12), ), subtitle: Text( room.virtualLocation, + maxLines: 2, style: Theme.of(context).textTheme.bodySmall?.copyWith( fontSize: 10, fontWeight: FontWeight.w400, color: ColorsManager.textGray, + overflow: TextOverflow.ellipsis, ), ), ); diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/weekly_calendar_page.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/weekly_calendar_page.dart index 0dd343a7..03972632 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/weekly_calendar_page.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/weekly_calendar_page.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:calendar_view/calendar_view.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/event_tile_widget.dart'; -import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/hatched_column_background.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/time_line_widget.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/week_day_header.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -23,6 +22,12 @@ class WeeklyCalendarPage extends StatelessWidget { this.endTime, this.selectedDateFromSideBarCalender, }); + static const double timeLineWidth = 65; + static const int totalDays = 7; + static const double dayColumnWidth = 220; // or any width you want + + final double calendarContentWidth = + timeLineWidth + (totalDays * dayColumnWidth); @override Widget build(BuildContext context) { @@ -52,154 +57,159 @@ class WeeklyCalendarPage extends StatelessWidget { ); } - final weekDays = _getWeekDays(weekStart); + - final selectedDayIndex = - weekDays.indexWhere((d) => isSameDay(d, selectedDate)); - final selectedSidebarIndex = selectedDateFromSideBarCalender == null - ? -1 - : weekDays - .indexWhere((d) => isSameDay(d, selectedDateFromSideBarCalender!)); + const double timeLineWidth = 65; - const double timeLineWidth = 80; - const int totalDays = 7; - final DateTime highlightStart = DateTime(2025, 7, 10); - final DateTime highlightEnd = DateTime(2025, 7, 19); return LayoutBuilder( builder: (context, constraints) { - final double calendarWidth = constraints.maxWidth; - final double dayColumnWidth = - (calendarWidth - timeLineWidth) / totalDays - 0.1; + bool isInRange(DateTime date, DateTime start, DateTime end) { - return !date.isBefore(start) && !date.isAfter(end); + !date.isBefore(start) && !date.isAfter(end); + // remove this line and Check if the date is within the range + return false; } - return Padding( - padding: const EdgeInsets.only(left: 25.0, right: 25.0, top: 25), - child: Stack( - children: [ - WeekView( - weekDetectorBuilder: ({ - required date, - required height, - required heightPerMinute, - required minuteSlotSize, - required width, - }) { - return isInRange(date, highlightStart, highlightEnd) - ? HatchedColumnBackground( - backgroundColor: ColorsManager.grey800, - lineColor: ColorsManager.textGray, - opacity: 0.3, - stripeSpacing: 12, - borderRadius: BorderRadius.circular(8), - ) - : const SizedBox(); - }, - pageViewPhysics: const NeverScrollableScrollPhysics(), - key: ValueKey(weekStart), - controller: eventController, - initialDay: weekStart, - startHour: startHour - 1, - endHour: endHour, - heightPerMinute: 1.1, - showLiveTimeLineInAllDays: false, - showVerticalLines: true, - emulateVerticalOffsetBy: -80, - startDay: WeekDays.monday, - liveTimeIndicatorSettings: const LiveTimeIndicatorSettings( - showBullet: false, - height: 0, - ), - weekDayBuilder: (date) { - return WeekDayHeader( - date: date, - isSelectedDay: isSameDay(date, selectedDate), - ); - }, - timeLineBuilder: (date) { - return TimeLineWidget(date: date); - }, - timeLineWidth: timeLineWidth, - weekPageHeaderBuilder: (start, end) => Container(), - weekTitleHeight: 60, - weekNumberBuilder: (firstDayOfWeek) => Padding( - padding: const EdgeInsets.only(right: 15, bottom: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text( - firstDayOfWeek.timeZoneName.replaceAll(':00', ''), - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontSize: 12, - color: ColorsManager.blackColor, - fontWeight: FontWeight.w400, - ), + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SizedBox( + width: calendarContentWidth, + child: Padding( + padding: + const EdgeInsets.only(left: 25.0, right: 25.0, top: 25), + child: Stack( + children: [ + Container( + child: WeekView( + minuteSlotSize: MinuteSlotSize.minutes15, + weekDetectorBuilder: ({ + required date, + required height, + required heightPerMinute, + required minuteSlotSize, + required width, + }) { + final isSelected = isSameDay(date, selectedDate); + final isSidebarSelected = + selectedDateFromSideBarCalender != null && + isSameDay( + date, selectedDateFromSideBarCalender!); + if (isSidebarSelected && !isSelected) { + return Container( + height: height, + width: width, + decoration: BoxDecoration( + color: Colors.orange.withOpacity(0.13), + borderRadius: BorderRadius.circular(8), + ), + ); + } else if (isSelected) { + return Container( + height: height, + width: width, + decoration: BoxDecoration( + color: + ColorsManager.spaceColor.withOpacity(0.07), + borderRadius: BorderRadius.circular(8), + ), + ); + } + return const SizedBox.shrink(); + }, + + // weekDetectorBuilder: ({ + // required date, + // required height, + // required heightPerMinute, + // required minuteSlotSize, + // required width, + // }) { + // return isInRange(date, highlightStart, highlightEnd) + // ? HatchedColumnBackground( + // backgroundColor: ColorsManager.grey800, + // lineColor: ColorsManager.textGray, + // opacity: 0.3, + // stripeSpacing: 12, + // borderRadius: BorderRadius.circular(8), + // ) + // : const SizedBox(); + // }, + pageViewPhysics: const NeverScrollableScrollPhysics(), + key: ValueKey(weekStart), + controller: eventController, + initialDay: weekStart, + startHour: startHour - 1, + endHour: endHour, + heightPerMinute: 1.7, + showLiveTimeLineInAllDays: false, + showVerticalLines: true, + emulateVerticalOffsetBy: -80, + startDay: WeekDays.monday, + liveTimeIndicatorSettings: + const LiveTimeIndicatorSettings( + showBullet: false, + height: 0, + ), + weekDayBuilder: (date) { + return WeekDayHeader( + date: date, + isSelectedDay: isSameDay(date, selectedDate), + ); + }, + timeLineBuilder: (date) { + return TimeLineWidget(date: date); + }, + timeLineWidth: timeLineWidth, + weekPageHeaderBuilder: (start, end) => Container(), + weekTitleHeight: 60, + weekNumberBuilder: (firstDayOfWeek) => Padding( + padding: const EdgeInsets.only(right: 15, bottom: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + firstDayOfWeek.timeZoneName + .replaceAll(':00', ''), + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith( + fontSize: 12, + color: ColorsManager.blackColor, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + eventTileBuilder: (date, events, boundary, start, end) { + return EventTileWidget( + events: events, + ); + }, ), - ], - ), - ), - eventTileBuilder: (date, events, boundary, start, end) { - return EventTileWidget( - events: events, - ); - }, - ), - if (selectedDayIndex >= 0) - Positioned( - left: (timeLineWidth + 3) + - (dayColumnWidth - 8) * (selectedDayIndex - 0.01), - top: 0, - bottom: 0, - width: dayColumnWidth, - child: IgnorePointer( - child: Container( - margin: const EdgeInsets.symmetric( - vertical: 0, horizontal: 4), - color: ColorsManager.spaceColor.withOpacity(0.07), ), - ), - ), - if (selectedSidebarIndex >= 0 && - selectedSidebarIndex != selectedDayIndex) - Positioned( - left: (timeLineWidth + 3) + - (dayColumnWidth - 8) * (selectedSidebarIndex - 0.01), - top: 0, - bottom: 0, - width: dayColumnWidth, - child: IgnorePointer( - child: Container( - margin: const EdgeInsets.symmetric( - vertical: 0, horizontal: 4), - color: Colors.orange.withOpacity(0.14), + Positioned( + right: 0, + top: 50, + bottom: 0, + child: IgnorePointer( + child: Container( + width: 1, + color: Theme.of(context).scaffoldBackgroundColor, + ), + ), ), - ), - ), - Positioned( - right: 0, - top: 50, - bottom: 0, - child: IgnorePointer( - child: Container( - width: 1, - color: Theme.of(context).scaffoldBackgroundColor, - ), + ], ), ), - ], - ), - ); + )); }, ); } - List _getWeekDays(DateTime date) { - final int weekday = date.weekday; - final DateTime monday = date.subtract(Duration(days: weekday - 1)); - return List.generate(7, (i) => monday.add(Duration(days: i))); - } + } bool isSameDay(DateTime d1, DateTime d2) { diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index b3c8a168..e99f4796 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -140,5 +140,6 @@ abstract class ApiEndpoints { static const String saveSchedule = '/schedule/{deviceUuid}'; static const String getBookableSpaces = '/bookable-spaces'; - static const String getCalendarEvents = '/api'; + static const String getBookings = + '/bookings?month={mm}%2F{yyyy}&space={space}'; } From 8522c0bbc35bf123941faea4f6a5450927f6207f Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 16 Jul 2025 16:26:15 +0300 Subject: [PATCH 070/118] Made `SpacesRecursiveHelper` private, to avoid creating unnecessary instances, since all its methods are static. --- .../main_module/helpers/spaces_recursive_helper.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart b/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart index 4322c4e8..c470ffc1 100644 --- a/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart +++ b/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart @@ -2,6 +2,8 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; abstract final class SpacesRecursiveHelper { + const SpacesRecursiveHelper._(); + static List recusrivelyUpdate( List spaces, SpaceDetailsModel updatedSpace, From 7b5b40a03cda611236defe5a4ad67ebae5ba2bab Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 16 Jul 2025 16:30:38 +0300 Subject: [PATCH 071/118] Refactor `recursivelyInsert` method in `SpacesRecursiveHelper` to use named parameters. Update `CommunityStructureCanvas` to reflect these changes, ensuring correct space insertion under the specified parent. --- .../helpers/spaces_recursive_helper.dart | 22 ++++++++++++------- .../widgets/community_structure_canvas.dart | 8 +++---- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart b/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart index c470ffc1..de5ce34f 100644 --- a/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart +++ b/lib/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart @@ -43,20 +43,26 @@ abstract final class SpacesRecursiveHelper { return nonNullSpaces; } - static List recusrivelyInsert( - List spaces, - SpaceModel newSpace, - String parentUuid, - ) { + static List recursivelyInsert({ + required List spaces, + required String parentUuid, + required SpaceModel newSpace, + }) { return spaces.map((space) { - if (space.uuid == parentUuid) { + final isParentSpace = space.uuid == parentUuid; + if (isParentSpace) { return space.copyWith( children: [...space.children, newSpace], ); } - if (space.children.isNotEmpty) { + final hasChildren = space.children.isNotEmpty; + if (hasChildren) { return space.copyWith( - children: recusrivelyInsert(space.children, newSpace, parentUuid), + children: recursivelyInsert( + spaces: space.children, + parentUuid: parentUuid, + newSpace: newSpace, + ), ); } return space; 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 03efab7d..692ffc0a 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 @@ -330,10 +330,10 @@ class _CommunityStructureCanvasState extends State communityUuid: widget.community.uuid, parentUuid: space.uuid, onSuccess: (updatedSpaceModel) { - final updatedSpaces = SpacesRecursiveHelper.recusrivelyInsert( - spaces, - updatedSpaceModel, - space.uuid, + final updatedSpaces = SpacesRecursiveHelper.recursivelyInsert( + spaces: widget.community.spaces, + parentUuid: space.uuid, + newSpace: updatedSpaceModel, ); context.read().add( CommunitiesUpdateCommunity( From c9b8fbb0c269210ce78c344511e92702e5b86048 Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 17 Jul 2025 11:20:32 +0300 Subject: [PATCH 072/118] Refactor calendar event loading: replace parameters with LoadEventsParam class and implement memory caching for improved performance --- .../services/remote_calendar_service.dart | 9 ++-- .../domain/LoadEventsParam.dart | 28 ++++++++++++ .../services/calendar_system_service.dart | 5 +-- .../bloc/calendar/events_bloc.dart | 45 ++++++++----------- .../bloc/calendar/events_event.dart | 12 ++--- .../model/memory_bookable_space_service.dart | 35 +++++++++++++++ .../presentation/view/booking_page.dart | 11 +++-- .../view/widgets/weekly_calendar_page.dart | 2 +- 8 files changed, 100 insertions(+), 47 deletions(-) create mode 100644 lib/pages/access_management/booking_system/domain/LoadEventsParam.dart create mode 100644 lib/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart diff --git a/lib/pages/access_management/booking_system/data/services/remote_calendar_service.dart b/lib/pages/access_management/booking_system/data/services/remote_calendar_service.dart index 15fb1fd9..55a5b0b8 100644 --- a/lib/pages/access_management/booking_system/data/services/remote_calendar_service.dart +++ b/lib/pages/access_management/booking_system/data/services/remote_calendar_service.dart @@ -1,4 +1,5 @@ import 'package:dio/dio.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart'; import 'package:syncrow_web/services/api/api_exception.dart'; @@ -13,17 +14,17 @@ class RemoteCalendarService implements CalendarSystemService { @override Future getCalendarEvents({ - required String spaceId, - required String month, - required String year, + required LoadEventsParam params, }) async { + final month = params.startDate.month.toString().padLeft(2, '0'); + final year = params.startDate.year.toString(); try { return await _httpService.get( path: ApiEndpoints.getBookings .replaceAll('{mm}', month) .replaceAll('{yyyy}', year) - .replaceAll('{space}', spaceId), + .replaceAll('{space}', params.id), expectedResponseModel: (json) { return CalendarEventsResponse.fromJson(json as Map); }, diff --git a/lib/pages/access_management/booking_system/domain/LoadEventsParam.dart b/lib/pages/access_management/booking_system/domain/LoadEventsParam.dart new file mode 100644 index 00000000..f5088b6e --- /dev/null +++ b/lib/pages/access_management/booking_system/domain/LoadEventsParam.dart @@ -0,0 +1,28 @@ +import 'package:equatable/equatable.dart'; + +class LoadEventsParam extends Equatable { + final DateTime startDate; + final DateTime endDate; + final String id; + + const LoadEventsParam({ + required this.startDate, + required this.endDate, + required this.id, + }); + + @override + List get props => [startDate, endDate, id]; + + LoadEventsParam copyWith({ + DateTime? startDate, + DateTime? endDate, + String? id, + }) { + return LoadEventsParam( + startDate: startDate ?? this.startDate, + endDate: endDate ?? this.endDate, + id: id ?? this.id, + ); + } +} diff --git a/lib/pages/access_management/booking_system/domain/services/calendar_system_service.dart b/lib/pages/access_management/booking_system/domain/services/calendar_system_service.dart index d999ef1a..3522054c 100644 --- a/lib/pages/access_management/booking_system/domain/services/calendar_system_service.dart +++ b/lib/pages/access_management/booking_system/domain/services/calendar_system_service.dart @@ -1,9 +1,8 @@ +import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart'; abstract class CalendarSystemService { Future getCalendarEvents({ - required String spaceId, - required String month, - required String year, + required LoadEventsParam params, }); } diff --git a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart index 1985348e..3945e5f5 100644 --- a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart +++ b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart @@ -2,8 +2,10 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:calendar_view/calendar_view.dart'; import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart'; part 'events_event.dart'; part 'events_state.dart'; @@ -11,12 +13,14 @@ class CalendarEventsBloc extends Bloc { final EventController eventController = EventController(); final CalendarSystemService calendarService; final Map> _eventsCache = {}; + final MemoryBookableSpaceService memoryService; - String? _lastSpaceId; - int? _lastMonth; - int? _lastYear; - CalendarEventsBloc({required this.calendarService}) : super(EventsInitial()) { + + CalendarEventsBloc({ + required this.calendarService, + required this.memoryService, + }) : super(EventsInitial()) { on(_onLoadEvents); on(_onAddEvent); on(_onDisposeResources); @@ -26,45 +30,32 @@ class CalendarEventsBloc extends Bloc { LoadEvents event, Emitter emit, ) async { - final month = event.weekEnd.month; - final year = event.weekEnd.year; - final cacheKey = - '${event.spaceId}-$year-${month.toString().padLeft(2, '0')}'; - - if (_eventsCache.containsKey(cacheKey)) { - final cachedEvents = _eventsCache[cacheKey]!; + final param = event.param; + final month = param.endDate.month; + final year = param.endDate.year; + final spaceId = param.id; + final cachedEvents = memoryService.getEvents(spaceId, year, month); + if (cachedEvents != null) { eventController.addAll(cachedEvents); emit(EventsLoaded( events: cachedEvents, - spaceId: event.spaceId, + spaceId: spaceId, month: month, year: year, )); return; } - if (_lastSpaceId == event.spaceId && - _lastMonth == month && - _lastYear == year) { - return; - } emit(EventsLoading()); try { - final response = await calendarService.getCalendarEvents( - month: month.toString().padLeft(2, '0'), - year: year.toString(), - spaceId: event.spaceId, - ); + final response = await calendarService.getCalendarEvents(params: param); final events = response.data.map(_toCalendarEventData).toList(); - _eventsCache[cacheKey] = events; + memoryService.setEvents(spaceId, year, month, events); eventController.addAll(events); - _lastSpaceId = event.spaceId; - _lastMonth = month; - _lastYear = year; emit(EventsLoaded( events: events, - spaceId: event.spaceId, + spaceId: spaceId, month: month, year: year, )); diff --git a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_event.dart b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_event.dart index 4f4cafcf..6a368e17 100644 --- a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_event.dart +++ b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_event.dart @@ -6,17 +6,11 @@ abstract class CalendarEventsEvent { } class LoadEvents extends CalendarEventsEvent { - final String spaceId; - final DateTime weekStart; - final DateTime weekEnd; - - const LoadEvents({ - required this.spaceId, - required this.weekStart, - required this.weekEnd, - }); + final LoadEventsParam param; + const LoadEvents(this.param); } + class AddEvent extends CalendarEventsEvent { final CalendarEventData event; const AddEvent(this.event); diff --git a/lib/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart b/lib/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart new file mode 100644 index 00000000..c9017d90 --- /dev/null +++ b/lib/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart @@ -0,0 +1,35 @@ +import 'package:calendar_view/calendar_view.dart'; + +class MemoryBookableSpaceService { + final Map> _eventsCache = {}; + + List? getEvents(String spaceId, int year, int month) { + final key = _generateKey(spaceId, year, month); + return _eventsCache[key]; + } + + void setEvents( + String spaceId, + int year, + int month, + List events, + ) { + final key = _generateKey(spaceId, year, month); + _eventsCache[key] = events; + } + + void addEvent(String spaceId, int year, int month, CalendarEventData event) { + final key = _generateKey(spaceId, year, month); + final events = _eventsCache[key] ?? []; + events.add(event); + _eventsCache[key] = events; + } + + void clear() { + _eventsCache.clear(); + } + + String _generateKey(String spaceId, int year, int month) { + return '$spaceId-$year-${month.toString().padLeft(2, '0')}'; + } +} \ No newline at end of file diff --git a/lib/pages/access_management/booking_system/presentation/view/booking_page.dart b/lib/pages/access_management/booking_system/presentation/view/booking_page.dart index 8d04f932..1030462d 100644 --- a/lib/pages/access_management/booking_system/presentation/view/booking_page.dart +++ b/lib/pages/access_management/booking_system/presentation/view/booking_page.dart @@ -2,11 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:calendar_view/calendar_view.dart'; import 'package:syncrow_web/pages/access_management/booking_system/data/services/remote_calendar_service.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_bloc.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_event.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_state.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/selected_bookable_space_bloc/selected_bookable_space_bloc.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/custom_calendar_page.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart'; @@ -46,9 +48,11 @@ class _BookingPageState extends State { if (selectedRoom != null) { context.read().add( LoadEvents( - spaceId: selectedRoom.uuid, - weekStart: dateState.weekStart, - weekEnd: dateState.weekStart.add(const Duration(days: 6)), + LoadEventsParam( + startDate: dateState.weekStart, + endDate: dateState.weekStart.add(const Duration(days: 6)), + id: selectedRoom.uuid, + ), ), ); } @@ -62,6 +66,7 @@ class _BookingPageState extends State { BlocProvider(create: (_) => DateSelectionBloc()), BlocProvider( create: (_) => CalendarEventsBloc( + memoryService: MemoryBookableSpaceService(), calendarService: RemoteCalendarService( HTTPService(), ), diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/weekly_calendar_page.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/weekly_calendar_page.dart index 03972632..2bfd5429 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/weekly_calendar_page.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/weekly_calendar_page.dart @@ -24,7 +24,7 @@ class WeeklyCalendarPage extends StatelessWidget { }); static const double timeLineWidth = 65; static const int totalDays = 7; - static const double dayColumnWidth = 220; // or any width you want + static const double dayColumnWidth = 220; final double calendarContentWidth = timeLineWidth + (totalDays * dayColumnWidth); From fe2f4a872ba6535d02f037d95888df17bc115e3e Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 17 Jul 2025 12:38:58 +0300 Subject: [PATCH 073/118] Refactor memory event handling: replace MemoryBookableSpaceService with a new implementation and integrate caching logic in CalendarEventsBloc --- .../memory_bookable_space_service.dart | 63 +++++++++++++++++++ .../domain/LoadEventsParam.dart | 6 ++ .../bloc/calendar/events_bloc.dart | 28 +-------- .../model/memory_bookable_space_service.dart | 35 ----------- .../presentation/view/booking_page.dart | 17 ++--- 5 files changed, 79 insertions(+), 70 deletions(-) create mode 100644 lib/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart delete mode 100644 lib/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart diff --git a/lib/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart b/lib/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart new file mode 100644 index 00000000..7aec2398 --- /dev/null +++ b/lib/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart @@ -0,0 +1,63 @@ +import 'package:syncrow_web/pages/access_management/booking_system/data/services/remote_calendar_service.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart'; + +class MemoryBookableSpaceService implements CalendarSystemService { + final Map _eventsCache = {}; + + @override + Future getCalendarEvents({ + required LoadEventsParam params, + }) async { + final key = params.generateKey(); + + return _eventsCache[key]!; + } + + void setEvents( + LoadEventsParam param, + CalendarEventsResponse events, + ) { + final key = param.generateKey(); + _eventsCache[key] = events; + } + + void addEvent(LoadEventsParam param, CalendarEventsResponse event) { + final key = param.generateKey(); + + _eventsCache[key] = event; + } + + void clear() { + _eventsCache.clear(); + } +} + +class MemoryCalendarServiceWithRemoteFallback implements CalendarSystemService { + final MemoryBookableSpaceService memoryService; + final RemoteCalendarService remoteService; + + MemoryCalendarServiceWithRemoteFallback({ + required this.memoryService, + required this.remoteService, + }); + + @override + Future getCalendarEvents({ + required LoadEventsParam params, + }) async { + final key = params.generateKey(); + final doesExistInMemory = memoryService._eventsCache.containsKey(key); + + if (doesExistInMemory) { + return memoryService.getCalendarEvents(params: params); + } else { + final remoteResult = + await remoteService.getCalendarEvents(params: params); + memoryService.setEvents(params, remoteResult); + + return remoteResult; + } + } +} diff --git a/lib/pages/access_management/booking_system/domain/LoadEventsParam.dart b/lib/pages/access_management/booking_system/domain/LoadEventsParam.dart index f5088b6e..542dd5dc 100644 --- a/lib/pages/access_management/booking_system/domain/LoadEventsParam.dart +++ b/lib/pages/access_management/booking_system/domain/LoadEventsParam.dart @@ -26,3 +26,9 @@ class LoadEventsParam extends Equatable { ); } } + +extension KeyGenerator on LoadEventsParam { + String generateKey() { + return '$id-${startDate.year}-${startDate.month.toString().padLeft(2, '0')}'; + } +} \ No newline at end of file diff --git a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart index 3945e5f5..b42947bd 100644 --- a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart +++ b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart @@ -5,21 +5,16 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart'; -import 'package:syncrow_web/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart'; part 'events_event.dart'; part 'events_state.dart'; class CalendarEventsBloc extends Bloc { final EventController eventController = EventController(); final CalendarSystemService calendarService; - final Map> _eventsCache = {}; - final MemoryBookableSpaceService memoryService; - - CalendarEventsBloc({ required this.calendarService, - required this.memoryService, }) : super(EventsInitial()) { on(_onLoadEvents); on(_onAddEvent); @@ -34,24 +29,12 @@ class CalendarEventsBloc extends Bloc { final month = param.endDate.month; final year = param.endDate.year; final spaceId = param.id; - final cachedEvents = memoryService.getEvents(spaceId, year, month); - if (cachedEvents != null) { - eventController.addAll(cachedEvents); - emit(EventsLoaded( - events: cachedEvents, - spaceId: spaceId, - month: month, - year: year, - )); - return; - } emit(EventsLoading()); try { final response = await calendarService.getCalendarEvents(params: param); final events = response.data.map(_toCalendarEventData).toList(); - memoryService.setEvents(spaceId, year, month, events); eventController.addAll(events); emit(EventsLoaded( events: events, @@ -69,15 +52,6 @@ class CalendarEventsBloc extends Bloc { if (state is EventsLoaded) { final loaded = state as EventsLoaded; - final cacheKey = - '${loaded.spaceId}-${loaded.year}-${loaded.month.toString().padLeft(2, '0')}'; - - if (_eventsCache.containsKey(cacheKey)) { - final cachedEvents = - List.from(_eventsCache[cacheKey]!); - cachedEvents.add(event.event); - _eventsCache[cacheKey] = cachedEvents; - } emit(EventsLoaded( events: [...eventController.events], diff --git a/lib/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart b/lib/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart deleted file mode 100644 index c9017d90..00000000 --- a/lib/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:calendar_view/calendar_view.dart'; - -class MemoryBookableSpaceService { - final Map> _eventsCache = {}; - - List? getEvents(String spaceId, int year, int month) { - final key = _generateKey(spaceId, year, month); - return _eventsCache[key]; - } - - void setEvents( - String spaceId, - int year, - int month, - List events, - ) { - final key = _generateKey(spaceId, year, month); - _eventsCache[key] = events; - } - - void addEvent(String spaceId, int year, int month, CalendarEventData event) { - final key = _generateKey(spaceId, year, month); - final events = _eventsCache[key] ?? []; - events.add(event); - _eventsCache[key] = events; - } - - void clear() { - _eventsCache.clear(); - } - - String _generateKey(String spaceId, int year, int month) { - return '$spaceId-$year-${month.toString().padLeft(2, '0')}'; - } -} \ No newline at end of file diff --git a/lib/pages/access_management/booking_system/presentation/view/booking_page.dart b/lib/pages/access_management/booking_system/presentation/view/booking_page.dart index 1030462d..b1e1ebfe 100644 --- a/lib/pages/access_management/booking_system/presentation/view/booking_page.dart +++ b/lib/pages/access_management/booking_system/presentation/view/booking_page.dart @@ -8,7 +8,7 @@ import 'package:syncrow_web/pages/access_management/booking_system/presentation/ import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_event.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_state.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/selected_bookable_space_bloc/selected_bookable_space_bloc.dart'; -import 'package:syncrow_web/pages/access_management/booking_system/presentation/model/memory_bookable_space_service.dart'; +import 'package:syncrow_web/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/custom_calendar_page.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart'; @@ -65,13 +65,14 @@ class _BookingPageState extends State { BlocProvider(create: (_) => SelectedBookableSpaceBloc()), BlocProvider(create: (_) => DateSelectionBloc()), BlocProvider( - create: (_) => CalendarEventsBloc( - memoryService: MemoryBookableSpaceService(), - calendarService: RemoteCalendarService( - HTTPService(), - ), - ), - ), + create: (_) => CalendarEventsBloc( + calendarService: MemoryCalendarServiceWithRemoteFallback( + remoteService: RemoteCalendarService( + HTTPService(), + ), + memoryService: MemoryBookableSpaceService(), + ), + )), ], child: Builder( builder: (context) => From 7876af9756f7da82266d6bf1b36731a6fdbf5b2a Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 17 Jul 2025 12:46:07 +0300 Subject: [PATCH 074/118] Refactor memory service implementation: rename MemoryBookableSpaceService to MemoryCalendarService for clarity and consistency --- .../data/services/memory_bookable_space_service.dart | 4 ++-- .../booking_system/presentation/view/booking_page.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart b/lib/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart index 7aec2398..034480ec 100644 --- a/lib/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart +++ b/lib/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart @@ -3,7 +3,7 @@ import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEv import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart'; import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart'; -class MemoryBookableSpaceService implements CalendarSystemService { +class MemoryCalendarService implements CalendarSystemService { final Map _eventsCache = {}; @override @@ -35,7 +35,7 @@ class MemoryBookableSpaceService implements CalendarSystemService { } class MemoryCalendarServiceWithRemoteFallback implements CalendarSystemService { - final MemoryBookableSpaceService memoryService; + final MemoryCalendarService memoryService; final RemoteCalendarService remoteService; MemoryCalendarServiceWithRemoteFallback({ diff --git a/lib/pages/access_management/booking_system/presentation/view/booking_page.dart b/lib/pages/access_management/booking_system/presentation/view/booking_page.dart index b1e1ebfe..aac5c5b7 100644 --- a/lib/pages/access_management/booking_system/presentation/view/booking_page.dart +++ b/lib/pages/access_management/booking_system/presentation/view/booking_page.dart @@ -70,7 +70,7 @@ class _BookingPageState extends State { remoteService: RemoteCalendarService( HTTPService(), ), - memoryService: MemoryBookableSpaceService(), + memoryService: MemoryCalendarService(), ), )), ], From 06f00da02c67e366c126af502dc54d99be5fb414 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 17 Jul 2025 13:00:38 +0300 Subject: [PATCH 075/118] Update shadow properties in CommunityStructureHeader for improved visual aesthetics. Adjusted blur radius and offset to enhance the header's appearance in the space management interface. --- .../main_module/widgets/community_structure_header.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart b/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart index 432b3ce4..2e1a350e 100644 --- a/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart +++ b/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart @@ -20,9 +20,9 @@ class CommunityStructureHeader extends StatelessWidget { color: ColorsManager.whiteColors, boxShadow: [ BoxShadow( - color: ColorsManager.shadowBlackColor, - blurRadius: 8, - offset: const Offset(0, 4), + color: ColorsManager.shadowBlackColor.withValues(alpha: 0.1), + blurRadius: 20, + offset: const Offset(0, 1), ), ], ), From 0e4d37ccac4e30272cf2513a3865e1d85e12342f Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 17 Jul 2025 14:50:29 +0300 Subject: [PATCH 076/118] 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 077/118] 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 078/118] 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 076c80fe4470c98d635742da670ff95e59df5702 Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 17 Jul 2025 17:02:23 +0300 Subject: [PATCH 079/118] Enhance garage door scheduling functionality and UI improvements --- .../garage_door/view/garage_door_control_view.dart | 12 ++++++++++-- .../schedule_device/bloc/schedule_bloc.dart | 3 ++- .../schedule_widgets/count_down_button.dart | 2 +- .../schedule_widgets/inching_mode_buttons.dart | 2 +- .../schedule_widgets/schedual_view.dart | 14 +++++++------- .../schedule_widgets/schedule_managment_ui.dart | 9 +++++---- .../schedule_widgets/schedule_mode_buttons.dart | 2 +- .../schedule_widgets/schedule_table.dart | 2 +- .../helper/add_schedule_dialog_helper.dart | 13 +++++++++---- 9 files changed, 37 insertions(+), 22 deletions(-) diff --git a/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart b/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart index 30d9bf5d..86ca1317 100644 --- a/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart +++ b/lib/pages/device_managment/garage_door/view/garage_door_control_view.dart @@ -6,6 +6,7 @@ import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_ import 'package:syncrow_web/pages/device_managment/garage_door/helper/garage_door_helper.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_model.dart'; import 'package:syncrow_web/pages/device_managment/garage_door/schedule_view/schedule_garage_view.dart'; +import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart'; import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart'; import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; @@ -94,11 +95,18 @@ class GarageDoorControlView extends StatelessWidget FetchGarageDoorSchedulesEvent( deviceId: deviceId, category: 'doorcontact_state'), ); - showDialog( + + showDialog( context: context, builder: (ctx) => BlocProvider.value( value: BlocProvider.of(context), - child: BuildGarageDoorScheduleView(status: status), + child: BuildScheduleView( + deviceUuid: deviceId, + category: 'Timer', + code: 'doorcontact_state', + countdownCode: 'Timer', + deviceType: 'GD', + ), )); }, name: 'Scheduling', diff --git a/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart b/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart index 62bef920..11b2ebbb 100644 --- a/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart +++ b/lib/pages/device_managment/schedule_device/bloc/schedule_bloc.dart @@ -287,7 +287,8 @@ class ScheduleBloc extends Bloc { try { if (state is ScheduleLoaded) { Status status = Status(code: '', value: ''); - if (event.deviceType == 'CUR_2') { + if (event.deviceType == 'CUR_2' || + event.deviceType == 'GD' ) { status = status.copyWith( code: 'control', value: event.functionOn == true ? 'open' : 'close'); diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/count_down_button.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/count_down_button.dart index 8fa1e290..752f2243 100644 --- a/lib/pages/device_managment/schedule_device/schedule_widgets/count_down_button.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/count_down_button.dart @@ -69,7 +69,7 @@ class CountdownModeButtons extends StatelessWidget { countDownCode: countDownCode), ); }, - backgroundColor: ColorsManager.primaryColorWithOpacity, + backgroundColor: ColorsManager.secondaryColor, child: const Text('Save'), ), ), diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/inching_mode_buttons.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/inching_mode_buttons.dart index e8dc5e79..c8fe1357 100644 --- a/lib/pages/device_managment/schedule_device/schedule_widgets/inching_mode_buttons.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/inching_mode_buttons.dart @@ -63,7 +63,7 @@ class InchingModeButtons extends StatelessWidget { ), ); }, - backgroundColor: ColorsManager.primaryColor, + backgroundColor: ColorsManager.secondaryColor, child: const Text('Save'), ), ), diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart index d5194f35..5103e9d0 100644 --- a/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart @@ -31,11 +31,12 @@ class BuildScheduleView extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (_) => ScheduleBloc(deviceId: deviceUuid,) + create: (_) => ScheduleBloc( + deviceId: deviceUuid, + ) ..add(ScheduleGetEvent(category: category)) ..add(ScheduleFetchStatusEvent( - deviceId: deviceUuid, - countdownCode: countdownCode ?? '')), + deviceId: deviceUuid, countdownCode: countdownCode ?? '')), child: Dialog( backgroundColor: Colors.white, insetPadding: const EdgeInsets.all(20), @@ -56,7 +57,7 @@ class BuildScheduleView extends StatelessWidget { children: [ const ScheduleHeader(), const SizedBox(height: 20), - if (deviceType == 'CUR_2') + if (deviceType == 'CUR_2' || deviceType == 'GD') const SizedBox() else ScheduleModeSelector( @@ -76,8 +77,7 @@ class BuildScheduleView extends StatelessWidget { category: category, time: '', function: Status( - code: code.toString(), - value: true), + code: code.toString(), value: true), days: [], ), isEdit: false, @@ -96,7 +96,7 @@ class BuildScheduleView extends StatelessWidget { } }, ), - if (deviceType != 'CUR_2') + if (deviceType != 'CUR_2'|| deviceType != 'GD') if (state.scheduleMode == ScheduleModes.countdown || state.scheduleMode == ScheduleModes.inching) CountdownInchingView( diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_managment_ui.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_managment_ui.dart index 39899fe5..924524f0 100644 --- a/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_managment_ui.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_managment_ui.dart @@ -24,12 +24,13 @@ class ScheduleManagementUI extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( - width: 170, + width: 177, height: 40, child: DefaultButton( - borderColor: ColorsManager.grayColor.withOpacity(0.5), - padding: 2, - backgroundColor: ColorsManager.graysColor, + borderWidth: 4, + borderColor: ColorsManager.neutralGray, + padding: 8, + backgroundColor: ColorsManager.textFieldGreyColor, borderRadius: 15, onPressed: onAddSchedule, child: Row( diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_buttons.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_buttons.dart index f1df1f20..76accf60 100644 --- a/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_buttons.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_buttons.dart @@ -39,7 +39,7 @@ class ScheduleModeButtons extends StatelessWidget { borderRadius: 8, height: 40, onPressed: onSave, - backgroundColor: ColorsManager.primaryColorWithOpacity, + backgroundColor: ColorsManager.secondaryColor, child: const Text('Save'), ), ), diff --git a/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart index c1771c1b..8dc4d211 100644 --- a/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart +++ b/lib/pages/device_managment/schedule_device/schedule_widgets/schedule_table.dart @@ -194,7 +194,7 @@ class _ScheduleTableView extends StatelessWidget { child: Text(_getSelectedDays( ScheduleModel.parseSelectedDays(schedule.days)))), Center(child: Text(formatIsoStringToTime(schedule.time, context))), - if (deviceType == 'CUR_2') + if (deviceType == 'CUR_2' || deviceType == 'GD') Center( child: Text(schedule.function.value == true ? 'open' : 'close')) else diff --git a/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart b/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart index e5695b9e..d94a75d0 100644 --- a/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart +++ b/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart @@ -23,7 +23,7 @@ class ScheduleDialogHelper { required String deviceType, }) { bool temp; - if (deviceType == 'CUR_2') { + if (deviceType == 'CUR_2' || deviceType == 'GD') { temp = schedule!.function.value == 'open' ? true : false; } else { temp = schedule!.function.value; @@ -116,7 +116,7 @@ class ScheduleDialogHelper { ScheduleModeButtons( onSave: () { dynamic temp; - if (deviceType == 'CUR_2') { + if (deviceType == 'CUR_2' || deviceType == 'GD') { temp = functionOn! ? 'open' : 'close'; } else { temp = functionOn; @@ -202,18 +202,23 @@ class ScheduleDialogHelper { ), const SizedBox(width: 10), Radio( + activeColor: ColorsManager.secondaryColor, + focusColor: ColorsManager.secondaryColor, value: true, groupValue: isOn, onChanged: (val) => onChanged(true), ), - Text(categor == 'CUR_2' ? 'open' : 'On'), + Text(categor == 'CUR_2' || categor == 'GD' ? 'open' : 'On'), const SizedBox(width: 10), Radio( + activeColor: ColorsManager.secondaryColor, + focusColor: ColorsManager.secondaryColor, + value: false, groupValue: isOn, onChanged: (val) => onChanged(false), ), - Text(categor == 'CUR_2' ? 'close' : 'Off'), + Text(categor == 'CUR_2' || categor == 'GDCUR_2' ? 'close' : 'Off'), ], ); } From 0cb287567276016ed5f95d74cb944445a01ecc59 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Thu, 17 Jul 2025 17:41:22 +0300 Subject: [PATCH 080/118] 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 081/118] 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 082/118] 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 083/118] 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 084/118] 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 085/118] 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 086/118] 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 087/118] 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 088/118] 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 652163fdaed3a689462218123c368301ca168a46 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 21 Jul 2025 14:47:42 +0300 Subject: [PATCH 089/118] Updated Space management route in `app_routes.dart` to use the new, incomplete, revamped space management. --- lib/utils/app_routes.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/app_routes.dart b/lib/utils/app_routes.dart index 263bdbd6..7663a3f3 100644 --- a/lib/utils/app_routes.dart +++ b/lib/utils/app_routes.dart @@ -5,7 +5,7 @@ import 'package:syncrow_web/pages/auth/view/login_page.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/view/device_managment_page.dart'; import 'package:syncrow_web/pages/home/view/home_page.dart'; import 'package:syncrow_web/pages/roles_and_permission/view/roles_and_permission_page.dart'; -import 'package:syncrow_web/pages/spaces_management/all_spaces/view/spaces_management_page.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/views/space_management_page.dart'; import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart'; From 6bdd28ec575b73d6cf2f8af447363117e5a04712 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 21 Jul 2025 14:50:29 +0300 Subject: [PATCH 090/118] Refactor main entry points to utilize SyncrowApp, removing legacy MyApp implementation and associated dependencies, since there was too much duplicated code. --- lib/main.dart | 71 ++---------------------------------- lib/main_dev.dart | 71 ++---------------------------------- lib/main_staging.dart | 76 ++++----------------------------------- lib/services/locator.dart | 4 +-- lib/syncrow_app.dart | 53 +++++++++++++++++++++++++++ lib/utils/app_routes.dart | 24 ++++++++++--- 6 files changed, 83 insertions(+), 216 deletions(-) create mode 100644 lib/syncrow_app.dart diff --git a/lib/main.dart b/lib/main.dart index a50d2615..071c7433 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,22 +1,9 @@ import 'package:firebase_core/firebase_core.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:go_router/go_router.dart'; import 'package:syncrow_web/firebase_options.dart'; -import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; -import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; -import 'package:syncrow_web/pages/home/bloc/home_event.dart'; -import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart'; -import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; -import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; -import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart'; import 'package:syncrow_web/services/locator.dart'; -import 'package:syncrow_web/utils/app_routes.dart'; -import 'package:syncrow_web/utils/constants/routes_const.dart'; -import 'package:syncrow_web/utils/navigation_service.dart'; -import 'package:syncrow_web/utils/theme/theme.dart'; +import 'package:syncrow_web/syncrow_app.dart'; Future main() async { try { @@ -33,59 +20,5 @@ Future main() async { ); initialSetup(); } catch (_) {} - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - MyApp({super.key}); - - final GoRouter _router = GoRouter( - initialLocation: RoutesConst.auth, - routes: AppRoutes.getRoutes(), - redirect: (context, state) async { - final checkToken = await AuthBloc.getTokenAndValidate(); - final loggedIn = checkToken == 'Success'; - final goingToLogin = state.uri.toString() == RoutesConst.auth; - - if (!loggedIn && !goingToLogin) return RoutesConst.auth; - if (loggedIn && goingToLogin) return RoutesConst.home; - - return null; - }, - ); - - @override - Widget build(BuildContext context) { - return MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => CreateRoutineBloc(), - ), - BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())), - BlocProvider( - create: (context) => VisitorPasswordBloc(), - ), - BlocProvider( - create: (context) => RoutineBloc(), - ), - BlocProvider( - create: (context) => SpaceTreeBloc(), - ), - ], - child: MaterialApp.router( - debugShowCheckedModeBanner: false, - scrollBehavior: const MaterialScrollBehavior().copyWith( - dragDevices: { - PointerDeviceKind.mouse, - PointerDeviceKind.touch, - PointerDeviceKind.stylus, - PointerDeviceKind.unknown, - }, - ), - key: NavigationService.navigatorKey, - // scaffoldMessengerKey: NavigationService.snackbarKey, - theme: myTheme, - routerConfig: _router, - )); - } + runApp(const SyncrowApp()); } diff --git a/lib/main_dev.dart b/lib/main_dev.dart index 284e2f30..49df196f 100644 --- a/lib/main_dev.dart +++ b/lib/main_dev.dart @@ -1,22 +1,9 @@ import 'package:firebase_core/firebase_core.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:go_router/go_router.dart'; import 'package:syncrow_web/firebase_options.dart'; -import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; -import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; -import 'package:syncrow_web/pages/home/bloc/home_event.dart'; -import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart'; -import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; -import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; -import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart'; import 'package:syncrow_web/services/locator.dart'; -import 'package:syncrow_web/utils/app_routes.dart'; -import 'package:syncrow_web/utils/constants/routes_const.dart'; -import 'package:syncrow_web/utils/navigation_service.dart'; -import 'package:syncrow_web/utils/theme/theme.dart'; +import 'package:syncrow_web/syncrow_app.dart'; Future main() async { try { @@ -33,59 +20,5 @@ Future main() async { ); initialSetup(); } catch (_) {} - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - MyApp({super.key}); - - final GoRouter _router = GoRouter( - initialLocation: RoutesConst.auth, - routes: AppRoutes.getRoutes(), - redirect: (context, state) async { - final checkToken = await AuthBloc.getTokenAndValidate(); - final loggedIn = checkToken == 'Success'; - final goingToLogin = state.uri.toString() == RoutesConst.auth; - - if (!loggedIn && !goingToLogin) return RoutesConst.auth; - if (loggedIn && goingToLogin) return RoutesConst.home; - - return null; - }, - ); - - @override - Widget build(BuildContext context) { - return MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => CreateRoutineBloc(), - ), - BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())), - BlocProvider( - create: (context) => VisitorPasswordBloc(), - ), - BlocProvider( - create: (context) => RoutineBloc(), - ), - BlocProvider( - create: (context) => SpaceTreeBloc(), - ), - ], - child: MaterialApp.router( - debugShowCheckedModeBanner: false, - scrollBehavior: const MaterialScrollBehavior().copyWith( - dragDevices: { - PointerDeviceKind.mouse, - PointerDeviceKind.touch, - PointerDeviceKind.stylus, - PointerDeviceKind.unknown, - }, - ), - key: NavigationService.navigatorKey, - // scaffoldMessengerKey: NavigationService.snackbarKey, - theme: myTheme, - routerConfig: _router, - )); - } + runApp(const SyncrowApp()); } diff --git a/lib/main_staging.dart b/lib/main_staging.dart index 6389c53a..6e0de3e1 100644 --- a/lib/main_staging.dart +++ b/lib/main_staging.dart @@ -1,26 +1,16 @@ import 'package:firebase_core/firebase_core.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:go_router/go_router.dart'; import 'package:syncrow_web/firebase_options.dart'; -import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; -import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; -import 'package:syncrow_web/pages/home/bloc/home_event.dart'; -import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart'; -import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; -import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; -import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart'; import 'package:syncrow_web/services/locator.dart'; -import 'package:syncrow_web/utils/app_routes.dart'; -import 'package:syncrow_web/utils/constants/routes_const.dart'; -import 'package:syncrow_web/utils/navigation_service.dart'; -import 'package:syncrow_web/utils/theme/theme.dart'; +import 'package:syncrow_web/syncrow_app.dart'; Future main() async { try { - const environment = String.fromEnvironment('FLAVOR', defaultValue: 'staging'); + const environment = String.fromEnvironment( + 'FLAVOR', + defaultValue: 'staging', + ); await dotenv.load(fileName: '.env.$environment'); WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( @@ -30,59 +20,5 @@ Future main() async { ); initialSetup(); } catch (_) {} - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - MyApp({super.key}); - - final GoRouter _router = GoRouter( - initialLocation: RoutesConst.auth, - routes: AppRoutes.getRoutes(), - redirect: (context, state) async { - final checkToken = await AuthBloc.getTokenAndValidate(); - final loggedIn = checkToken == 'Success'; - final goingToLogin = state.uri.toString() == RoutesConst.auth; - - if (!loggedIn && !goingToLogin) return RoutesConst.auth; - if (loggedIn && goingToLogin) return RoutesConst.home; - - return null; - }, - ); - - @override - Widget build(BuildContext context) { - return MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => CreateRoutineBloc(), - ), - BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())), - BlocProvider( - create: (context) => VisitorPasswordBloc(), - ), - BlocProvider( - create: (context) => RoutineBloc(), - ), - BlocProvider( - create: (context) => SpaceTreeBloc(), - ), - ], - child: MaterialApp.router( - debugShowCheckedModeBanner: false, - scrollBehavior: const MaterialScrollBehavior().copyWith( - dragDevices: { - PointerDeviceKind.mouse, - PointerDeviceKind.touch, - PointerDeviceKind.stylus, - PointerDeviceKind.unknown, - }, - ), - key: NavigationService.navigatorKey, - // scaffoldMessengerKey: NavigationService.snackbarKey, - theme: myTheme, - routerConfig: _router, - )); - } + runApp(const SyncrowApp()); } diff --git a/lib/services/locator.dart b/lib/services/locator.dart index 055deb05..83fa5f56 100644 --- a/lib/services/locator.dart +++ b/lib/services/locator.dart @@ -4,9 +4,7 @@ import 'package:syncrow_web/services/api/http_interceptor.dart'; import 'package:syncrow_web/services/api/http_service.dart'; final GetIt serviceLocator = GetIt.instance; -//setupLocator() // to search for dependency injection in flutter -initialSetup() { +void initialSetup() { serviceLocator.registerSingleton(HTTPInterceptor()); - //Base classes serviceLocator.registerSingleton(HTTPService.setupDioClient()); } diff --git a/lib/syncrow_app.dart b/lib/syncrow_app.dart new file mode 100644 index 00000000..54df3351 --- /dev/null +++ b/lib/syncrow_app.dart @@ -0,0 +1,53 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; +import 'package:syncrow_web/pages/home/bloc/home_event.dart'; +import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart'; +import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; +import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; +import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart'; +import 'package:syncrow_web/utils/app_routes.dart'; +import 'package:syncrow_web/utils/navigation_service.dart'; +import 'package:syncrow_web/utils/theme/theme.dart'; + +class SyncrowApp extends StatelessWidget { + const SyncrowApp({super.key}); + + @override + Widget build(BuildContext context) { + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => CreateRoutineBloc(), + ), + BlocProvider( + create: (context) => HomeBloc()..add(const FetchUserInfo()), + ), + BlocProvider( + create: (context) => VisitorPasswordBloc(), + ), + BlocProvider( + create: (context) => RoutineBloc(), + ), + BlocProvider( + create: (context) => SpaceTreeBloc(), + ), + ], + child: MaterialApp.router( + debugShowCheckedModeBanner: false, + scrollBehavior: const MaterialScrollBehavior().copyWith( + dragDevices: { + PointerDeviceKind.mouse, + PointerDeviceKind.touch, + PointerDeviceKind.stylus, + PointerDeviceKind.unknown, + }, + ), + key: NavigationService.navigatorKey, + theme: myTheme, + routerConfig: AppRoutes.router, + ), + ); + } +} diff --git a/lib/utils/app_routes.dart b/lib/utils/app_routes.dart index 7663a3f3..aea52478 100644 --- a/lib/utils/app_routes.dart +++ b/lib/utils/app_routes.dart @@ -1,6 +1,7 @@ import 'package:go_router/go_router.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/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/auth/view/login_page.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/view/device_managment_page.dart'; import 'package:syncrow_web/pages/home/view/home_page.dart'; @@ -9,9 +10,22 @@ import 'package:syncrow_web/pages/space_management_v2/main_module/views/space_ma import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart'; -class AppRoutes { - static List getRoutes() { - return [ +abstract final class AppRoutes { + const AppRoutes._(); + + static final GoRouter router = GoRouter( + initialLocation: RoutesConst.auth, + redirect: (context, state) async { + final checkToken = await AuthBloc.getTokenAndValidate(); + final loggedIn = checkToken == 'Success'; + final goingToLogin = state.uri.toString() == RoutesConst.auth; + + if (!loggedIn && !goingToLogin) return RoutesConst.auth; + if (loggedIn && goingToLogin) return RoutesConst.home; + + return null; + }, + routes: [ GoRoute( path: RoutesConst.auth, builder: (context, state) => const LoginPage(), @@ -43,6 +57,6 @@ class AppRoutes { name: 'analytics', builder: (context, state) => const AnalyticsPage(), ), - ]; - } + ], + ); } From c473325883fe3bc12d17cb30a7f9b1efe0eed519 Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Mon, 21 Jul 2025 15:01:38 +0300 Subject: [PATCH 091/118] 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 d3bd363b70b6e2a811dcef0e8b7d7401e4dacad6 Mon Sep 17 00:00:00 2001 From: mohammad Date: Mon, 21 Jul 2025 15:10:15 +0300 Subject: [PATCH 092/118] Fix category check in schedule dialog for water heater --- .../water_heater/helper/add_schedule_dialog_helper.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart b/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart index d94a75d0..6b5ff033 100644 --- a/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart +++ b/lib/pages/device_managment/water_heater/helper/add_schedule_dialog_helper.dart @@ -218,7 +218,7 @@ class ScheduleDialogHelper { groupValue: isOn, onChanged: (val) => onChanged(false), ), - Text(categor == 'CUR_2' || categor == 'GDCUR_2' ? 'close' : 'Off'), + Text(categor == 'CUR_2' || categor == 'GD' ? 'close' : 'Off'), ], ); } From 5589e5b58780f2f013692f7ce2b1f0648dcfb58c Mon Sep 17 00:00:00 2001 From: mohammad Date: Mon, 21 Jul 2025 15:22:17 +0300 Subject: [PATCH 093/118] Refactor booking system: update API endpoint format, add ResetEvents event, and enhance UI components for improved user experience --- .../bloc/calendar/events_bloc.dart | 18 +- .../bloc/calendar/events_event.dart | 7 + .../presentation/view/booking_page.dart | 331 +++++++++--------- .../view/widgets/booking_sidebar.dart | 28 +- .../view/widgets/custom_calendar_page.dart | 34 +- .../view/widgets/event_tile_widget.dart | 8 +- .../view/widgets/room_list_item.dart | 2 +- .../view/widgets/week_day_header.dart | 43 ++- .../view/widgets/weekly_calendar_page.dart | 15 +- lib/utils/constants/api_const.dart | 3 +- 10 files changed, 271 insertions(+), 218 deletions(-) diff --git a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart index b42947bd..59a1aa28 100644 --- a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart +++ b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart @@ -10,7 +10,7 @@ part 'events_event.dart'; part 'events_state.dart'; class CalendarEventsBloc extends Bloc { - final EventController eventController = EventController(); + EventController eventController = EventController(); final CalendarSystemService calendarService; CalendarEventsBloc({ @@ -20,7 +20,9 @@ class CalendarEventsBloc extends Bloc { on(_onAddEvent); on(_onDisposeResources); on(_onGoToWeek); + on(_onResetEvents); } + Future _onLoadEvents( LoadEvents event, Emitter emit, @@ -126,4 +128,18 @@ class CalendarEventsBloc extends Bloc { eventController.dispose(); return super.close(); } + + void _onResetEvents( + ResetEvents event, + Emitter emit, + ) { + if (calendarService is MemoryCalendarServiceWithRemoteFallback) { + (calendarService as MemoryCalendarServiceWithRemoteFallback) + .memoryService + .clear(); + } + eventController.dispose(); + eventController = EventController(); + emit(EventsInitial()); + } } diff --git a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_event.dart b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_event.dart index 6a368e17..ecd7f975 100644 --- a/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_event.dart +++ b/lib/pages/access_management/booking_system/presentation/bloc/calendar/events_event.dart @@ -29,3 +29,10 @@ class CheckWeekHasEvents extends CalendarEventsEvent { final DateTime weekStart; const CheckWeekHasEvents(this.weekStart); } + +class ResetEvents extends CalendarEventsEvent { + const ResetEvents(); + + @override + List get props => []; +} \ No newline at end of file diff --git a/lib/pages/access_management/booking_system/presentation/view/booking_page.dart b/lib/pages/access_management/booking_system/presentation/view/booking_page.dart index aac5c5b7..68934ddc 100644 --- a/lib/pages/access_management/booking_system/presentation/view/booking_page.dart +++ b/lib/pages/access_management/booking_system/presentation/view/booking_page.dart @@ -1,3 +1,4 @@ +// booking_page.dart import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:calendar_view/calendar_view.dart'; @@ -26,7 +27,33 @@ class BookingPage extends StatefulWidget { } class _BookingPageState extends State { - late final EventController _eventController; + @override + Widget build(BuildContext context) { + return MultiBlocProvider( + providers: [ + BlocProvider(create: (_) => SelectedBookableSpaceBloc()), + BlocProvider(create: (_) => DateSelectionBloc()), + BlocProvider( + create: (_) => CalendarEventsBloc( + calendarService: MemoryCalendarServiceWithRemoteFallback( + remoteService: RemoteCalendarService(HTTPService()), + memoryService: MemoryCalendarService(), + ), + ), + ), + ], + child: _BookingPageContent(), + ); + } +} + +class _BookingPageContent extends StatefulWidget { + @override + State<_BookingPageContent> createState() => _BookingPageContentState(); +} + +class _BookingPageContentState extends State<_BookingPageContent> { + late EventController _eventController; @override void initState() { @@ -40,7 +67,7 @@ class _BookingPageState extends State { super.dispose(); } - void _dispatchLoadEvents(BuildContext context) { + void _loadEvents(BuildContext context) { final selectedRoom = context.read().state.selectedBookableSpace; final dateState = context.read().state; @@ -60,186 +87,170 @@ class _BookingPageState extends State { @override Widget build(BuildContext context) { - return MultiBlocProvider( - providers: [ - BlocProvider(create: (_) => SelectedBookableSpaceBloc()), - BlocProvider(create: (_) => DateSelectionBloc()), - BlocProvider( - create: (_) => CalendarEventsBloc( - calendarService: MemoryCalendarServiceWithRemoteFallback( - remoteService: RemoteCalendarService( - HTTPService(), - ), - memoryService: MemoryCalendarService(), - ), - )), - ], - child: Builder( - builder: (context) => - BlocListener( - listenWhen: (prev, curr) => curr is EventsLoaded, + return BlocListener( + listener: (context, state) { + if (state.selectedBookableSpace != null) { + // Reset events and clear cache when room changes + context.read().add(ResetEvents()); + _loadEvents(context); + } + }, + child: BlocListener( + listener: (context, state) { + _loadEvents(context); + }, + child: BlocListener( listener: (context, state) { if (state is EventsLoaded) { _eventController.removeWhere((_) => true); _eventController.addAll(state.events); } }, - child: BlocListener( - listener: (context, state) => _dispatchLoadEvents(context), - child: BlocListener( - listener: (context, state) => _dispatchLoadEvents(context), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Container( - decoration: BoxDecoration( - color: ColorsManager.whiteColors, - boxShadow: [ - BoxShadow( - color: ColorsManager.blackColor.withOpacity(0.1), - offset: const Offset(3, 0), - blurRadius: 6, - spreadRadius: 0, - ), - ], + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + boxShadow: [ + BoxShadow( + color: ColorsManager.blackColor.withOpacity(0.1), + offset: const Offset(3, 0), + blurRadius: 6, + spreadRadius: 0, ), - child: Column( - children: [ - Expanded( - flex: 2, - child: BlocBuilder( - builder: (context, state) { - return BookingSidebar( - onRoomSelected: (selectedRoom) { - context - .read() - .add(SelectBookableSpace(selectedRoom)); - }, - ); - }, - ), - ), - Expanded( - child: BlocBuilder( - builder: (context, dateState) { - return CustomCalendarPage( - selectedDate: dateState.selectedDate, - onDateChanged: (day, month, year) { - final newDate = DateTime(year, month, day); - context - .read() - .add(SelectDate(newDate)); - context.read().add( - SelectDateFromSidebarCalendar(newDate)); - }, - ); - }, - ), - ), - ], - ), - ), + ], ), - Expanded( - flex: 5, - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: Column( + children: [ + Expanded( + flex: 2, + child: BlocBuilder( + builder: (context, state) { + return BookingSidebar( + onRoomSelected: (selectedRoom) { + context + .read() + .add(SelectBookableSpace(selectedRoom)); + }, + ); + }, + ), + ), + Expanded( + child: + BlocBuilder( + builder: (context, dateState) { + return CustomCalendarPage( + selectedDate: dateState.selectedDate, + onDateChanged: (day, month, year) { + final newDate = DateTime(year, month, day); + context + .read() + .add(SelectDate(newDate)); + context.read().add( + SelectDateFromSidebarCalendar(newDate)); + }, + ); + }, + ), + ), + ], + ), + ), + ), + Expanded( + flex: 5, + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - SvgTextButton( - svgAsset: Assets.homeIcon, - label: 'Manage Bookable Spaces', - onPressed: () {}, - ), - const SizedBox(width: 20), - SvgTextButton( - svgAsset: Assets.groupIcon, - label: 'Manage Users', - onPressed: () {}, - ), - ], + SvgTextButton( + svgAsset: Assets.homeIcon, + label: 'Manage Bookable Spaces', + onPressed: () {}, ), - BlocBuilder( - builder: (context, state) { - final weekStart = state.weekStart; - final weekEnd = - weekStart.add(const Duration(days: 6)); - return WeekNavigation( - weekStart: weekStart, - weekEnd: weekEnd, - onPreviousWeek: () { - context - .read() - .add(PreviousWeek()); - }, - onNextWeek: () { - context - .read() - .add(NextWeek()); - }, - ); - }, + const SizedBox(width: 20), + SvgTextButton( + svgAsset: Assets.groupIcon, + label: 'Manage Users', + onPressed: () {}, ), ], ), - Expanded( - flex: 5, - child: BlocBuilder( - builder: (context, roomState) { - final selectedRoom = - roomState.selectedBookableSpace; - return BlocBuilder( - builder: (context, dateState) { - return BlocListener( - listenWhen: (prev, curr) => - curr is EventsLoaded, - listener: (context, state) { - if (state is EventsLoaded) { - _eventController - .removeWhere((_) => true); - _eventController.addAll(state.events); - } - }, - child: WeeklyCalendarPage( - startTime: selectedRoom - ?.bookableConfig.startTime, - endTime: selectedRoom - ?.bookableConfig.endTime, - weekStart: dateState.weekStart, - selectedDate: dateState.selectedDate, - eventController: _eventController, - selectedDateFromSideBarCalender: context - .watch() - .state - .selectedDateFromSideBarCalender, - ), + BlocBuilder( + builder: (context, state) { + final weekStart = state.weekStart; + final weekEnd = + weekStart.add(const Duration(days: 6)); + return WeekNavigation( + weekStart: weekStart, + weekEnd: weekEnd, + onPreviousWeek: () { + context + .read() + .add(PreviousWeek()); + }, + onNextWeek: () { + context + .read() + .add(NextWeek()); + }, + ); + }, + ), + ], + ), + const SizedBox(height: 20), + Expanded( + flex: 5, + child: BlocBuilder( + builder: (context, roomState) { + final selectedRoom = + roomState.selectedBookableSpace; + return BlocBuilder( + builder: (context, dateState) { + return BlocBuilder( + builder: (context, eventState) { + return WeeklyCalendarPage( + key: ValueKey( + selectedRoom?.uuid ?? 'no-room'), + startTime: selectedRoom + ?.bookableConfig.startTime, + endTime: + selectedRoom?.bookableConfig.endTime, + weekStart: dateState.weekStart, + selectedDate: dateState.selectedDate, + eventController: _eventController, + selectedDateFromSideBarCalender: context + .watch() + .state + .selectedDateFromSideBarCalender, + // isLoading: eventState is EventsLoading, ); }, ); }, - ), - ), - ], + ); + }, + ), ), - ), + ], ), - ], + ), ), - ), + ], ), ), ), diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart index 666df3bb..bd051a94 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart @@ -76,21 +76,29 @@ class __SidebarContentState extends State<_SidebarContent> { builder: (context, state) { return Column( children: [ - const _SidebarHeader(title: 'Spaces'), + Padding( + padding: const EdgeInsets.only(top: 10.0, bottom: 10.0), + child: Container( + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + boxShadow: [ + BoxShadow( + color: ColorsManager.blackColor.withOpacity(0.1), + offset: const Offset(0, 4), + blurRadius: 4, + spreadRadius: 0, + ), + ], + ), + child: _SidebarHeader(title: 'Spaces')), + ), Container( decoration: BoxDecoration( color: ColorsManager.whiteColors, - borderRadius: BorderRadius.circular(8.0), boxShadow: [ BoxShadow( color: ColorsManager.blackColor.withOpacity(0.1), - offset: const Offset(0, -2), - blurRadius: 4, - spreadRadius: 0, - ), - BoxShadow( - color: ColorsManager.blackColor.withOpacity(0.1), - offset: const Offset(0, 2), + offset: const Offset(0, 4), blurRadius: 4, spreadRadius: 0, ), @@ -220,7 +228,7 @@ class _SidebarHeader extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.all(16.0), + padding: const EdgeInsets.all(10.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/custom_calendar_page.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/custom_calendar_page.dart index eb758311..21dfe646 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/custom_calendar_page.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/custom_calendar_page.dart @@ -66,18 +66,28 @@ class _CustomCalendarPageState extends State { weekdayLabels: const ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'], ); - return CalendarDatePicker2( - config: config, - value: [_selectedDate], - onValueChanged: (dates) { - final picked = dates.first; - if (picked != null) { - setState(() { - _selectedDate = picked; - }); - widget.onDateChanged(picked.day, picked.month, picked.year); - } - }, + return Container( + decoration: const BoxDecoration( + border: Border( + top: BorderSide( + color: ColorsManager.textGray, + width: 1.0, + ), + ), + ), + child: CalendarDatePicker2( + config: config, + value: [_selectedDate], + onValueChanged: (dates) { + final picked = dates.first; + if (picked != null) { + setState(() { + _selectedDate = picked; + }); + widget.onDateChanged(picked.day, picked.month, picked.year); + } + }, + ), ); } } diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/event_tile_widget.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/event_tile_widget.dart index b7e942d6..87c10a81 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/event_tile_widget.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/event_tile_widget.dart @@ -32,15 +32,17 @@ class EventTileWidget extends StatelessWidget { return Expanded( child: Container( width: double.infinity, - padding: EdgeInsets.all(5), + padding: const EdgeInsets.all(5), decoration: BoxDecoration( color: isEventEnded ? ColorsManager.grayColor.withOpacity(0.1) : ColorsManager.blue1.withOpacity(0.1), borderRadius: BorderRadius.circular(6), - border: const Border( + border: Border( left: BorderSide( - color: ColorsManager.grayColor, + color: isEventEnded + ? ColorsManager.grayColor + : ColorsManager.secondaryColor, width: 4, ), ), diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/room_list_item.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/room_list_item.dart index 83eda16b..57a14002 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/room_list_item.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/room_list_item.dart @@ -21,7 +21,7 @@ class RoomListItem extends StatelessWidget { groupValue: isSelected ? room.uuid : null, visualDensity: const VisualDensity(vertical: -4), onChanged: (value) => onTap(), - activeColor: ColorsManager.primaryColor, + activeColor: ColorsManager.secondaryColor, title: Text( room.spaceName, maxLines: 2, diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/week_day_header.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/week_day_header.dart index 57e35c6d..325ce582 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/week_day_header.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/week_day_header.dart @@ -14,26 +14,33 @@ class WeekDayHeader extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( - children: [ - Text( - DateFormat('EEE').format(date).toUpperCase(), - style: TextStyle( - fontWeight: FontWeight.w400, - fontSize: 14, - color: isSelectedDay ? Colors.blue : Colors.black, + return ColoredBox( + color: isSelectedDay + ? ColorsManager.secondaryColor.withOpacity(0.1) + : Colors.transparent, + child: Column( + children: [ + const SizedBox( + height: 10, ), - ), - Text( - DateFormat('d').format(date), - style: TextStyle( - fontWeight: FontWeight.w700, - fontSize: 20, - color: - isSelectedDay ? ColorsManager.blue1 : ColorsManager.blackColor, + Text( + DateFormat('EEE').format(date).toUpperCase(), + style: const TextStyle( + fontWeight: FontWeight.w400, + fontSize: 14, + color: ColorsManager.blackColor, + ), ), - ), - ], + Text( + DateFormat('d').format(date), + style: const TextStyle( + fontWeight: FontWeight.w700, + fontSize: 30, + color: ColorsManager.blackColor, + ), + ), + ], + ), ); } } diff --git a/lib/pages/access_management/booking_system/presentation/view/widgets/weekly_calendar_page.dart b/lib/pages/access_management/booking_system/presentation/view/widgets/weekly_calendar_page.dart index 2bfd5429..931746b7 100644 --- a/lib/pages/access_management/booking_system/presentation/view/widgets/weekly_calendar_page.dart +++ b/lib/pages/access_management/booking_system/presentation/view/widgets/weekly_calendar_page.dart @@ -24,7 +24,7 @@ class WeeklyCalendarPage extends StatelessWidget { }); static const double timeLineWidth = 65; static const int totalDays = 7; - static const double dayColumnWidth = 220; + static const double dayColumnWidth = 220; final double calendarContentWidth = timeLineWidth + (totalDays * dayColumnWidth); @@ -57,13 +57,10 @@ class WeeklyCalendarPage extends StatelessWidget { ); } - - - const double timeLineWidth = 65; + const double timeLineWidth = 90; return LayoutBuilder( builder: (context, constraints) { - bool isInRange(DateTime date, DateTime start, DateTime end) { !date.isBefore(start) && !date.isAfter(end); // remove this line and Check if the date is within the range @@ -100,7 +97,6 @@ class WeeklyCalendarPage extends StatelessWidget { width: width, decoration: BoxDecoration( color: Colors.orange.withOpacity(0.13), - borderRadius: BorderRadius.circular(8), ), ); } else if (isSelected) { @@ -110,7 +106,6 @@ class WeeklyCalendarPage extends StatelessWidget { decoration: BoxDecoration( color: ColorsManager.spaceColor.withOpacity(0.07), - borderRadius: BorderRadius.circular(8), ), ); } @@ -143,7 +138,7 @@ class WeeklyCalendarPage extends StatelessWidget { heightPerMinute: 1.7, showLiveTimeLineInAllDays: false, showVerticalLines: true, - emulateVerticalOffsetBy: -80, + emulateVerticalOffsetBy: -95, startDay: WeekDays.monday, liveTimeIndicatorSettings: const LiveTimeIndicatorSettings( @@ -161,7 +156,7 @@ class WeeklyCalendarPage extends StatelessWidget { }, timeLineWidth: timeLineWidth, weekPageHeaderBuilder: (start, end) => Container(), - weekTitleHeight: 60, + weekTitleHeight: 90, weekNumberBuilder: (firstDayOfWeek) => Padding( padding: const EdgeInsets.only(right: 15, bottom: 10), child: Column( @@ -208,8 +203,6 @@ class WeeklyCalendarPage extends StatelessWidget { }, ); } - - } bool isSameDay(DateTime d1, DateTime d2) { diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index e99f4796..a53582be 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -140,6 +140,5 @@ abstract class ApiEndpoints { static const String saveSchedule = '/schedule/{deviceUuid}'; static const String getBookableSpaces = '/bookable-spaces'; - static const String getBookings = - '/bookings?month={mm}%2F{yyyy}&space={space}'; + static const String getBookings = '/bookings?month={mm}-{yyyy}&space={space}'; } From 7af8887d4f051df9328c04d969a01bad1d3a8df3 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 21 Jul 2025 15:57:10 +0300 Subject: [PATCH 094/118] 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 095/118] 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 096/118] 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 097/118] 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 098/118] 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 099/118] 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 100/118] 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 101/118] 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 102/118] 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 103/118] 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 104/118] 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 105/118] 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 106/118] 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 107/118] 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 108/118] 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 109/118] 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 110/118] 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 111/118] 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 112/118] 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 113/118] 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 114/118] 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 115/118] 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 116/118] 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 117/118] 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 118/118] 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,