Compare commits

..

1 Commits

24 changed files with 505 additions and 520 deletions

View File

@ -1,9 +1,22 @@
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:syncrow_web/firebase_options.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/services/locator.dart';
import 'package:syncrow_web/syncrow_app.dart';
import 'package:syncrow_web/utils/app_routes.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/theme/theme.dart';
Future<void> main() async {
try {
@ -20,5 +33,59 @@ Future<void> main() async {
);
initialSetup();
} catch (_) {}
runApp(const SyncrowApp());
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
final GoRouter _router = GoRouter(
initialLocation: RoutesConst.auth,
routes: AppRoutes.getRoutes(),
redirect: (context, state) async {
final checkToken = await AuthBloc.getTokenAndValidate();
final loggedIn = checkToken == 'Success';
final goingToLogin = state.uri.toString() == RoutesConst.auth;
if (!loggedIn && !goingToLogin) return RoutesConst.auth;
if (loggedIn && goingToLogin) return RoutesConst.home;
return null;
},
);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<CreateRoutineBloc>(
create: (context) => CreateRoutineBloc(),
),
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(),
),
BlocProvider<RoutineBloc>(
create: (context) => RoutineBloc(),
),
BlocProvider<SpaceTreeBloc>(
create: (context) => SpaceTreeBloc(),
),
],
child: MaterialApp.router(
debugShowCheckedModeBanner: false,
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown,
},
),
key: NavigationService.navigatorKey,
// scaffoldMessengerKey: NavigationService.snackbarKey,
theme: myTheme,
routerConfig: _router,
));
}
}

View File

@ -1,9 +1,22 @@
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:syncrow_web/firebase_options.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/services/locator.dart';
import 'package:syncrow_web/syncrow_app.dart';
import 'package:syncrow_web/utils/app_routes.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/theme/theme.dart';
Future<void> main() async {
try {
@ -20,5 +33,59 @@ Future<void> main() async {
);
initialSetup();
} catch (_) {}
runApp(const SyncrowApp());
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
final GoRouter _router = GoRouter(
initialLocation: RoutesConst.auth,
routes: AppRoutes.getRoutes(),
redirect: (context, state) async {
final checkToken = await AuthBloc.getTokenAndValidate();
final loggedIn = checkToken == 'Success';
final goingToLogin = state.uri.toString() == RoutesConst.auth;
if (!loggedIn && !goingToLogin) return RoutesConst.auth;
if (loggedIn && goingToLogin) return RoutesConst.home;
return null;
},
);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<CreateRoutineBloc>(
create: (context) => CreateRoutineBloc(),
),
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(),
),
BlocProvider<RoutineBloc>(
create: (context) => RoutineBloc(),
),
BlocProvider<SpaceTreeBloc>(
create: (context) => SpaceTreeBloc(),
),
],
child: MaterialApp.router(
debugShowCheckedModeBanner: false,
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown,
},
),
key: NavigationService.navigatorKey,
// scaffoldMessengerKey: NavigationService.snackbarKey,
theme: myTheme,
routerConfig: _router,
));
}
}

View File

@ -1,16 +1,26 @@
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:syncrow_web/firebase_options.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/services/locator.dart';
import 'package:syncrow_web/syncrow_app.dart';
import 'package:syncrow_web/utils/app_routes.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/theme/theme.dart';
Future<void> main() async {
try {
const environment = String.fromEnvironment(
'FLAVOR',
defaultValue: 'staging',
);
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'staging');
await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
@ -20,5 +30,59 @@ Future<void> main() async {
);
initialSetup();
} catch (_) {}
runApp(const SyncrowApp());
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
final GoRouter _router = GoRouter(
initialLocation: RoutesConst.auth,
routes: AppRoutes.getRoutes(),
redirect: (context, state) async {
final checkToken = await AuthBloc.getTokenAndValidate();
final loggedIn = checkToken == 'Success';
final goingToLogin = state.uri.toString() == RoutesConst.auth;
if (!loggedIn && !goingToLogin) return RoutesConst.auth;
if (loggedIn && goingToLogin) return RoutesConst.home;
return null;
},
);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<CreateRoutineBloc>(
create: (context) => CreateRoutineBloc(),
),
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(),
),
BlocProvider<RoutineBloc>(
create: (context) => RoutineBloc(),
),
BlocProvider<SpaceTreeBloc>(
create: (context) => SpaceTreeBloc(),
),
],
child: MaterialApp.router(
debugShowCheckedModeBanner: false,
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown,
},
),
key: NavigationService.navigatorKey,
// scaffoldMessengerKey: NavigationService.snackbarKey,
theme: myTheme,
routerConfig: _router,
));
}
}

