Compare commits

...

6 Commits

Author SHA1 Message Date
9d130139f7 return true refactor code 2025-07-16 12:17:09 +03:00
bee4e05404 delete unused route 2025-07-16 12:10:11 +03:00
6db60a2a97 dont use context in async block 2025-07-16 11:31:54 +03:00
7f9f39811b use switch state instead of if else 2025-07-16 11:26:14 +03:00
739b491bd8 debouncer Note 2025-07-16 11:21:32 +03:00
db157f30c5 first notes requests 2025-07-16 10:35:16 +03:00
16 changed files with 148 additions and 128 deletions

View File

@ -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<PaginatedDataModel<BookableSpacemodel>> 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<String, dynamic>;
return PaginatedDataModel.fromJson(
result,
BookableSpacemodel.fromJsonList,
);
},
);
return response;
} on DioException catch (e) {
final message = e.response?.data as Map<String, dynamic>?;
final error = message?['error'] as Map<String, dynamic>?;
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<PaginatedDataModel<BookableSpacemodel>>();
_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<String, dynamic>;
return PaginatedDataModel.fromJson(
result,
BookableSpacemodel.fromJsonList,
);
},
);
completer.complete(response);
} on DioException catch (e) {
final message = e.response?.data as Map<String, dynamic>?;
final error = message?['error'] as Map<String, dynamic>?;
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

View File

@ -23,7 +23,7 @@ class BookableSpacesBloc
LoadBookableSpacesEvent event, Emitter<BookableSpacesState> 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));

View File

@ -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 {

View File

@ -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(),
),
],
),

View File

@ -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<SetupBookableSpacesDialog> createState() =>
_SetupBookableSpacesDialogState();
}
class _SetupBookableSpacesDialogState extends State<SetupBookableSpacesDialog> {
final TextEditingController pointsController = TextEditingController();
@override
void dispose() {
pointsController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<StepsCubit>(
create: editingBookableSpace == null
create: widget.editingBookableSpace == null
? (context) => StepsCubit()..initDialogValue()
: (context) => StepsCubit()..editValueInit(),
),
BlocProvider<NonBookableSpacesBloc>(
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,
);
}),
],

View File

@ -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,

View File

@ -18,20 +18,16 @@ class DetailsStepsWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
child: BlocBuilder<StepsCubit, StepsState>(
builder: (context, state) {
if (state is StepOneState) {
return const SpacesStepDetailsWidget();
} else if (state is StepTwoState) {
return StepTwoDetailsWidget(
child: BlocBuilder<StepsCubit, StepsState>(builder: (context, state) {
return switch (state) {
StepOneState() => const SpacesStepDetailsWidget(),
StepTwoState() => StepTwoDetailsWidget(
pointsController: pointsController,
editingBookableSpace:editingBookableSpace
);
} else {
return const SizedBox();
}
},
),
editingBookableSpace: editingBookableSpace,
),
StepsInitial() => const SizedBox(),
};
}),
);
}
}

View File

@ -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) {

View File

@ -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<BookableSpacemodel>(

View File

@ -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,
});

View File

@ -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,
),
),
);

View File

@ -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';
@ -19,7 +17,6 @@ class SpacesStepDetailsWidget extends StatefulWidget {
}
class _SpacesStepDetailsWidgetState extends State<SpacesStepDetailsWidget> {
Timer? _debounce;
ScrollController scrollController = ScrollController();
int currentPage = 1;
String? currentSearchTerm;
@ -80,7 +77,7 @@ class _SpacesStepDetailsWidgetState extends State<SpacesStepDetailsWidget> {
borderRadius: BorderRadius.circular(20),
boxShadow: const [
BoxShadow(
color: Color(0x40000000),
color: ColorsManager.shadowOfDetailsContainer,
offset: Offset.zero,
blurRadius: 5,
),
@ -94,7 +91,7 @@ class _SpacesStepDetailsWidgetState extends State<SpacesStepDetailsWidget> {
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 +99,16 @@ class _SpacesStepDetailsWidgetState extends State<SpacesStepDetailsWidget> {
child: SearchUnbookableSpacesWidget(
title: 'Search',
onChanged: (p0) {
if (_debounce?.isActive ?? false) _debounce!.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
currentSearchTerm = p0;
currentPage = 1;
context.read<NonBookableSpacesBloc>().add(
LoadUnBookableSpacesEvent(
nonBookableSpacesParams: NonBookableSpacesParams(
currentPage: currentPage,
searchedWords: currentSearchTerm,
),
currentSearchTerm = p0;
currentPage = 1;
context.read<NonBookableSpacesBloc>().add(
LoadUnBookableSpacesEvent(
nonBookableSpacesParams: NonBookableSpacesParams(
currentPage: currentPage,
searchedWords: currentSearchTerm,
),
);
});
),
);
},
),
),
@ -127,15 +121,13 @@ class _SpacesStepDetailsWidgetState extends State<SpacesStepDetailsWidget> {
}
},
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<NonBookableSpacesBloc>().add(
LoadUnBookableSpacesEvent(
@ -147,28 +139,28 @@ class _SpacesStepDetailsWidgetState extends State<SpacesStepDetailsWidget> {
),
);
},
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(),
};
},
),
)

View File

@ -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<TimePickerWidget> createState() => _TimePickerWidgetState();
@ -20,6 +20,13 @@ class TimePickerWidget extends StatefulWidget {
class _TimePickerWidgetState extends State<TimePickerWidget> {
TimeOfDay? timePicked;
@override
void initState() {
widget.nonBookableSpacesBloc = context.read<NonBookableSpacesBloc>();
super.initState();
}
@override
Widget build(BuildContext context) {
return InkWell(
@ -42,7 +49,7 @@ class _TimePickerWidgetState extends State<TimePickerWidget> {
);
widget.onTimePicked(tempTime);
timePicked = tempTime;
context.read<NonBookableSpacesBloc>().add(CheckConfigurValidityEvent());
widget.nonBookableSpacesBloc.add(CheckConfigurValidityEvent());
setState(() {});
},
child: Container(

View File

@ -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);
}

View File

@ -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';
}

View File

@ -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) {