Compare commits

..

6 Commits

Author SHA1 Message Date
645a07287e Refactor date selection: add SelectDateFromSidebarCalendar event and update state management for improved clarity 2025-07-10 14:15:57 +03:00
e55e793081 Implement-Calendar-ui (#342)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->



## Description

<!--- Describe your changes in detail -->
Implement Calendar ui

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [x]  New feature (non-breaking change which adds functionality)
- [ ] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore
2025-07-10 12:13:40 +03:00
885ef61114 Refactor booking system: replace DebouncedBookingSystemService with DebouncedBookableSpacesService and streamline search handling 2025-07-10 12:10:28 +03:00
bfd6b5c3a0 Refactor booking system: replace BookingSystemService with BookableSystemService and update parameter handling for improved clarity 2025-07-10 11:25:35 +03:00
2b638940ae Refactor booking system: enhance parameter handling for improved clarity and maintainability 2025-07-10 11:14:30 +03:00
3e95bf4473 Refactor booking system: replace individual parameters with LoadBookableSpacesParam for improved clarity and maintainability 2025-07-10 10:56:10 +03:00
12 changed files with 230 additions and 146 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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