View File

@ -10,7 +10,7 @@ part 'events_event.dart';
part 'events_state.dart';
class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
final EventController eventController = EventController();
EventController eventController = EventController();
final CalendarSystemService calendarService;
CalendarEventsBloc({
@ -20,7 +20,9 @@ class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
on<AddEvent>(_onAddEvent);
on<DisposeResources>(_onDisposeResources);
on<GoToWeek>(_onGoToWeek);
on<ResetEvents>(_onResetEvents);
}
Future<void> _onLoadEvents(
LoadEvents event,
Emitter<CalendarEventState> emit,
@ -126,4 +128,18 @@ class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
eventController.dispose();
return super.close();
}
void _onResetEvents(
ResetEvents event,
Emitter<CalendarEventState> emit,
) {
if (calendarService is MemoryCalendarServiceWithRemoteFallback) {
(calendarService as MemoryCalendarServiceWithRemoteFallback)
.memoryService
.clear();
}
eventController.dispose();
eventController = EventController();
emit(EventsInitial());
}
}

View File

@ -29,3 +29,10 @@ class CheckWeekHasEvents extends CalendarEventsEvent {
final DateTime weekStart;
const CheckWeekHasEvents(this.weekStart);
}
class ResetEvents extends CalendarEventsEvent {
const ResetEvents();
@override
List<Object?> get props => [];
}

View File

