mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-17 02:25:31 +00:00
Compare commits
6 Commits
2d16bda61d
...
implement-
Author | SHA1 | Date | |
---|---|---|---|
645a07287e | |||
e55e793081 | |||
885ef61114 | |||
bfd6b5c3a0 | |||
2b638940ae | |||
3e95bf4473 |
@ -1,37 +1,40 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/load_bookable_spaces_param.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/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/access_management/booking_system/domain/services/bookable_system_service.dart';
|
||||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||||
|
|
||||||
class BookableSpacesService implements BookingSystemService {
|
class RemoteBookableSpacesService implements BookableSystemService {
|
||||||
const BookableSpacesService(this._httpService);
|
const RemoteBookableSpacesService(this._httpService);
|
||||||
|
|
||||||
final HTTPService _httpService;
|
final HTTPService _httpService;
|
||||||
static const _defaultErrorMessage = 'Failed to load bookable spaces';
|
static const _defaultErrorMessage = 'Failed to load bookable spaces';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PaginatedBookableSpaces> getBookableSpaces({
|
Future<PaginatedBookableSpaces> getBookableSpaces({
|
||||||
required int page,
|
required LoadBookableSpacesParam param,
|
||||||
required int size,
|
|
||||||
required String search,
|
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final response = await _httpService.get(
|
final response = await _httpService.get(
|
||||||
path: ApiEndpoints.getBookableSpaces,
|
path: ApiEndpoints.getBookableSpaces,
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'page': page,
|
'page': param.page,
|
||||||
'size': size,
|
'size': param.size,
|
||||||
'active': true,
|
'active': true,
|
||||||
'configured': true,
|
'configured': true,
|
||||||
if (search.isNotEmpty && search != 'null') 'search': search,
|
if (param.search != null &&
|
||||||
},
|
param.search.isNotEmpty &&
|
||||||
expectedResponseModel: (json) {
|
param.search != 'null')
|
||||||
return PaginatedBookableSpaces.fromJson(
|
'search': param.search,
|
||||||
json as Map<String, dynamic>,
|
},
|
||||||
);
|
expectedResponseModel: (json) {
|
||||||
});
|
return PaginatedBookableSpaces.fromJson(
|
||||||
|
json as Map<String, dynamic>,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
return response;
|
return response;
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
final responseData = e.response?.data;
|
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];
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/load_bookable_spaces_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/paginated_bookable_spaces.dart';
|
||||||
|
|
||||||
|
abstract class BookableSystemService {
|
||||||
|
Future<PaginatedBookableSpaces> getBookableSpaces({
|
||||||
|
required LoadBookableSpacesParam param,
|
||||||
|
});
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/paginated_bookable_spaces.dart';
|
|
||||||
|
|
||||||
abstract class BookingSystemService {
|
|
||||||
Future<PaginatedBookableSpaces> getBookableSpaces({
|
|
||||||
required int page,
|
|
||||||
required int size,
|
|
||||||
required String search,
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,47 +1,35 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/load_bookable_spaces_param.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/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/access_management/booking_system/domain/services/bookable_system_service.dart';
|
||||||
|
|
||||||
class DebouncedBookingSystemService implements BookingSystemService {
|
class DebouncedBookableSpacesService implements BookableSystemService {
|
||||||
final BookingSystemService _inner;
|
final BookableSystemService _inner;
|
||||||
final Duration debounceDuration;
|
final Duration debounceDuration;
|
||||||
|
|
||||||
Timer? _debounceTimer;
|
Timer? _debounceTimer;
|
||||||
Completer<PaginatedBookableSpaces>? _lastCompleter;
|
Completer<PaginatedBookableSpaces>? _lastCompleter;
|
||||||
|
|
||||||
int? _lastPage;
|
DebouncedBookableSpacesService(
|
||||||
int? _lastSize;
|
|
||||||
bool? _lastIncludeSpaces;
|
|
||||||
String? _lastSearch;
|
|
||||||
|
|
||||||
DebouncedBookingSystemService(
|
|
||||||
this._inner, {
|
this._inner, {
|
||||||
this.debounceDuration = const Duration(milliseconds: 500),
|
this.debounceDuration = const Duration(milliseconds: 500),
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PaginatedBookableSpaces> getBookableSpaces({
|
Future<PaginatedBookableSpaces> getBookableSpaces({
|
||||||
required int page,
|
required LoadBookableSpacesParam param,
|
||||||
required int size,
|
|
||||||
required String search,
|
|
||||||
}) {
|
}) {
|
||||||
_debounceTimer?.cancel();
|
_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>();
|
final completer = Completer<PaginatedBookableSpaces>();
|
||||||
_lastCompleter = completer;
|
_lastCompleter = completer;
|
||||||
|
|
||||||
_lastPage = page;
|
|
||||||
_lastSize = size;
|
|
||||||
_lastSearch = search;
|
|
||||||
|
|
||||||
_debounceTimer = Timer(debounceDuration, () async {
|
_debounceTimer = Timer(debounceDuration, () async {
|
||||||
try {
|
try {
|
||||||
final result = await _inner.getBookableSpaces(
|
final result = await _inner.getBookableSpaces(param: param);
|
||||||
page: _lastPage!,
|
|
||||||
size: _lastSize!,
|
|
||||||
search: _lastSearch!,
|
|
||||||
);
|
|
||||||
if (!completer.isCompleted) {
|
if (!completer.isCompleted) {
|
||||||
completer.complete(result);
|
completer.complete(result);
|
||||||
}
|
}
|
@ -2,11 +2,12 @@ import 'package:bloc/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_event.dart';
|
||||||
import 'date_selection_state.dart';
|
import 'date_selection_state.dart';
|
||||||
|
|
||||||
|
|
||||||
class DateSelectionBloc extends Bloc<DateSelectionEvent, DateSelectionState> {
|
class DateSelectionBloc extends Bloc<DateSelectionEvent, DateSelectionState> {
|
||||||
DateSelectionBloc() : super(DateSelectionState.initial()) {
|
DateSelectionBloc() : super(DateSelectionState.initial()) {
|
||||||
on<SelectDate>((event, emit) {
|
on<SelectDate>((event, emit) {
|
||||||
final newWeekStart = _getStartOfWeek(event.selectedDate);
|
final newWeekStart = _getStartOfWeek(event.selectedDate);
|
||||||
emit(DateSelectionState(
|
emit(state.copyWith(
|
||||||
selectedDate: event.selectedDate,
|
selectedDate: event.selectedDate,
|
||||||
weekStart: newWeekStart,
|
weekStart: newWeekStart,
|
||||||
));
|
));
|
||||||
@ -14,19 +15,21 @@ class DateSelectionBloc extends Bloc<DateSelectionEvent, DateSelectionState> {
|
|||||||
|
|
||||||
on<NextWeek>((event, emit) {
|
on<NextWeek>((event, emit) {
|
||||||
final newWeekStart = state.weekStart.add(const Duration(days: 7));
|
final newWeekStart = state.weekStart.add(const Duration(days: 7));
|
||||||
final inNewWeek = state.selectedDate
|
emit(state.copyWith(
|
||||||
.isAfter(newWeekStart.subtract(const Duration(days: 1))) &&
|
|
||||||
state.selectedDate
|
|
||||||
.isBefore(newWeekStart.add(const Duration(days: 7)));
|
|
||||||
emit(DateSelectionState(
|
|
||||||
selectedDate: state.selectedDate,
|
|
||||||
weekStart: newWeekStart,
|
weekStart: newWeekStart,
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
on<PreviousWeek>((event, emit) {
|
on<PreviousWeek>((event, emit) {
|
||||||
emit(DateSelectionState(
|
final newWeekStart = state.weekStart.subtract(const Duration(days: 7));
|
||||||
selectedDate: state.selectedDate!.subtract(const Duration(days: 7)),
|
emit(state.copyWith(
|
||||||
weekStart: state.weekStart.subtract(const Duration(days: 7)),
|
weekStart: newWeekStart,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
on<SelectDateFromSidebarCalendar>((event, emit) {
|
||||||
|
emit(state.copyWith(
|
||||||
|
selectedDateFromSideBarCalender: event.selectedDate,
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -10,4 +10,9 @@ class SelectDate extends DateSelectionEvent {
|
|||||||
|
|
||||||
class NextWeek extends DateSelectionEvent {}
|
class NextWeek extends DateSelectionEvent {}
|
||||||
|
|
||||||
class PreviousWeek extends DateSelectionEvent {}
|
class PreviousWeek extends DateSelectionEvent {}
|
||||||
|
|
||||||
|
class SelectDateFromSidebarCalendar extends DateSelectionEvent {
|
||||||
|
final DateTime selectedDate;
|
||||||
|
SelectDateFromSidebarCalendar(this.selectedDate);
|
||||||
|
}
|
@ -1,21 +1,34 @@
|
|||||||
|
|
||||||
class DateSelectionState {
|
class DateSelectionState {
|
||||||
final DateTime selectedDate;
|
final DateTime selectedDate;
|
||||||
final DateTime weekStart;
|
final DateTime weekStart;
|
||||||
|
final DateTime? selectedDateFromSideBarCalender;
|
||||||
|
|
||||||
const DateSelectionState({
|
DateSelectionState({
|
||||||
required this.selectedDate,
|
required this.selectedDate,
|
||||||
required this.weekStart,
|
required this.weekStart,
|
||||||
|
this.selectedDateFromSideBarCalender,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory DateSelectionState.initial() {
|
factory DateSelectionState.initial() {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
|
final weekStart = now.subtract(Duration(days: now.weekday - 1));
|
||||||
return DateSelectionState(
|
return DateSelectionState(
|
||||||
selectedDate: now,
|
selectedDate: now,
|
||||||
weekStart: _getStartOfWeek(now),
|
weekStart: weekStart,
|
||||||
|
selectedDateFromSideBarCalender: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DateTime _getStartOfWeek(DateTime date) {
|
DateSelectionState copyWith({
|
||||||
return date.subtract(Duration(days: date.weekday - 1));
|
DateTime? selectedDate,
|
||||||
|
DateTime? weekStart,
|
||||||
|
DateTime? selectedDateFromSideBarCalender,
|
||||||
|
}) {
|
||||||
|
return DateSelectionState(
|
||||||
|
selectedDate: selectedDate ?? this.selectedDate,
|
||||||
|
weekStart: weekStart ?? this.weekStart,
|
||||||
|
selectedDateFromSideBarCalender: selectedDateFromSideBarCalender ?? this.selectedDateFromSideBarCalender,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
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/domain/load_bookable_spaces_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/services/bookable_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_event.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/sidebar/sidebar_state.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/sidebar/sidebar_state.dart';
|
||||||
|
|
||||||
class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
||||||
final BookingSystemService _bookingService;
|
final BookableSystemService _bookingService;
|
||||||
Timer? _searchDebounce;
|
|
||||||
int _currentPage = 1;
|
int _currentPage = 1;
|
||||||
final int _pageSize = 20;
|
final int _pageSize = 20;
|
||||||
String _currentSearch = '';
|
String _currentSearch = '';
|
||||||
@ -35,9 +35,11 @@ class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
|||||||
_currentSearch = '';
|
_currentSearch = '';
|
||||||
|
|
||||||
final paginatedSpaces = await _bookingService.getBookableSpaces(
|
final paginatedSpaces = await _bookingService.getBookableSpaces(
|
||||||
page: _currentPage,
|
param: LoadBookableSpacesParam(
|
||||||
size: _pageSize,
|
page: _currentPage,
|
||||||
search: _currentSearch,
|
size: _pageSize,
|
||||||
|
search: _currentSearch,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
@ -67,9 +69,11 @@ class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
|||||||
_currentPage++;
|
_currentPage++;
|
||||||
|
|
||||||
final paginatedSpaces = await _bookingService.getBookableSpaces(
|
final paginatedSpaces = await _bookingService.getBookableSpaces(
|
||||||
page: _currentPage,
|
param: LoadBookableSpacesParam(
|
||||||
size: _pageSize,
|
page: _currentPage,
|
||||||
search: _currentSearch,
|
size: _pageSize,
|
||||||
|
search: _currentSearch,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final updatedRooms = [...state.allRooms, ...paginatedSpaces.data];
|
final updatedRooms = [...state.allRooms, ...paginatedSpaces.data];
|
||||||
@ -79,6 +83,7 @@ class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
|||||||
displayedRooms: updatedRooms,
|
displayedRooms: updatedRooms,
|
||||||
isLoadingMore: false,
|
isLoadingMore: false,
|
||||||
hasMore: paginatedSpaces.hasNext,
|
hasMore: paginatedSpaces.hasNext,
|
||||||
|
totalPages: paginatedSpaces.totalPage,
|
||||||
currentPage: _currentPage,
|
currentPage: _currentPage,
|
||||||
));
|
));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -99,11 +104,12 @@ class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
|||||||
_currentPage = 1;
|
_currentPage = 1;
|
||||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
final paginatedSpaces = await _bookingService.getBookableSpaces(
|
final paginatedSpaces = await _bookingService.getBookableSpaces(
|
||||||
page: _currentPage,
|
param: LoadBookableSpacesParam(
|
||||||
size: _pageSize,
|
page: _currentPage,
|
||||||
search: _currentSearch,
|
size: _pageSize,
|
||||||
|
search: _currentSearch,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
allRooms: paginatedSpaces.data,
|
allRooms: paginatedSpaces.data,
|
||||||
displayedRooms: paginatedSpaces.data,
|
displayedRooms: paginatedSpaces.data,
|
||||||
@ -137,7 +143,6 @@ class SidebarBloc extends Bloc<SidebarEvent, SidebarState> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
_searchDebounce?.cancel();
|
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,6 +120,9 @@ class _BookingPageState extends State<BookingPage> {
|
|||||||
context
|
context
|
||||||
.read<DateSelectionBloc>()
|
.read<DateSelectionBloc>()
|
||||||
.add(SelectDate(newDate));
|
.add(SelectDate(newDate));
|
||||||
|
context
|
||||||
|
.read<DateSelectionBloc>()
|
||||||
|
.add(SelectDateFromSidebarCalendar(newDate));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -227,6 +230,10 @@ class _BookingPageState extends State<BookingPage> {
|
|||||||
weekStart: dateState.weekStart,
|
weekStart: dateState.weekStart,
|
||||||
selectedDate: dateState.selectedDate,
|
selectedDate: dateState.selectedDate,
|
||||||
eventController: _eventController,
|
eventController: _eventController,
|
||||||
|
selectedDateFromSideBarCalender: context
|
||||||
|
.watch<DateSelectionBloc>()
|
||||||
|
.state
|
||||||
|
.selectedDateFromSideBarCalender,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import 'dart:async';
|
|
||||||
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/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/data/services/bookable_spaces_service.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/data/services/remote_bookable_spaces_service.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/bookable_room.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/bookable_room.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/sidebar/sidebar_bloc.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/sidebar/sidebar_bloc.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_event.dart';
|
||||||
@ -23,7 +22,7 @@ class BookingSidebar extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => SidebarBloc(BookableSpacesService(
|
create: (context) => SidebarBloc(RemoteBookableSpacesService(
|
||||||
HTTPService(),
|
HTTPService(),
|
||||||
))
|
))
|
||||||
..add(LoadBookableSpaces()),
|
..add(LoadBookableSpaces()),
|
||||||
@ -46,7 +45,6 @@ class _SidebarContent extends StatefulWidget {
|
|||||||
class __SidebarContentState extends State<_SidebarContent> {
|
class __SidebarContentState extends State<_SidebarContent> {
|
||||||
final TextEditingController searchController = TextEditingController();
|
final TextEditingController searchController = TextEditingController();
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
Timer? _searchDebounce;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -57,7 +55,6 @@ class __SidebarContentState extends State<_SidebarContent> {
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_scrollController.dispose();
|
_scrollController.dispose();
|
||||||
_searchDebounce?.cancel();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,10 +66,7 @@ class __SidebarContentState extends State<_SidebarContent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handleSearch(String value) {
|
void _handleSearch(String value) {
|
||||||
_searchDebounce?.cancel();
|
context.read<SidebarBloc>().add(SearchRoomsEvent(value));
|
||||||
_searchDebounce = Timer(const Duration(milliseconds: 300), () {
|
|
||||||
context.read<SidebarBloc>().add(SearchRoomsEvent(value));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -9,6 +9,7 @@ class WeeklyCalendarPage extends StatelessWidget {
|
|||||||
final EventController eventController;
|
final EventController eventController;
|
||||||
final String? startTime;
|
final String? startTime;
|
||||||
final String? endTime;
|
final String? endTime;
|
||||||
|
final DateTime? selectedDateFromSideBarCalender;
|
||||||
|
|
||||||
const WeeklyCalendarPage({
|
const WeeklyCalendarPage({
|
||||||
super.key,
|
super.key,
|
||||||
@ -17,37 +18,61 @@ class WeeklyCalendarPage extends StatelessWidget {
|
|||||||
required this.eventController,
|
required this.eventController,
|
||||||
this.startTime,
|
this.startTime,
|
||||||
this.endTime,
|
this.endTime,
|
||||||
|
this.selectedDateFromSideBarCalender,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final startHour = _parseHour(startTime, defaultValue: 0);
|
final startHour = _parseHour(startTime, defaultValue: 0);
|
||||||
final endHour = _parseHour(endTime, defaultValue: 24);
|
final endHour = _parseHour(endTime, defaultValue: 24);
|
||||||
|
|
||||||
if (endTime == null || endTime!.isEmpty) {
|
if (endTime == null || endTime!.isEmpty) {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: Text(
|
child: Column(
|
||||||
'Please select a bookable space to view the calendar.',
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.calendar_today,
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
size: 80,
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
'Please select a bookable space to view the calendar.',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: ColorsManager.lightGrayColor),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final weekDays = _getWeekDays(weekStart);
|
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 = 80;
|
||||||
|
const int totalDays = 7;
|
||||||
|
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
final double calendarWidth = constraints.maxWidth;
|
final double calendarWidth = constraints.maxWidth;
|
||||||
const double timeLineWidth = 80;
|
|
||||||
const int totalDays = 7;
|
|
||||||
final double dayColumnWidth =
|
final double dayColumnWidth =
|
||||||
(calendarWidth - timeLineWidth) / totalDays;
|
(calendarWidth - timeLineWidth) / totalDays - 0.1;
|
||||||
final selectedDayIndex =
|
|
||||||
weekDays.indexWhere((d) => isSameDay(d, selectedDate));
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 25.0, right: 25.0, top: 25),
|
padding: const EdgeInsets.only(left: 25.0, right: 25.0, top: 25),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
WeekView(
|
WeekView(
|
||||||
|
pageViewPhysics: const NeverScrollableScrollPhysics(),
|
||||||
key: ValueKey(weekStart),
|
key: ValueKey(weekStart),
|
||||||
controller: eventController,
|
controller: eventController,
|
||||||
initialDay: weekStart,
|
initialDay: weekStart,
|
||||||
@ -63,47 +88,29 @@ class WeeklyCalendarPage extends StatelessWidget {
|
|||||||
height: 0,
|
height: 0,
|
||||||
),
|
),
|
||||||
weekDayBuilder: (date) {
|
weekDayBuilder: (date) {
|
||||||
final weekDays = _getWeekDays(weekStart);
|
|
||||||
final selectedDayIndex =
|
|
||||||
weekDays.indexWhere((d) => isSameDay(d, selectedDate));
|
|
||||||
final index = weekDays.indexWhere((d) => isSameDay(d, date));
|
final index = weekDays.indexWhere((d) => isSameDay(d, date));
|
||||||
final isSelectedDay = index == selectedDayIndex;
|
final isSelectedDay = index == selectedDayIndex;
|
||||||
final isToday = isSameDay(date, DateTime.now());
|
return Column(
|
||||||
|
children: [
|
||||||
return Container(
|
Text(
|
||||||
decoration: isSelectedDay
|
DateFormat('EEE').format(date).toUpperCase(),
|
||||||
? BoxDecoration(
|
style: TextStyle(
|
||||||
color: ColorsManager.blue1.withOpacity(0.2),
|
fontWeight: FontWeight.w400,
|
||||||
borderRadius: BorderRadius.circular(6),
|
fontSize: 14,
|
||||||
)
|
color: isSelectedDay ? Colors.blue : Colors.black,
|
||||||
: isToday
|
|
||||||
? BoxDecoration(
|
|
||||||
color: ColorsManager.blue1.withOpacity(0.08),
|
|
||||||
borderRadius: BorderRadius.circular(6),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
DateFormat('EEE').format(date).toUpperCase(),
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
fontSize: 14,
|
|
||||||
color: isSelectedDay ? Colors.blue : Colors.black,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
DateFormat('d').format(date),
|
Text(
|
||||||
style: TextStyle(
|
DateFormat('d').format(date),
|
||||||
fontWeight: FontWeight.w700,
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontWeight: FontWeight.w700,
|
||||||
color: isSelectedDay
|
fontSize: 20,
|
||||||
? ColorsManager.blue1
|
color: isSelectedDay
|
||||||
: ColorsManager.blackColor,
|
? ColorsManager.blue1
|
||||||
),
|
: ColorsManager.blackColor,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
timeLineBuilder: (date) {
|
timeLineBuilder: (date) {
|
||||||
@ -149,12 +156,22 @@ class WeeklyCalendarPage extends StatelessWidget {
|
|||||||
timeLineWidth: timeLineWidth,
|
timeLineWidth: timeLineWidth,
|
||||||
weekPageHeaderBuilder: (start, end) => Container(),
|
weekPageHeaderBuilder: (start, end) => Container(),
|
||||||
weekTitleHeight: 60,
|
weekTitleHeight: 60,
|
||||||
weekNumberBuilder: (firstDayOfWeek) => Text(
|
weekNumberBuilder: (firstDayOfWeek) => Padding(
|
||||||
firstDayOfWeek.timeZoneName,
|
padding: const EdgeInsets.only(right: 15, bottom: 10),
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
child: Column(
|
||||||
color: Colors.black,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
fontWeight: FontWeight.bold,
|
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) {
|
eventTileBuilder: (date, events, boundary, start, end) {
|
||||||
return Container(
|
return Container(
|
||||||
@ -171,9 +188,8 @@ class WeeklyCalendarPage extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(6),
|
padding: const EdgeInsets.all(6),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isEventEnded
|
color: isEventEnded
|
||||||
? ColorsManager.grayColor
|
? ColorsManager.lightGrayBorderColor
|
||||||
: ColorsManager.lightGrayColor
|
: ColorsManager.blue1.withOpacity(0.25),
|
||||||
.withOpacity(0.25),
|
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -206,15 +222,32 @@ class WeeklyCalendarPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
if (selectedDayIndex >= 0)
|
if (selectedDayIndex >= 0)
|
||||||
Positioned(
|
Positioned(
|
||||||
left: timeLineWidth + dayColumnWidth * selectedDayIndex,
|
left: (timeLineWidth + 3) +
|
||||||
|
(dayColumnWidth - 8) * (selectedDayIndex - 0.01),
|
||||||
top: 0,
|
top: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
width: dayColumnWidth,
|
width: dayColumnWidth,
|
||||||
child: IgnorePointer(
|
child: IgnorePointer(
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.symmetric(
|
margin: const EdgeInsets.symmetric(
|
||||||
vertical: 0, horizontal: 2),
|
vertical: 0, horizontal: 4),
|
||||||
color: ColorsManager.blue1.withOpacity(0.1),
|
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),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -253,7 +286,6 @@ int _parseHour(String? time, {required int defaultValue}) {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return int.parse(time.split(':')[0]);
|
return int.parse(time.split(':')[0]);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user