mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-08-25 15:39:41 +00:00
Refactor booking system: replace individual parameters with LoadBookableSpacesParam for improved clarity and maintainability
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/paginated_bookable_spaces.dart';
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/services/booking_system_service.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/params/load_communities_param.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';
|
||||
@ -13,25 +14,29 @@ class BookableSpacesService implements BookingSystemService {
|
||||
|
||||
@override
|
||||
Future<PaginatedBookableSpaces> getBookableSpaces({
|
||||
required int page,
|
||||
required int size,
|
||||
required String search,
|
||||
required LoadCommunitiesParam param,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.getBookableSpaces,
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
'size': size,
|
||||
'page': param.page,
|
||||
'size': param.size,
|
||||
'active': true,
|
||||
'configured': true,
|
||||
if (search.isNotEmpty && search != 'null') 'search': search,
|
||||
if (param.search != null &&
|
||||
param.search.isNotEmpty &&
|
||||
param.search != 'null')
|
||||
'search': param.search,
|
||||
if (param.includeSpaces != null)
|
||||
'includeSpaces': param.includeSpaces,
|
||||
},
|
||||
expectedResponseModel: (json) {
|
||||
return PaginatedBookableSpaces.fromJson(
|
||||
json as Map<String, dynamic>,
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} on DioException catch (e) {
|
||||
final responseData = e.response?.data;
|
||||
|
@ -0,0 +1,36 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class LoadBookableSpacesParam extends Equatable {
|
||||
const LoadBookableSpacesParam({
|
||||
this.page = 1,
|
||||
this.size = 25,
|
||||
this.search = '',
|
||||
this.active = true,
|
||||
this.configured = true,
|
||||
});
|
||||
|
||||
final int page;
|
||||
final int size;
|
||||
final String search;
|
||||
final bool active;
|
||||
final bool configured;
|
||||
|
||||
LoadBookableSpacesParam copyWith({
|
||||
int? page,
|
||||
int? size,
|
||||
String? search,
|
||||
bool? active,
|
||||
bool? configured,
|
||||
}) {
|
||||
return LoadBookableSpacesParam(
|
||||
page: page ?? this.page,
|
||||
size: size ?? this.size,
|
||||
search: search ?? this.search,
|
||||
active: active ?? this.active,
|
||||
configured: configured ?? this.configured,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [page, size, search, active, configured];
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
import 'dart:async';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/paginated_bookable_spaces.dart';
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/services/booking_system_service.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart';
|
||||
|
||||
class DebouncedBookingSystemService implements BookingSystemService {
|
||||
final BookingSystemService _inner;
|
||||
@ -9,11 +11,6 @@ class DebouncedBookingSystemService implements BookingSystemService {
|
||||
Timer? _debounceTimer;
|
||||
Completer<PaginatedBookableSpaces>? _lastCompleter;
|
||||
|
||||
int? _lastPage;
|
||||
int? _lastSize;
|
||||
bool? _lastIncludeSpaces;
|
||||
String? _lastSearch;
|
||||
|
||||
DebouncedBookingSystemService(
|
||||
this._inner, {
|
||||
this.debounceDuration = const Duration(milliseconds: 500),
|
||||
@ -21,27 +18,20 @@ class DebouncedBookingSystemService implements BookingSystemService {
|
||||
|
||||
@override
|
||||
Future<PaginatedBookableSpaces> getBookableSpaces({
|
||||
required int page,
|
||||
required int size,
|
||||
required String search,
|
||||
required LoadCommunitiesParam param,
|
||||
}) {
|
||||
_debounceTimer?.cancel();
|
||||
_lastCompleter?.completeError(StateError("Cancelled by new search"));
|
||||
if (_lastCompleter != null && !_lastCompleter!.isCompleted) {
|
||||
_lastCompleter!
|
||||
.completeError(StateError("Cancelled by new search"));
|
||||
}
|
||||
|
||||
final completer = Completer<PaginatedBookableSpaces>();
|
||||
_lastCompleter = completer;
|
||||
|
||||
_lastPage = page;
|
||||
_lastSize = size;
|
||||
_lastSearch = search;
|
||||
|
||||
_debounceTimer = Timer(debounceDuration, () async {
|
||||
try {
|
||||
final result = await _inner.getBookableSpaces(
|
||||
page: _lastPage!,
|
||||
size: _lastSize!,
|
||||
search: _lastSearch!,
|
||||
);
|
||||
final result = await _inner.getBookableSpaces(param: param);
|
||||
if (!completer.isCompleted) {
|
||||
completer.complete(result);
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/paginated_bookable_spaces.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart';
|
||||
|
||||
abstract class BookingSystemService {
|
||||
Future<PaginatedBookableSpaces> getBookableSpaces({
|
||||
required int page,
|
||||
required int size,
|
||||
required String search,
|
||||
|
||||
required LoadCommunitiesParam param,
|
||||
});
|
||||
}
|
@ -3,10 +3,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/services/booking_system_service.dart';
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/sidebar/sidebar_event.dart';
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/sidebar/sidebar_state.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart';
|
||||
|
||||
class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
||||
final BookingSystemService _bookingService;
|
||||
Timer? _searchDebounce;
|
||||
int _currentPage = 1;
|
||||
final int _pageSize = 20;
|
||||
String _currentSearch = '';
|
||||
@ -35,9 +35,11 @@ class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
||||
_currentSearch = '';
|
||||
|
||||
final paginatedSpaces = await _bookingService.getBookableSpaces(
|
||||
param: LoadCommunitiesParam(
|
||||
page: _currentPage,
|
||||
size: _pageSize,
|
||||
search: _currentSearch,
|
||||
),
|
||||
);
|
||||
|
||||
emit(state.copyWith(
|
||||
@ -67,9 +69,12 @@ class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
||||
_currentPage++;
|
||||
|
||||
final paginatedSpaces = await _bookingService.getBookableSpaces(
|
||||
param: LoadCommunitiesParam(
|
||||
page: _currentPage,
|
||||
size: _pageSize,
|
||||
search: _currentSearch,
|
||||
// Add any other required params
|
||||
),
|
||||
);
|
||||
|
||||
final updatedRooms = [...state.allRooms, ...paginatedSpaces.data];
|
||||
@ -79,6 +84,7 @@ class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
||||
displayedRooms: updatedRooms,
|
||||
isLoadingMore: false,
|
||||
hasMore: paginatedSpaces.hasNext,
|
||||
totalPages: paginatedSpaces.totalPage,
|
||||
currentPage: _currentPage,
|
||||
));
|
||||
} catch (e) {
|
||||
@ -99,11 +105,13 @@ class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
||||
_currentPage = 1;
|
||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||
final paginatedSpaces = await _bookingService.getBookableSpaces(
|
||||
param: LoadCommunitiesParam(
|
||||
page: _currentPage,
|
||||
size: _pageSize,
|
||||
search: _currentSearch,
|
||||
// Add other fields if required
|
||||
),
|
||||
);
|
||||
|
||||
emit(state.copyWith(
|
||||
allRooms: paginatedSpaces.data,
|
||||
displayedRooms: paginatedSpaces.data,
|
||||
@ -137,7 +145,6 @@ class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_searchDebounce?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
@ -40,14 +40,18 @@ class WeeklyCalendarPage extends StatelessWidget {
|
||||
const double timeLineWidth = 80;
|
||||
const int totalDays = 7;
|
||||
final double dayColumnWidth =
|
||||
(calendarWidth - timeLineWidth) / totalDays;
|
||||
(calendarWidth - timeLineWidth) / totalDays - 0.1;
|
||||
final selectedDayIndex =
|
||||
weekDays.indexWhere((d) => isSameDay(d, selectedDate));
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 25.0, right: 25.0, top: 25),
|
||||
child: Stack(
|
||||
children: [
|
||||
WeekView(
|
||||
AbsorbPointer(
|
||||
absorbing: false,
|
||||
ignoringSemantics: false,
|
||||
child: WeekView(
|
||||
scrollPhysics: const NeverScrollableScrollPhysics(),
|
||||
key: ValueKey(weekStart),
|
||||
controller: eventController,
|
||||
initialDay: weekStart,
|
||||
@ -64,25 +68,14 @@ class WeeklyCalendarPage extends StatelessWidget {
|
||||
),
|
||||
weekDayBuilder: (date) {
|
||||
final weekDays = _getWeekDays(weekStart);
|
||||
final selectedDayIndex =
|
||||
weekDays.indexWhere((d) => isSameDay(d, selectedDate));
|
||||
final index = weekDays.indexWhere((d) => isSameDay(d, date));
|
||||
final selectedDayIndex = weekDays
|
||||
.indexWhere((d) => isSameDay(d, selectedDate));
|
||||
final index =
|
||||
weekDays.indexWhere((d) => isSameDay(d, date));
|
||||
final isSelectedDay = index == selectedDayIndex;
|
||||
final isToday = isSameDay(date, DateTime.now());
|
||||
|
||||
return Container(
|
||||
decoration: isSelectedDay
|
||||
? BoxDecoration(
|
||||
color: ColorsManager.blue1.withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
)
|
||||
: isToday
|
||||
? BoxDecoration(
|
||||
color: ColorsManager.blue1.withOpacity(0.08),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
)
|
||||
: null,
|
||||
child: Column(
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
DateFormat('EEE').format(date).toUpperCase(),
|
||||
@ -103,7 +96,6 @@ class WeeklyCalendarPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
timeLineBuilder: (date) {
|
||||
@ -127,7 +119,8 @@ class WeeklyCalendarPage extends StatelessWidget {
|
||||
),
|
||||
WidgetSpan(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 2, top: 6),
|
||||
padding:
|
||||
const EdgeInsets.only(left: 2, top: 6),
|
||||
child: Text(
|
||||
period,
|
||||
style: const TextStyle(
|
||||
@ -149,17 +142,33 @@ class WeeklyCalendarPage extends StatelessWidget {
|
||||
timeLineWidth: timeLineWidth,
|
||||
weekPageHeaderBuilder: (start, end) => Container(),
|
||||
weekTitleHeight: 60,
|
||||
weekNumberBuilder: (firstDayOfWeek) => Text(
|
||||
firstDayOfWeek.timeZoneName,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
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 Container(
|
||||
margin:
|
||||
const EdgeInsets.symmetric(vertical: 2, horizontal: 2),
|
||||
margin: const EdgeInsets.symmetric(
|
||||
vertical: 2, horizontal: 2),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: events.map((event) {
|
||||
@ -171,16 +180,16 @@ class WeeklyCalendarPage extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
color: isEventEnded
|
||||
? ColorsManager.grayColor
|
||||
: ColorsManager.lightGrayColor
|
||||
.withOpacity(0.25),
|
||||
? ColorsManager.lightGrayBorderColor
|
||||
: ColorsManager.blue1.withOpacity(0.25),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
DateFormat('h:mm a').format(event.startTime!),
|
||||
DateFormat('h:mm a')
|
||||
.format(event.startTime!),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
@ -203,18 +212,19 @@ class WeeklyCalendarPage extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)),
|
||||
if (selectedDayIndex >= 0)
|
||||
Positioned(
|
||||
left: timeLineWidth + dayColumnWidth * selectedDayIndex,
|
||||
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: 2),
|
||||
color: ColorsManager.blue1.withOpacity(0.1),
|
||||
vertical: 0, horizontal: 4),
|
||||
color: ColorsManager.spaceColor.withOpacity(0.07),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -253,7 +263,6 @@ int _parseHour(String? time, {required int defaultValue}) {
|
||||
}
|
||||
try {
|
||||
return int.parse(time.split(':')[0]);
|
||||
|
||||
} catch (e) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
Reference in New Issue
Block a user