@ -1,3 +1,4 @@
// booking_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:calendar_view/calendar_view.dart';
@ -26,7 +27,33 @@ class BookingPage extends StatefulWidget {
}
class _BookingPageState extends State<BookingPage> {
late final EventController _eventController;
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(create: (_) => SelectedBookableSpaceBloc()),
BlocProvider(create: (_) => DateSelectionBloc()),
BlocProvider(
create: (_) => CalendarEventsBloc(
calendarService: MemoryCalendarServiceWithRemoteFallback(
remoteService: RemoteCalendarService(HTTPService()),
memoryService: MemoryCalendarService(),
),
),
),
],
child: _BookingPageContent(),
);
}
}
class _BookingPageContent extends StatefulWidget {
@override
State<_BookingPageContent> createState() => _BookingPageContentState();
}
class _BookingPageContentState extends State<_BookingPageContent> {
late EventController _eventController;
@override
void initState() {
@ -40,7 +67,7 @@ class _BookingPageState extends State<BookingPage> {
super.dispose();
}
void _dispatchLoadEvents(BuildContext context) {
void _loadEvents(BuildContext context) {
final selectedRoom =
context.read<SelectedBookableSpaceBloc>().state.selectedBookableSpace;
final dateState = context.read<DateSelectionBloc>().state;
@ -60,186 +87,170 @@ class _BookingPageState extends State<BookingPage> {
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(create: (_) => SelectedBookableSpaceBloc()),
BlocProvider(create: (_) => DateSelectionBloc()),
BlocProvider(
create: (_) => CalendarEventsBloc(
calendarService: MemoryCalendarServiceWithRemoteFallback(
remoteService: RemoteCalendarService(
HTTPService(),
),
memoryService: MemoryCalendarService(),
),
)),
],
child: Builder(
builder: (context) =>
BlocListener<CalendarEventsBloc, CalendarEventState>(
listenWhen: (prev, curr) => curr is EventsLoaded,
return BlocListener<SelectedBookableSpaceBloc, SelectedBookableSpaceState>(
listener: (context, state) {
if (state.selectedBookableSpace != null) {
// Reset events and clear cache when room changes
context.read<CalendarEventsBloc>().add(ResetEvents());
_loadEvents(context);
}
},
child: BlocListener<DateSelectionBloc, DateSelectionState>(
listener: (context, state) {
_loadEvents(context);
},
child: BlocListener<CalendarEventsBloc, CalendarEventState>(
listener: (context, state) {
if (state is EventsLoaded) {
_eventController.removeWhere((_) => true);
_eventController.addAll(state.events);
}
},
child: BlocListener<SelectedBookableSpaceBloc,
SelectedBookableSpaceState>(
listener: (context, state) => _dispatchLoadEvents(context),
child: BlocListener<DateSelectionBloc, DateSelectionState>(
listener: (context, state) => _dispatchLoadEvents(context),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
boxShadow: [
BoxShadow(
color: ColorsManager.blackColor.withOpacity(0.1),
offset: const Offset(3, 0),
blurRadius: 6,
spreadRadius: 0,
),
],
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
boxShadow: [
BoxShadow(
color: ColorsManager.blackColor.withOpacity(0.1),
offset: const Offset(3, 0),
blurRadius: 6,
spreadRadius: 0,
),
child: Column(
children: [
Expanded(
flex: 2,
child: BlocBuilder<SelectedBookableSpaceBloc,
SelectedBookableSpaceState>(
builder: (context, state) {
return BookingSidebar(
onRoomSelected: (selectedRoom) {
context
.read<SelectedBookableSpaceBloc>()
.add(SelectBookableSpace(selectedRoom));
},
);
},
),
),
Expanded(
child: BlocBuilder<DateSelectionBloc,
DateSelectionState>(
builder: (context, dateState) {
return CustomCalendarPage(
selectedDate: dateState.selectedDate,
onDateChanged: (day, month, year) {
final newDate = DateTime(year, month, day);
context
.read<DateSelectionBloc>()
.add(SelectDate(newDate));
context.read<DateSelectionBloc>().add(
SelectDateFromSidebarCalendar(newDate));
},
);
},
),
),
],
),
),
],
),
Expanded(
flex: 5,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
child: Column(
children: [
Expanded(
flex: 2,
child: BlocBuilder<SelectedBookableSpaceBloc,
SelectedBookableSpaceState>(
builder: (context, state) {
return BookingSidebar(
onRoomSelected: (selectedRoom) {
context
.read<SelectedBookableSpaceBloc>()
.add(SelectBookableSpace(selectedRoom));
},
);
},
),
),
Expanded(
child:
BlocBuilder<DateSelectionBloc, DateSelectionState>(
builder: (context, dateState) {
return CustomCalendarPage(
selectedDate: dateState.selectedDate,
onDateChanged: (day, month, year) {
final newDate = DateTime(year, month, day);
context
.read<DateSelectionBloc>()
.add(SelectDate(newDate));
context.read<DateSelectionBloc>().add(
SelectDateFromSidebarCalendar(newDate));
},
);
},
),
),
],
),
),
),
Expanded(
flex: 5,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
SvgTextButton(
svgAsset: Assets.homeIcon,
label: 'Manage Bookable Spaces',
onPressed: () {},
),
const SizedBox(width: 20),
SvgTextButton(
svgAsset: Assets.groupIcon,
label: 'Manage Users',
onPressed: () {},
),
],
SvgTextButton(
svgAsset: Assets.homeIcon,
label: 'Manage Bookable Spaces',
onPressed: () {},
),
BlocBuilder<DateSelectionBloc,
DateSelectionState>(
builder: (context, state) {
final weekStart = state.weekStart;
final weekEnd =
weekStart.add(const Duration(days: 6));
return WeekNavigation(
weekStart: weekStart,
weekEnd: weekEnd,
onPreviousWeek: () {
context
.read<DateSelectionBloc>()
.add(PreviousWeek());
},
onNextWeek: () {
context
.read<DateSelectionBloc>()
.add(NextWeek());
},
);
},
const SizedBox(width: 20),
SvgTextButton(
svgAsset: Assets.groupIcon,
label: 'Manage Users',
onPressed: () {},
),
],
),
Expanded(
flex: 5,
child: BlocBuilder<SelectedBookableSpaceBloc,
SelectedBookableSpaceState>(
builder: (context, roomState) {
final selectedRoom =
roomState.selectedBookableSpace;
return BlocBuilder<DateSelectionBloc,
DateSelectionState>(
builder: (context, dateState) {
return BlocListener<CalendarEventsBloc,
CalendarEventState>(
listenWhen: (prev, curr) =>
curr is EventsLoaded,
listener: (context, state) {
if (state is EventsLoaded) {
_eventController
.removeWhere((_) => true);
_eventController.addAll(state.events);
}
},
child: WeeklyCalendarPage(
startTime: selectedRoom
?.bookableConfig.startTime,
endTime: selectedRoom
?.bookableConfig.endTime,
weekStart: dateState.weekStart,
selectedDate: dateState.selectedDate,
eventController: _eventController,
selectedDateFromSideBarCalender: context
.watch<DateSelectionBloc>()
.state
.selectedDateFromSideBarCalender,
),
BlocBuilder<DateSelectionBloc, DateSelectionState>(
builder: (context, state) {
final weekStart = state.weekStart;
final weekEnd =
weekStart.add(const Duration(days: 6));
return WeekNavigation(
weekStart: weekStart,
weekEnd: weekEnd,
onPreviousWeek: () {
context
.read<DateSelectionBloc>()
.add(PreviousWeek());
},
onNextWeek: () {
context
.read<DateSelectionBloc>()
.add(NextWeek());
},
);
},
),
],
),
const SizedBox(height: 20),
Expanded(
flex: 5,
child: BlocBuilder<SelectedBookableSpaceBloc,
SelectedBookableSpaceState>(
builder: (context, roomState) {
final selectedRoom =
roomState.selectedBookableSpace;
return BlocBuilder<DateSelectionBloc,
DateSelectionState>(
builder: (context, dateState) {
return BlocBuilder<CalendarEventsBloc,
CalendarEventState>(
builder: (context, eventState) {
return WeeklyCalendarPage(
key: ValueKey(
selectedRoom?.uuid ?? 'no-room'),
startTime: selectedRoom
?.bookableConfig.startTime,
endTime:
selectedRoom?.bookableConfig.endTime,
weekStart: dateState.weekStart,
selectedDate: dateState.selectedDate,
eventController: _eventController,
selectedDateFromSideBarCalender: context
.watch<DateSelectionBloc>()
.state
.selectedDateFromSideBarCalender,
// isLoading: eventState is EventsLoading,
);
},
);
},
),
),
],
);
},
),
),
),
],
),
],
),
),
),
],
),
),
),

