insert after update and refactor

This commit is contained in:
Rafeek-Khoudare
2025-07-10 15:52:15 +03:00
parent d58da9644f
commit aab2b4a52a
5 changed files with 84 additions and 373 deletions

View File

@ -1,5 +1,6 @@
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.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/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/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/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart';
@ -15,6 +16,7 @@ class BookableSpacesBloc
BookableSpacesBloc(this.bookableSpacesService) BookableSpacesBloc(this.bookableSpacesService)
: super(BookableSpacesInitial()) { : super(BookableSpacesInitial()) {
on<LoadBookableSpacesEvent>(_onLoadBookableSpaces); on<LoadBookableSpacesEvent>(_onLoadBookableSpaces);
on<InsertUpdatedSpaceEvent>(_onInsertUpdatedSpaceEven);
} }
Future<void> _onLoadBookableSpaces( Future<void> _onLoadBookableSpaces(
@ -31,4 +33,31 @@ class BookableSpacesBloc
); );
} }
} }
void _onInsertUpdatedSpaceEven(
InsertUpdatedSpaceEvent event, Emitter<BookableSpacesState> 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));
}
} }

View File

@ -11,3 +11,14 @@ class LoadBookableSpacesEvent extends BookableSpacesEvent {
final BookableSpacesParams params; final BookableSpacesParams params;
const LoadBookableSpacesEvent(this.params); const LoadBookableSpacesEvent(this.params);
} }
class InsertUpdatedSpaceEvent extends BookableSpacesEvent {
final PaginatedDataModel<BookableSpacemodel> bookableSpaces;
final BookableSpacemodel bookableSpace;
final BookableSpaceConfig updatedBookableSpaceConfig;
const InsertUpdatedSpaceEvent({
required this.bookableSpaces,
required this.bookableSpace,
required this.updatedBookableSpaceConfig,
});
}

View File

@ -24,3 +24,5 @@ final class BookableSpacesError extends BookableSpacesState {
required this.error, required this.error,
}); });
} }
class InsertingUpdatedSpaceState extends BookableSpacesState {}

View File

@ -1,24 +1,22 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/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/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/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/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/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.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/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/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 { class ManageBookableSpacesPage extends StatefulWidget {
const ManageBookableSpacesPage({super.key}); final PageController pageController;
const ManageBookableSpacesPage({
super.key,
required this.pageController,
});
@override @override
State<ManageBookableSpacesPage> createState() => State<ManageBookableSpacesPage> createState() =>
@ -26,63 +24,39 @@ class ManageBookableSpacesPage extends StatefulWidget {
} }
class _ManageBookableSpacesPageState extends State<ManageBookableSpacesPage> { class _ManageBookableSpacesPageState extends State<ManageBookableSpacesPage> {
final PageController _pageController = PageController(initialPage: 1);
int _currentPageIndex = 1;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return WebScaffold( return MultiBlocProvider(
enableMenuSidebar: false, providers: [
appBarTitle: Text( BlocProvider(
'Access Management', create: (context) => BookableSpacesBloc(
style: ResponsiveTextTheme.of(context).deviceManagementTitle, RemoteBookableSpacesService(HTTPService()),
), )..add(
centerBody: Row( LoadBookableSpacesEvent(
mainAxisSize: MainAxisSize.min, BookableSpacesParams(currentPage: 1),
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,
), ),
), ),
),
BlocProvider(
create: (context) => UpdateBookableSpacesBloc(
RemoteUpdateBookableSpaceService(HTTPService()),
), ),
TextButton( )
onPressed: () => _switchPage(1), ],
child: Text( child: ManageBookableSpacesWidget(
'Booking System', pageController: widget.pageController,
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(),
), ),
); );
} }
void _switchPage(int index) {
setState(() => _currentPageIndex = index);
_pageController.jumpToPage(index);
}
} }
class ManageBookableSpacesWidget extends StatelessWidget { class ManageBookableSpacesWidget extends StatelessWidget {
const ManageBookableSpacesWidget({super.key}); final PageController pageController;
const ManageBookableSpacesWidget({
super.key,
required this.pageController,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -91,332 +65,23 @@ class ManageBookableSpacesWidget extends StatelessWidget {
child: Column( child: Column(
children: [ children: [
Expanded( Expanded(
flex: 10, flex: 10, child: TopPartWidget(pageController: pageController)),
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<BookableSpacesBloc>();
showDialog(
context: context,
builder: (context) => BlocProvider.value(
value: bloc,
child: SetupBookableSpacesDialog(),
),
);
},
)
],
),
)),
const SizedBox( const SizedBox(
height: 10, height: 10,
), ),
Expanded( const Expanded(
flex: 85, flex: 85,
child: BlocBuilder<BookableSpacesBloc, BookableSpacesState>( child: TablePartWidget(),
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<BookableSpacesBloc>()
.add(LoadBookableSpacesEvent(
BookableSpacesParams(currentPage: 1),
)),
child: const Text('try Again'))
]);
} else if (state is BookableSpacesLoaded) {
return CustomDataTable<BookableSpacemodel>(
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<Color>(
(Set<WidgetState> states) {
return ColorsManager.blue1;
}),
inactiveTrackColor: ColorsManager.lightGrayColor,
thumbColor: WidgetStateProperty.resolveWith<Color>(
(Set<WidgetState> 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();
}
},
),
), ),
const SizedBox( const SizedBox(
height: 5, height: 5,
), ),
Expanded( const Expanded(
flex: 5, flex: 5,
child: BlocBuilder<BookableSpacesBloc, BookableSpacesState>( child: BottomPaginationPartWidget(),
builder: (context, state) {
if (state is BookableSpacesLoaded) {
final totalPages = state.bookableSpacesList.totalPages;
final currentPage = state.bookableSpacesList.page;
List<Widget> paginationItems = [];
// « Two pages back
if (currentPage > 2) {
paginationItems.add(
_buildArrowButton(
label: '«',
onTap: () {
context.read<BookableSpacesBloc>().add(
LoadBookableSpacesEvent(
BookableSpacesParams(
currentPage: currentPage - 2),
),
);
},
),
);
}
// < One page back
if (currentPage > 1) {
paginationItems.add(
_buildArrowButton(
label: '<',
onTap: () {
context.read<BookableSpacesBloc>().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<BookableSpacesBloc>().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<BookableSpacesBloc>().add(
LoadBookableSpacesEvent(
BookableSpacesParams(
currentPage: currentPage + 1),
),
);
},
),
);
}
// » Two pages forward
if (currentPage + 1 < totalPages) {
paginationItems.add(
_buildArrowButton(
label: '»',
onTap: () {
context.read<BookableSpacesBloc>().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,
),
),
),
),
);
}
} }

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.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/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/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/buttons_divider_bottom_dialog_widget.dart';
@ -21,6 +22,9 @@ class NextFirstStepButton extends StatelessWidget {
? null ? null
: () { : () {
context.read<StepsCubit>().goToNextStep(); context.read<StepsCubit>().goToNextStep();
context
.read<NonBookableSpacesBloc>()
.add(CheckConfigurValidityEvent());
}, },
onCancelPressed: () => context.pop(), onCancelPressed: () => context.pop(),
); );