View File

@ -76,21 +76,29 @@ class __SidebarContentState extends State<_SidebarContent> {
builder: (context, state) {
return Column(
children: [
const _SidebarHeader(title: 'Spaces'),
Padding(
padding: const EdgeInsets.only(top: 10.0, bottom: 10.0),
child: Container(
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
boxShadow: [
BoxShadow(
color: ColorsManager.blackColor.withOpacity(0.1),
offset: const Offset(0, 4),
blurRadius: 4,
spreadRadius: 0,
),
],
),
child: _SidebarHeader(title: 'Spaces')),
),
Container(
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
borderRadius: BorderRadius.circular(8.0),
boxShadow: [
BoxShadow(
color: ColorsManager.blackColor.withOpacity(0.1),
offset: const Offset(0, -2),
blurRadius: 4,
spreadRadius: 0,
),
BoxShadow(
color: ColorsManager.blackColor.withOpacity(0.1),
offset: const Offset(0, 2),
offset: const Offset(0, 4),
blurRadius: 4,
spreadRadius: 0,
),
@ -220,7 +228,7 @@ class _SidebarHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [

View File

@ -66,18 +66,28 @@ class _CustomCalendarPageState extends State<CustomCalendarPage> {
weekdayLabels: const ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'],
);
return CalendarDatePicker2(
config: config,
value: [_selectedDate],
onValueChanged: (dates) {
final picked = dates.first;
if (picked != null) {
setState(() {
_selectedDate = picked;
});
widget.onDateChanged(picked.day, picked.month, picked.year);
}
},
return Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
),
),
child: CalendarDatePicker2(
config: config,
value: [_selectedDate],
onValueChanged: (dates) {
final picked = dates.first;
if (picked != null) {
setState(() {
_selectedDate = picked;
});
widget.onDateChanged(picked.day, picked.month, picked.year);
}
},
),
);
}
}

View File

@ -32,15 +32,17 @@ class EventTileWidget extends StatelessWidget {
return Expanded(
child: Container(
width: double.infinity,
padding: EdgeInsets.all(5),
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: isEventEnded
? ColorsManager.grayColor.withOpacity(0.1)
: ColorsManager.blue1.withOpacity(0.1),
borderRadius: BorderRadius.circular(6),
border: const Border(
border: Border(
left: BorderSide(
color: ColorsManager.grayColor,
color: isEventEnded
? ColorsManager.grayColor
: ColorsManager.secondaryColor,
width: 4,
),
),

View File

@ -21,7 +21,7 @@ class RoomListItem extends StatelessWidget {
groupValue: isSelected ? room.uuid : null,
visualDensity: const VisualDensity(vertical: -4),
onChanged: (value) => onTap(),
activeColor: ColorsManager.primaryColor,
activeColor: ColorsManager.secondaryColor,
title: Text(
room.spaceName,
maxLines: 2,

View File

@ -14,26 +14,33 @@ class WeekDayHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
DateFormat('EEE').format(date).toUpperCase(),
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: 14,
color: isSelectedDay ? Colors.blue : Colors.black,
return ColoredBox(
color: isSelectedDay
? ColorsManager.secondaryColor.withOpacity(0.1)
: Colors.transparent,
child: Column(
children: [
const SizedBox(
height: 10,
),
),
Text(
DateFormat('d').format(date),
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 20,
color:
isSelectedDay ? ColorsManager.blue1 : ColorsManager.blackColor,
Text(
DateFormat('EEE').format(date).toUpperCase(),
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 14,
color: ColorsManager.blackColor,
),
),
),
],
Text(
DateFormat('d').format(date),
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 30,
color: ColorsManager.blackColor,
),
),
],
),
);
}
}

View File

@ -24,7 +24,7 @@ class WeeklyCalendarPage extends StatelessWidget {
});
static const double timeLineWidth = 65;
static const int totalDays = 7;
static const double dayColumnWidth = 220;
static const double dayColumnWidth = 220;
final double calendarContentWidth =
timeLineWidth + (totalDays * dayColumnWidth);
@ -57,13 +57,10 @@ class WeeklyCalendarPage extends StatelessWidget {
);
}
const double timeLineWidth = 65;
const double timeLineWidth = 90;
return LayoutBuilder(
builder: (context, constraints) {
bool isInRange(DateTime date, DateTime start, DateTime end) {
!date.isBefore(start) && !date.isAfter(end);
// remove this line and Check if the date is within the range
@ -100,7 +97,6 @@ class WeeklyCalendarPage extends StatelessWidget {
width: width,
decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.13),
borderRadius: BorderRadius.circular(8),
),
);
} else if (isSelected) {
@ -110,7 +106,6 @@ class WeeklyCalendarPage extends StatelessWidget {
decoration: BoxDecoration(
color:
ColorsManager.spaceColor.withOpacity(0.07),
borderRadius: BorderRadius.circular(8),
),
);
}
@ -143,7 +138,7 @@ class WeeklyCalendarPage extends StatelessWidget {
heightPerMinute: 1.7,
showLiveTimeLineInAllDays: false,
showVerticalLines: true,
emulateVerticalOffsetBy: -80,
emulateVerticalOffsetBy: -95,
startDay: WeekDays.monday,
liveTimeIndicatorSettings:
const LiveTimeIndicatorSettings(
@ -161,7 +156,7 @@ class WeeklyCalendarPage extends StatelessWidget {
},
timeLineWidth: timeLineWidth,
weekPageHeaderBuilder: (start, end) => Container(),
weekTitleHeight: 60,
weekTitleHeight: 90,
weekNumberBuilder: (firstDayOfWeek) => Padding(
padding: const EdgeInsets.only(right: 15, bottom: 10),
child: Column(
@ -208,8 +203,6 @@ class WeeklyCalendarPage extends StatelessWidget {
},
);
}
}
bool isSameDay(DateTime d1, DateTime d2) {

View File

@ -9,8 +9,6 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/presen
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/products/data/services/remote_products_service.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/products/presentation/bloc/products_bloc.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/data/services/remote_reorder_spaces_service.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_bloc.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/data/services/unique_space_details_spaces_decorator_service.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart';
@ -27,16 +25,15 @@ class SpaceManagementPage extends StatefulWidget {
class _SpaceManagementPageState extends State<SpaceManagementPage> {
late final CommunitiesBloc communitiesBloc;
late final HTTPService _httpService;
@override
void initState() {
_httpService = HTTPService();
communitiesBloc = CommunitiesBloc(
communitiesService: DebouncedCommunitiesService(
RemoteCommunitiesService(_httpService),
RemoteCommunitiesService(HTTPService()),
),
)..add(const LoadCommunities(LoadCommunitiesParam()));
super.initState();
}
@ -53,18 +50,13 @@ class _SpaceManagementPageState extends State<SpaceManagementPage> {
BlocProvider(
create: (context) => SpaceDetailsBloc(
UniqueSpaceDetailsSpacesDecoratorService(
RemoteSpaceDetailsService(httpService: _httpService),
RemoteSpaceDetailsService(httpService: HTTPService()),
),
),
),
BlocProvider(
create: (context) => ProductsBloc(
RemoteProductsService(_httpService),
),
),
BlocProvider(
create: (context) => ReorderSpacesBloc(
RemoteReorderSpacesService(_httpService),
RemoteProductsService(HTTPService()),
),
),
],

View File

@ -11,8 +11,6 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/presentation/bloc/reorder_spaces_bloc.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -166,16 +164,6 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
context.read<CommunitiesBloc>().add(
CommunitiesUpdateCommunity(newCommunity),
);
context.read<ReorderSpacesBloc>().add(
ReorderSpacesEvent(
ReorderSpacesParam(
communityUuid: widget.community.uuid,
parentSpaceUuid: data.parent?.uuid ?? '',
spaces: children,
),
),
);
}
void _onSpaceTapped(SpaceModel? space) {
@ -257,13 +245,6 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
final levelXOffset = <int, double>{};
_calculateLayout(community.spaces, 0, levelXOffset);
const horizontalCanvasPadding = 100.0;
final originalPositions = Map.of(_positions);
_positions.clear();
for (final entry in originalPositions.entries) {
_positions[entry.key] = entry.value.translate(horizontalCanvasPadding, 0);
}
final selectedSpace = widget.selectedSpace;
final highlightedUuids = <String>{};
if (selectedSpace != null) {
@ -281,7 +262,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
community: widget.community,
);
final createButtonX = (levelXOffset[0] ?? 0.0) + horizontalCanvasPadding;
final createButtonX = levelXOffset[0] ?? 0.0;
const createButtonY = 0.0;
widgets.add(
@ -313,12 +294,10 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
CommunityModel? community,
SpaceModel? parent,
}) {
const targetWidth = 40.0;
final padding = (_horizontalSpacing - targetWidth) / 2;
if (spaces.isNotEmpty) {
final firstChildPos = _positions[spaces.first.uuid]!;
final targetPos = Offset(
firstChildPos.dx - padding - targetWidth,
firstChildPos.dx - (_horizontalSpacing / 4),
firstChildPos.dy,
);
widgets.add(_buildDropTarget(parent, community, 0, targetPos));
@ -400,7 +379,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
);
final targetPos = Offset(
position.dx + cardWidth + padding,
position.dx + cardWidth + (_horizontalSpacing / 4) - 20,
position.dy,
);
widgets.add(_buildDropTarget(parent, community, i + 1, targetPos));
@ -435,33 +414,24 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
child: DragTarget<SpaceReorderDataModel>(
builder: (context, candidateData, rejectedData) {
if (_draggedData == null) {
return const SizedBox.shrink();
return const SizedBox();
}
final children = parent?.children ?? community?.spaces ?? [];
final isSameParent = (_draggedData!.parent?.uuid == parent?.uuid &&
_draggedData!.community == null) ||
(_draggedData!.community?.uuid == community?.uuid &&
_draggedData!.parent == null);
final isTargetForDragged = (_draggedData?.parent?.uuid == parent?.uuid &&
_draggedData?.community == null) ||
(_draggedData?.community?.uuid == community?.uuid &&
_draggedData?.parent == null);
if (!isSameParent) {
return const SizedBox.shrink();
if (!isTargetForDragged) {
return const SizedBox();
}
final oldIndex =
children.indexWhere((s) => s.uuid == _draggedData!.space.uuid);
if (oldIndex != -1 && (oldIndex == index || oldIndex == index - 1)) {
return const SizedBox.shrink();
}
return AnimatedContainer(
duration: const Duration(milliseconds: 150),
return Container(
width: 40,
alignment: Alignment.center,
height: _cardHeight,
decoration: BoxDecoration(
color: context.theme.colorScheme.primary.withValues(
alpha: candidateData.isNotEmpty ? 0.9 : 0.3,
alpha: candidateData.isNotEmpty ? 0.7 : 0.3,
),
borderRadius: BorderRadius.circular(8),
),
@ -484,9 +454,6 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
final oldIndex =
children.indexWhere((s) => s.uuid == data.data.space.uuid);
if (oldIndex == -1) {
return true;
}
if (oldIndex == index || oldIndex == index - 1) {
return false;
}
@ -514,7 +481,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
child: SizedBox(
width: context.screenWidth * 5,
height: context.screenHeight * 5,
child: Stack(clipBehavior: Clip.none, children: treeWidgets),
child: Stack(children: treeWidgets),
),
),
);

View File

@ -1,58 +0,0 @@
import 'package:dio/dio.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/services/reorder_spaces_service.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';
final class RemoteReorderSpacesService implements ReorderSpacesService {
RemoteReorderSpacesService(this._httpClient);
final HTTPService _httpClient;
@override
Future<void> reorderSpaces(ReorderSpacesParam param) async {
try {
await _httpClient.post(
path: await _makeUrl(param),
body: param.toJson(),
expectedResponseModel: (json) => json,
);
} on DioException catch (e) {
final message = e.response?.data as Map<String, dynamic>?;
throw APIException(_getErrorMessageFromBody(message));
} catch (e) {
throw APIException(e.toString());
}
}
String _getErrorMessageFromBody(Map<String, dynamic>? body) {
if (body == null) return 'Failed to delete space';
final error = body['error'] as Map<String, dynamic>?;
final errorMessage = error?['message'] as String? ?? '';
return errorMessage;
}
Future<String> _makeUrl(ReorderSpacesParam param) async {
final projectUuid = await ProjectManager.getProjectUUID();
final communityUuid = param.communityUuid;
if (projectUuid == null || projectUuid.isEmpty) {
throw APIException('Project UUID is not set');
}
if (communityUuid.isEmpty) {
throw APIException('Community UUID is not set');
}
if (param.parentSpaceUuid.isEmpty) {
throw APIException('Parent Space UUID is not set');
}
return ApiEndpoints.reorderSpaces
.replaceAll('{projectUuid}', projectUuid)
.replaceAll('{communityUuid}', communityUuid)
.replaceAll('{parentSpaceUuid}', param.parentSpaceUuid);
}
}

View File

@ -1,21 +0,0 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
class ReorderSpacesParam extends Equatable {
const ReorderSpacesParam({
required this.communityUuid,
required this.parentSpaceUuid,
required this.spaces,
});
final String communityUuid;
final String parentSpaceUuid;
final List<SpaceModel> spaces;
@override
List<Object?> get props => [spaces, communityUuid, parentSpaceUuid];
Map<String, dynamic> toJson() => {
'spacesUuids': spaces.map((space) => space.uuid).toList(),
};
}

View File

@ -1,5 +0,0 @@
import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart';
abstract interface class ReorderSpacesService {
Future<void> reorderSpaces(ReorderSpacesParam param);
}

View File

@ -1,35 +0,0 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/params/reorder_spaces_param.dart';
import 'package:syncrow_web/pages/space_management_v2/modules/reorder_spaces/domain/services/reorder_spaces_service.dart';
import 'package:syncrow_web/services/api/api_exception.dart';
part 'reorder_spaces_event.dart';
part 'reorder_spaces_state.dart';
class ReorderSpacesBloc extends Bloc<ReorderSpacesEvent, ReorderSpacesState> {
ReorderSpacesBloc(
this._reorderSpacesService,
) : super(const ReorderSpacesInitial()) {
on<ReorderSpacesEvent>(_onReorderSpacesEvent);
}
final ReorderSpacesService _reorderSpacesService;
Future<void> _onReorderSpacesEvent(
ReorderSpacesEvent event,
Emitter<ReorderSpacesState> emit,
) async {
emit(const ReorderSpacesLoading());
try {
await _reorderSpacesService.reorderSpaces(event.param);
emit(const ReorderSpacesSuccess());
} on APIException catch (e) {
emit(ReorderSpacesFailure(e.message));
} catch (e) {
emit(ReorderSpacesFailure(e.toString()));
} finally {
emit(const ReorderSpacesInitial());
}
}
}

View File

@ -1,10 +0,0 @@
part of 'reorder_spaces_bloc.dart';
final class ReorderSpacesEvent extends Equatable {
const ReorderSpacesEvent(this.param);
final ReorderSpacesParam param;
@override
List<Object> get props => [param];
}

View File

@ -1,29 +0,0 @@
part of 'reorder_spaces_bloc.dart';
sealed class ReorderSpacesState extends Equatable {
const ReorderSpacesState();
@override
List<Object> get props => [];
}
final class ReorderSpacesInitial extends ReorderSpacesState {
const ReorderSpacesInitial();
}
final class ReorderSpacesLoading extends ReorderSpacesState {
const ReorderSpacesLoading();
}
final class ReorderSpacesSuccess extends ReorderSpacesState {
const ReorderSpacesSuccess();
}
final class ReorderSpacesFailure extends ReorderSpacesState {
const ReorderSpacesFailure(this.errorMessage);
final String errorMessage;
@override
List<Object> get props => [errorMessage];
}

View File

@ -4,7 +4,9 @@ import 'package:syncrow_web/services/api/http_interceptor.dart';
import 'package:syncrow_web/services/api/http_service.dart';
final GetIt serviceLocator = GetIt.instance;
void initialSetup() {
//setupLocator() // to search for dependency injection in flutter
initialSetup() {
serviceLocator.registerSingleton<HTTPInterceptor>(HTTPInterceptor());
//Base classes
serviceLocator.registerSingleton<Dio>(HTTPService.setupDioClient());
}

View File

@ -1,53 +0,0 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/utils/app_routes.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/theme/theme.dart';
class SyncrowApp extends StatelessWidget {
const SyncrowApp({super.key});
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<CreateRoutineBloc>(
create: (context) => CreateRoutineBloc(),
),
BlocProvider(
create: (context) => HomeBloc()..add(const FetchUserInfo()),
),
BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(),
),
BlocProvider<RoutineBloc>(
create: (context) => RoutineBloc(),
),
BlocProvider<SpaceTreeBloc>(
create: (context) => SpaceTreeBloc(),
),
],
child: MaterialApp.router(
debugShowCheckedModeBanner: false,
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown,
},
),
key: NavigationService.navigatorKey,
theme: myTheme,
routerConfig: AppRoutes.router,
),
);
}
}

View File

@ -1,31 +1,17 @@
import 'package:go_router/go_router.dart';
import 'package:syncrow_web/pages/access_management/view/access_management.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/views/analytics_page.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/auth/view/login_page.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/view/device_managment_page.dart';
import 'package:syncrow_web/pages/home/view/home_page.dart';
import 'package:syncrow_web/pages/roles_and_permission/view/roles_and_permission_page.dart';
import 'package:syncrow_web/pages/space_management_v2/main_module/views/space_management_page.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/view/spaces_management_page.dart';
import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart';
abstract final class AppRoutes {
const AppRoutes._();
static final GoRouter router = GoRouter(
initialLocation: RoutesConst.auth,
redirect: (context, state) async {
final checkToken = await AuthBloc.getTokenAndValidate();
final loggedIn = checkToken == 'Success';
final goingToLogin = state.uri.toString() == RoutesConst.auth;
if (!loggedIn && !goingToLogin) return RoutesConst.auth;
if (loggedIn && goingToLogin) return RoutesConst.home;
return null;
},
routes: [
class AppRoutes {
static List<GoRoute> getRoutes() {
return [
GoRoute(
path: RoutesConst.auth,
builder: (context, state) => const LoginPage(),
@ -57,6 +43,6 @@ abstract final class AppRoutes {
name: 'analytics',
builder: (context, state) => const AnalyticsPage(),
),
],
);
];
}
}

View File

@ -41,8 +41,6 @@ abstract class ApiEndpoints {
'/projects/{projectId}/communities/{communityId}/spaces/{spaceId}';
static const String getSpaceHierarchy =
'/projects/{projectId}/communities/{communityId}/spaces';
static const String reorderSpaces =
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{parentSpaceUuid}/spaces/order';
// Community Module
static const String createCommunity = '/projects/{projectId}/communities';
@ -142,6 +140,5 @@ abstract class ApiEndpoints {
static const String saveSchedule = '/schedule/{deviceUuid}';
static const String getBookableSpaces = '/bookable-spaces';
static const String getBookings =
'/bookings?month={mm}%2F{yyyy}&space={space}';
static const String getBookings = '/bookings?month={mm}-{yyyy}&space={space}';
}