mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-08-26 07:29:41 +00:00
Compare commits
45 Commits
d65f9ceea9
...
fix-week-n
Author | SHA1 | Date | |
---|---|---|---|
59058cf2d2 | |||
0eb4652f26 | |||
460639b681 | |||
2f89c3486c | |||
5589e5b587 | |||
d3bd363b70 | |||
3a4fce966c | |||
6bdd28ec57 | |||
652163fdae | |||
076c80fe44 | |||
d12b4c0c65 | |||
22c8c54fab | |||
95300071e9 | |||
06f00da02c | |||
7876af9756 | |||
fe2f4a872b | |||
c9b8fbb0c2 | |||
7b5b40a03c | |||
8522c0bbc3 | |||
c6729f476f | |||
fc70669f1d | |||
f03c28f7fd | |||
6ec972a520 | |||
62f67f5a5f | |||
3e634dc7a2 | |||
8e303af0d7 | |||
4ef4858cee | |||
9a203b2fd9 | |||
39c5fd1bca | |||
308eb65d46 | |||
75b9f4a4e6 | |||
fe4063ef8f | |||
029b5d32e0 | |||
428c81efff | |||
288c252f46 | |||
7399dee687 | |||
08e2ed4b4c | |||
59e04708cd | |||
338d4f5737 | |||
5532935a3a | |||
249cbfc242 | |||
8167926620 | |||
2681c837f5 | |||
b6664ec1ba | |||
dcf1df9b4a |
@ -1,22 +1,9 @@
|
|||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flutter_dotenv/flutter_dotenv.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/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/services/locator.dart';
|
||||||
import 'package:syncrow_web/utils/app_routes.dart';
|
import 'package:syncrow_web/syncrow_app.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 {
|
Future<void> main() async {
|
||||||
try {
|
try {
|
||||||
@ -33,59 +20,5 @@ Future<void> main() async {
|
|||||||
);
|
);
|
||||||
initialSetup();
|
initialSetup();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
runApp(MyApp());
|
runApp(const SyncrowApp());
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,9 @@
|
|||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flutter_dotenv/flutter_dotenv.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/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/services/locator.dart';
|
||||||
import 'package:syncrow_web/utils/app_routes.dart';
|
import 'package:syncrow_web/syncrow_app.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 {
|
Future<void> main() async {
|
||||||
try {
|
try {
|
||||||
@ -33,59 +20,5 @@ Future<void> main() async {
|
|||||||
);
|
);
|
||||||
initialSetup();
|
initialSetup();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
runApp(MyApp());
|
runApp(const SyncrowApp());
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,16 @@
|
|||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flutter_dotenv/flutter_dotenv.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/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/services/locator.dart';
|
||||||
import 'package:syncrow_web/utils/app_routes.dart';
|
import 'package:syncrow_web/syncrow_app.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 {
|
Future<void> main() async {
|
||||||
try {
|
try {
|
||||||
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'staging');
|
const environment = String.fromEnvironment(
|
||||||
|
'FLAVOR',
|
||||||
|
defaultValue: 'staging',
|
||||||
|
);
|
||||||
await dotenv.load(fileName: '.env.$environment');
|
await dotenv.load(fileName: '.env.$environment');
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(
|
||||||
@ -30,59 +20,5 @@ Future<void> main() async {
|
|||||||
);
|
);
|
||||||
initialSetup();
|
initialSetup();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
runApp(MyApp());
|
runApp(const SyncrowApp());
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/data/services/remote_calendar_service.dart';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart';
|
||||||
|
|
||||||
|
class MemoryCalendarService implements CalendarSystemService {
|
||||||
|
final Map<String, CalendarEventsResponse> _eventsCache = {};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<CalendarEventsResponse> getCalendarEvents({
|
||||||
|
required LoadEventsParam params,
|
||||||
|
}) async {
|
||||||
|
final key = params.generateKey();
|
||||||
|
|
||||||
|
return _eventsCache[key]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEvents(
|
||||||
|
LoadEventsParam param,
|
||||||
|
CalendarEventsResponse events,
|
||||||
|
) {
|
||||||
|
final key = param.generateKey();
|
||||||
|
_eventsCache[key] = events;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addEvent(LoadEventsParam param, CalendarEventsResponse event) {
|
||||||
|
final key = param.generateKey();
|
||||||
|
|
||||||
|
_eventsCache[key] = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
_eventsCache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MemoryCalendarServiceWithRemoteFallback implements CalendarSystemService {
|
||||||
|
final MemoryCalendarService memoryService;
|
||||||
|
final RemoteCalendarService remoteService;
|
||||||
|
|
||||||
|
MemoryCalendarServiceWithRemoteFallback({
|
||||||
|
required this.memoryService,
|
||||||
|
required this.remoteService,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<CalendarEventsResponse> getCalendarEvents({
|
||||||
|
required LoadEventsParam params,
|
||||||
|
}) async {
|
||||||
|
final key = params.generateKey();
|
||||||
|
final doesExistInMemory = memoryService._eventsCache.containsKey(key);
|
||||||
|
|
||||||
|
if (doesExistInMemory) {
|
||||||
|
return memoryService.getCalendarEvents(params: params);
|
||||||
|
} else {
|
||||||
|
final remoteResult =
|
||||||
|
await remoteService.getCalendarEvents(params: params);
|
||||||
|
memoryService.setEvents(params, remoteResult);
|
||||||
|
|
||||||
|
return remoteResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart';
|
||||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||||
@ -13,147 +14,21 @@ class RemoteCalendarService implements CalendarSystemService {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<CalendarEventsResponse> getCalendarEvents({
|
Future<CalendarEventsResponse> getCalendarEvents({
|
||||||
required String spaceId,
|
required LoadEventsParam params,
|
||||||
}) async {
|
}) async {
|
||||||
|
final month = params.startDate.month.toString().padLeft(2, '0');
|
||||||
|
final year = params.startDate.year.toString();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await _httpService.get(
|
return await _httpService.get<CalendarEventsResponse>(
|
||||||
path: ApiEndpoints.getCalendarEvents,
|
path: ApiEndpoints.getBookings
|
||||||
queryParameters: {
|
.replaceAll('{mm}', month)
|
||||||
'spaceId': spaceId,
|
.replaceAll('{yyyy}', year)
|
||||||
},
|
.replaceAll('{space}', params.id),
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
return CalendarEventsResponse.fromJson(
|
return CalendarEventsResponse.fromJson(json as Map<String, dynamic>);
|
||||||
json as Map<String, dynamic>,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return CalendarEventsResponse.fromJson(response as Map<String, dynamic>);
|
|
||||||
} on DioException catch (e) {
|
|
||||||
final responseData = e.response?.data;
|
|
||||||
if (responseData is Map<String, dynamic>) {
|
|
||||||
final errorMessage = responseData['error']?['message'] as String? ??
|
|
||||||
responseData['message'] as String? ??
|
|
||||||
_defaultErrorMessage;
|
|
||||||
throw APIException(errorMessage);
|
|
||||||
}
|
|
||||||
throw APIException(_defaultErrorMessage);
|
|
||||||
} catch (e) {
|
|
||||||
throw APIException('$_defaultErrorMessage: ${e.toString()}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FakeRemoteCalendarService implements CalendarSystemService {
|
|
||||||
const FakeRemoteCalendarService(this._httpService, {this.useDummy = false});
|
|
||||||
|
|
||||||
final HTTPService _httpService;
|
|
||||||
final bool useDummy;
|
|
||||||
static const _defaultErrorMessage = 'Failed to load Calendar';
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<CalendarEventsResponse> getCalendarEvents({
|
|
||||||
required String spaceId,
|
|
||||||
}) async {
|
|
||||||
if (useDummy) {
|
|
||||||
final dummyJson = {
|
|
||||||
'statusCode': 200,
|
|
||||||
'message': 'Successfully fetched all bookings',
|
|
||||||
'data': [
|
|
||||||
{
|
|
||||||
'uuid': 'd4553fa6-a0c9-4f42-81c9-99a13a57bf80',
|
|
||||||
'date': '2025-07-11T10:22:00.626Z',
|
|
||||||
'startTime': '09:00:00',
|
|
||||||
'endTime': '12:00:00',
|
|
||||||
'cost': 10,
|
|
||||||
'user': {
|
|
||||||
'uuid': '784394ff-3197-4c39-9f07-48dc44920b1e',
|
|
||||||
'firstName': 'salsabeel',
|
|
||||||
'lastName': 'abuzaid',
|
|
||||||
'email': 'test@test.com',
|
|
||||||
'companyName': null
|
|
||||||
},
|
|
||||||
'space': {
|
|
||||||
'uuid': '000f4d81-43e4-4ad7-865c-0f8b04b7081e',
|
|
||||||
'spaceName': '2(1)'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'uuid': 'e9b27af0-b963-4d98-9657-454c4ba78561',
|
|
||||||
'date': '2025-07-11T10:22:00.626Z',
|
|
||||||
'startTime': '12:00:00',
|
|
||||||
'endTime': '13:00:00',
|
|
||||||
'cost': 10,
|
|
||||||
'user': {
|
|
||||||
'uuid': '784394ff-3197-4c39-9f07-48dc44920b1e',
|
|
||||||
'firstName': 'salsabeel',
|
|
||||||
'lastName': 'abuzaid',
|
|
||||||
'email': 'test@test.com',
|
|
||||||
'companyName': null
|
|
||||||
},
|
|
||||||
'space': {
|
|
||||||
'uuid': '000f4d81-43e4-4ad7-865c-0f8b04b7081e',
|
|
||||||
'spaceName': '2(1)'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'uuid': 'e9b27af0-b963-4d98-9657-454c4ba78561',
|
|
||||||
'date': '2025-07-13T10:22:00.626Z',
|
|
||||||
'startTime': '15:30:00',
|
|
||||||
'endTime': '19:00:00',
|
|
||||||
'cost': 20,
|
|
||||||
'user': {
|
|
||||||
'uuid': '784394ff-3197-4c39-9f07-48dc44920b1e',
|
|
||||||
'firstName': 'salsabeel',
|
|
||||||
'lastName': 'abuzaid',
|
|
||||||
'email': 'test@test.com',
|
|
||||||
'companyName': null
|
|
||||||
},
|
|
||||||
'space': {
|
|
||||||
'uuid': '000f4d81-43e4-4ad7-865c-0f8b04b7081e',
|
|
||||||
'spaceName': '2(1)'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'success': true
|
|
||||||
};
|
|
||||||
final response = CalendarEventsResponse.fromJson(dummyJson);
|
|
||||||
|
|
||||||
// Filter events by spaceId
|
|
||||||
final filteredData = response.data.where((event) {
|
|
||||||
return event.space.uuid == spaceId;
|
|
||||||
}).toList();
|
|
||||||
print('Filtering events for spaceId: $spaceId');
|
|
||||||
print('Found ${filteredData.length} matching events');
|
|
||||||
return filteredData.isNotEmpty
|
|
||||||
? CalendarEventsResponse(
|
|
||||||
statusCode: response.statusCode,
|
|
||||||
message: response.message,
|
|
||||||
data: filteredData,
|
|
||||||
success: response.success,
|
|
||||||
)
|
|
||||||
: CalendarEventsResponse(
|
|
||||||
statusCode: 404,
|
|
||||||
message: 'No events found for spaceId: $spaceId',
|
|
||||||
data: [],
|
|
||||||
success: false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
final response = await _httpService.get(
|
|
||||||
path: ApiEndpoints.getCalendarEvents,
|
|
||||||
queryParameters: {
|
|
||||||
'spaceId': spaceId,
|
|
||||||
},
|
|
||||||
expectedResponseModel: (json) {
|
|
||||||
return CalendarEventsResponse.fromJson(
|
|
||||||
json as Map<String, dynamic>,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return CalendarEventsResponse.fromJson(response as Map<String, dynamic>);
|
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
final responseData = e.response?.data;
|
final responseData = e.response?.data;
|
||||||
if (responseData is Map<String, dynamic>) {
|
if (responseData is Map<String, dynamic>) {
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
class LoadEventsParam extends Equatable {
|
||||||
|
final DateTime startDate;
|
||||||
|
final DateTime endDate;
|
||||||
|
final String id;
|
||||||
|
|
||||||
|
const LoadEventsParam({
|
||||||
|
required this.startDate,
|
||||||
|
required this.endDate,
|
||||||
|
required this.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [startDate, endDate, id];
|
||||||
|
|
||||||
|
LoadEventsParam copyWith({
|
||||||
|
DateTime? startDate,
|
||||||
|
DateTime? endDate,
|
||||||
|
String? id,
|
||||||
|
}) {
|
||||||
|
return LoadEventsParam(
|
||||||
|
startDate: startDate ?? this.startDate,
|
||||||
|
endDate: endDate ?? this.endDate,
|
||||||
|
id: id ?? this.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension KeyGenerator on LoadEventsParam {
|
||||||
|
String generateKey() {
|
||||||
|
return '$id-${startDate.year}-${startDate.month.toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart';
|
||||||
|
|
||||||
abstract class CalendarSystemService {
|
abstract class CalendarSystemService {
|
||||||
Future<CalendarEventsResponse> getCalendarEvents({
|
Future<CalendarEventsResponse> getCalendarEvents({
|
||||||
required String spaceId,
|
required LoadEventsParam params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,37 +2,48 @@ import 'dart:async';
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:calendar_view/calendar_view.dart';
|
import 'package:calendar_view/calendar_view.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/services/calendar_system_service.dart';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart';
|
||||||
part 'events_event.dart';
|
part 'events_event.dart';
|
||||||
part 'events_state.dart';
|
part 'events_state.dart';
|
||||||
|
|
||||||
class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
|
class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
|
||||||
final EventController eventController = EventController();
|
EventController eventController = EventController();
|
||||||
final CalendarSystemService calendarService;
|
final CalendarSystemService calendarService;
|
||||||
|
|
||||||
CalendarEventsBloc({required this.calendarService}) : super(EventsInitial()) {
|
CalendarEventsBloc({
|
||||||
|
required this.calendarService,
|
||||||
|
}) : super(EventsInitial()) {
|
||||||
on<LoadEvents>(_onLoadEvents);
|
on<LoadEvents>(_onLoadEvents);
|
||||||
on<AddEvent>(_onAddEvent);
|
on<AddEvent>(_onAddEvent);
|
||||||
on<StartTimer>(_onStartTimer);
|
|
||||||
on<DisposeResources>(_onDisposeResources);
|
on<DisposeResources>(_onDisposeResources);
|
||||||
on<GoToWeek>(_onGoToWeek);
|
on<GoToWeek>(_onGoToWeek);
|
||||||
|
on<ResetEvents>(_onResetEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onLoadEvents(
|
Future<void> _onLoadEvents(
|
||||||
LoadEvents event,
|
LoadEvents event,
|
||||||
Emitter<CalendarEventState> emit,
|
Emitter<CalendarEventState> emit,
|
||||||
) async {
|
) async {
|
||||||
|
final param = event.param;
|
||||||
|
final month = param.endDate.month;
|
||||||
|
final year = param.endDate.year;
|
||||||
|
final spaceId = param.id;
|
||||||
|
|
||||||
emit(EventsLoading());
|
emit(EventsLoading());
|
||||||
try {
|
try {
|
||||||
final response = await calendarService.getCalendarEvents(
|
final response = await calendarService.getCalendarEvents(params: param);
|
||||||
spaceId: event.spaceId,
|
|
||||||
);
|
final events = response.data.map(_toCalendarEventData).toList();
|
||||||
final events =
|
|
||||||
response.data.map<CalendarEventData>(_toCalendarEventData).toList();
|
|
||||||
eventController.addAll(events);
|
eventController.addAll(events);
|
||||||
emit(EventsLoaded(events: events));
|
emit(EventsLoaded(
|
||||||
|
events: events,
|
||||||
|
spaceId: spaceId,
|
||||||
|
month: month,
|
||||||
|
year: year,
|
||||||
|
));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(EventsError('Failed to load events'));
|
emit(EventsError('Failed to load events'));
|
||||||
}
|
}
|
||||||
@ -40,16 +51,19 @@ class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
|
|||||||
|
|
||||||
void _onAddEvent(AddEvent event, Emitter<CalendarEventState> emit) {
|
void _onAddEvent(AddEvent event, Emitter<CalendarEventState> emit) {
|
||||||
eventController.add(event.event);
|
eventController.add(event.event);
|
||||||
|
|
||||||
if (state is EventsLoaded) {
|
if (state is EventsLoaded) {
|
||||||
final loaded = state as EventsLoaded;
|
final loaded = state as EventsLoaded;
|
||||||
|
|
||||||
emit(EventsLoaded(
|
emit(EventsLoaded(
|
||||||
events: [...eventController.events],
|
events: [...eventController.events],
|
||||||
|
spaceId: loaded.spaceId,
|
||||||
|
month: loaded.month,
|
||||||
|
year: loaded.year,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onStartTimer(StartTimer event, Emitter<CalendarEventState> emit) {}
|
|
||||||
|
|
||||||
void _onDisposeResources(
|
void _onDisposeResources(
|
||||||
DisposeResources event, Emitter<CalendarEventState> emit) {
|
DisposeResources event, Emitter<CalendarEventState> emit) {
|
||||||
eventController.dispose();
|
eventController.dispose();
|
||||||
@ -61,6 +75,9 @@ class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
|
|||||||
final newWeekDays = _getWeekDays(event.weekDate);
|
final newWeekDays = _getWeekDays(event.weekDate);
|
||||||
emit(EventsLoaded(
|
emit(EventsLoaded(
|
||||||
events: loaded.events,
|
events: loaded.events,
|
||||||
|
spaceId: loaded.spaceId,
|
||||||
|
month: loaded.month,
|
||||||
|
year: loaded.year,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,14 +107,13 @@ class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return CalendarEventData(
|
return CalendarEventData(
|
||||||
date: startTime,
|
date: startTime,
|
||||||
startTime: startTime,
|
startTime: startTime,
|
||||||
endTime: endTime,
|
endTime: endTime,
|
||||||
title:
|
title: '${booking.user.firstName} ${booking.user.lastName}',
|
||||||
'${booking.space.spaceName} - ${booking.user.firstName} ${booking.user.lastName}',
|
|
||||||
description: 'Cost: ${booking.cost}',
|
description: 'Cost: ${booking.cost}',
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
event: booking,
|
event: booking,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,4 +128,18 @@ class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
|
|||||||
eventController.dispose();
|
eventController.dispose();
|
||||||
return super.close();
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,11 @@ abstract class CalendarEventsEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LoadEvents extends CalendarEventsEvent {
|
class LoadEvents extends CalendarEventsEvent {
|
||||||
final String spaceId;
|
final LoadEventsParam param;
|
||||||
final DateTime weekStart;
|
const LoadEvents(this.param);
|
||||||
final DateTime weekEnd;
|
|
||||||
|
|
||||||
const LoadEvents({
|
|
||||||
required this.spaceId,
|
|
||||||
required this.weekStart,
|
|
||||||
required this.weekEnd,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class AddEvent extends CalendarEventsEvent {
|
class AddEvent extends CalendarEventsEvent {
|
||||||
final CalendarEventData event;
|
final CalendarEventData event;
|
||||||
const AddEvent(this.event);
|
const AddEvent(this.event);
|
||||||
@ -35,3 +29,10 @@ class CheckWeekHasEvents extends CalendarEventsEvent {
|
|||||||
final DateTime weekStart;
|
final DateTime weekStart;
|
||||||
const CheckWeekHasEvents(this.weekStart);
|
const CheckWeekHasEvents(this.weekStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ResetEvents extends CalendarEventsEvent {
|
||||||
|
const ResetEvents();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
@ -7,11 +7,17 @@ class EventsInitial extends CalendarEventState {}
|
|||||||
|
|
||||||
class EventsLoading extends CalendarEventState {}
|
class EventsLoading extends CalendarEventState {}
|
||||||
|
|
||||||
class EventsLoaded extends CalendarEventState {
|
final class EventsLoaded extends CalendarEventState {
|
||||||
final List<CalendarEventData> events;
|
final List<CalendarEventData> events;
|
||||||
|
final String spaceId;
|
||||||
|
final int month;
|
||||||
|
final int year;
|
||||||
|
|
||||||
EventsLoaded({
|
EventsLoaded({
|
||||||
required this.events,
|
required this.events,
|
||||||
|
required this.spaceId,
|
||||||
|
required this.month,
|
||||||
|
required this.year,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,13 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:calendar_view/calendar_view.dart';
|
import 'package:calendar_view/calendar_view.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/data/services/remote_calendar_service.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/data/services/remote_calendar_service.dart';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_bloc.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_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 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_state.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_state.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/selected_bookable_space_bloc/selected_bookable_space_bloc.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/selected_bookable_space_bloc/selected_bookable_space_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/custom_calendar_page.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/custom_calendar_page.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart';
|
||||||
@ -24,7 +26,33 @@ class BookingPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _BookingPageState extends State<BookingPage> {
|
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
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -38,7 +66,7 @@ class _BookingPageState extends State<BookingPage> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _dispatchLoadEvents(BuildContext context) {
|
void _loadEvents(BuildContext context) {
|
||||||
final selectedRoom =
|
final selectedRoom =
|
||||||
context.read<SelectedBookableSpaceBloc>().state.selectedBookableSpace;
|
context.read<SelectedBookableSpaceBloc>().state.selectedBookableSpace;
|
||||||
final dateState = context.read<DateSelectionBloc>().state;
|
final dateState = context.read<DateSelectionBloc>().state;
|
||||||
@ -46,9 +74,11 @@ class _BookingPageState extends State<BookingPage> {
|
|||||||
if (selectedRoom != null) {
|
if (selectedRoom != null) {
|
||||||
context.read<CalendarEventsBloc>().add(
|
context.read<CalendarEventsBloc>().add(
|
||||||
LoadEvents(
|
LoadEvents(
|
||||||
spaceId: selectedRoom.uuid,
|
LoadEventsParam(
|
||||||
weekStart: dateState.weekStart,
|
startDate: dateState.weekStart,
|
||||||
weekEnd: dateState.weekStart.add(const Duration(days: 6)),
|
endDate: dateState.weekStart.add(const Duration(days: 6)),
|
||||||
|
id: selectedRoom.uuid,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -56,182 +86,168 @@ class _BookingPageState extends State<BookingPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiBlocProvider(
|
return BlocListener<SelectedBookableSpaceBloc, SelectedBookableSpaceState>(
|
||||||
providers: [
|
listener: (context, state) {
|
||||||
BlocProvider(create: (_) => SelectedBookableSpaceBloc()),
|
if (state.selectedBookableSpace != null) {
|
||||||
BlocProvider(create: (_) => DateSelectionBloc()),
|
context.read<CalendarEventsBloc>().add(const ResetEvents());
|
||||||
BlocProvider(
|
_loadEvents(context);
|
||||||
create: (_) => CalendarEventsBloc(
|
}
|
||||||
calendarService:
|
},
|
||||||
FakeRemoteCalendarService(HTTPService(), useDummy: true),
|
child: BlocListener<DateSelectionBloc, DateSelectionState>(
|
||||||
),
|
listener: (context, state) {
|
||||||
),
|
_loadEvents(context);
|
||||||
],
|
},
|
||||||
child: Builder(
|
child: BlocListener<CalendarEventsBloc, CalendarEventState>(
|
||||||
builder: (context) =>
|
|
||||||
BlocListener<CalendarEventsBloc, CalendarEventState>(
|
|
||||||
listenWhen: (prev, curr) => curr is EventsLoaded,
|
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
if (state is EventsLoaded) {
|
if (state is EventsLoaded) {
|
||||||
_eventController.removeWhere((_) => true);
|
_eventController.removeWhere((_) => true);
|
||||||
_eventController.addAll(state.events);
|
_eventController.addAll(state.events);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: BlocListener<SelectedBookableSpaceBloc,
|
child: Row(
|
||||||
SelectedBookableSpaceState>(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
listener: (context, state) => _dispatchLoadEvents(context),
|
children: [
|
||||||
child: BlocListener<DateSelectionBloc, DateSelectionState>(
|
Expanded(
|
||||||
listener: (context, state) => _dispatchLoadEvents(context),
|
child: Container(
|
||||||
child: Row(
|
decoration: BoxDecoration(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
color: ColorsManager.whiteColors,
|
||||||
children: [
|
boxShadow: [
|
||||||
Expanded(
|
BoxShadow(
|
||||||
child: Container(
|
color: ColorsManager.blackColor.withOpacity(0.1),
|
||||||
decoration: BoxDecoration(
|
offset: const Offset(3, 0),
|
||||||
color: ColorsManager.whiteColors,
|
blurRadius: 6,
|
||||||
boxShadow: [
|
spreadRadius: 0,
|
||||||
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(
|
child: Column(
|
||||||
flex: 4,
|
children: [
|
||||||
child: Padding(
|
Expanded(
|
||||||
padding: const EdgeInsets.all(20.0),
|
flex: 2,
|
||||||
child: Column(
|
child: BlocBuilder<SelectedBookableSpaceBloc,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
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: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Row(
|
SvgTextButton(
|
||||||
children: [
|
svgAsset: Assets.homeIcon,
|
||||||
SvgTextButton(
|
label: 'Manage Bookable Spaces',
|
||||||
svgAsset: Assets.homeIcon,
|
onPressed: () {},
|
||||||
label: 'Manage Bookable Spaces',
|
|
||||||
onPressed: () {},
|
|
||||||
),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
SvgTextButton(
|
|
||||||
svgAsset: Assets.groupIcon,
|
|
||||||
label: 'Manage Users',
|
|
||||||
onPressed: () {},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
BlocBuilder<DateSelectionBloc,
|
const SizedBox(width: 20),
|
||||||
DateSelectionState>(
|
SvgTextButton(
|
||||||
builder: (context, state) {
|
svgAsset: Assets.groupIcon,
|
||||||
final weekStart = state.weekStart;
|
label: 'Manage Users',
|
||||||
final weekEnd =
|
onPressed: () {},
|
||||||
weekStart.add(const Duration(days: 6));
|
|
||||||
return WeekNavigation(
|
|
||||||
weekStart: weekStart,
|
|
||||||
weekEnd: weekEnd,
|
|
||||||
onPreviousWeek: () {
|
|
||||||
context
|
|
||||||
.read<DateSelectionBloc>()
|
|
||||||
.add(PreviousWeek());
|
|
||||||
},
|
|
||||||
onNextWeek: () {
|
|
||||||
context
|
|
||||||
.read<DateSelectionBloc>()
|
|
||||||
.add(NextWeek());
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Expanded(
|
BlocBuilder<DateSelectionBloc, DateSelectionState>(
|
||||||
child: BlocBuilder<SelectedBookableSpaceBloc,
|
builder: (context, state) {
|
||||||
SelectedBookableSpaceState>(
|
final weekStart = state.weekStart;
|
||||||
builder: (context, roomState) {
|
final weekEnd =
|
||||||
final selectedRoom =
|
weekStart.add(const Duration(days: 6));
|
||||||
roomState.selectedBookableSpace;
|
return WeekNavigation(
|
||||||
return BlocBuilder<DateSelectionBloc,
|
weekStart: weekStart,
|
||||||
DateSelectionState>(
|
weekEnd: weekEnd,
|
||||||
builder: (context, dateState) {
|
onPreviousWeek: () {
|
||||||
return BlocListener<CalendarEventsBloc,
|
context
|
||||||
CalendarEventState>(
|
.read<DateSelectionBloc>()
|
||||||
listenWhen: (prev, curr) =>
|
.add(PreviousWeek());
|
||||||
curr is EventsLoaded,
|
},
|
||||||
listener: (context, state) {
|
onNextWeek: () {
|
||||||
if (state is EventsLoaded) {
|
context
|
||||||
_eventController
|
.read<DateSelectionBloc>()
|
||||||
.removeWhere((_) => true);
|
.add(NextWeek());
|
||||||
_eventController.addAll(state.events);
|
},
|
||||||
}
|
);
|
||||||
},
|
},
|
||||||
child: WeeklyCalendarPage(
|
),
|
||||||
startTime: selectedRoom
|
],
|
||||||
?.bookableConfig.startTime,
|
),
|
||||||
endTime: selectedRoom
|
const SizedBox(height: 20),
|
||||||
?.bookableConfig.endTime,
|
Expanded(
|
||||||
weekStart: dateState.weekStart,
|
flex: 5,
|
||||||
selectedDate: dateState.selectedDate,
|
child: BlocBuilder<SelectedBookableSpaceBloc,
|
||||||
eventController: _eventController,
|
SelectedBookableSpaceState>(
|
||||||
selectedDateFromSideBarCalender: context
|
builder: (context, roomState) {
|
||||||
.watch<DateSelectionBloc>()
|
final selectedRoom =
|
||||||
.state
|
roomState.selectedBookableSpace;
|
||||||
.selectedDateFromSideBarCalender,
|
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,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -72,29 +72,33 @@ class __SidebarContentState extends State<_SidebarContent> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocConsumer<SidebarBloc, SidebarState>(
|
return BlocConsumer<SidebarBloc, SidebarState>(
|
||||||
listener: (context, state) {
|
listener: (context, state) {},
|
||||||
if (state.currentPage == 1 && searchController.text.isNotEmpty) {
|
|
||||||
searchController.clear();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
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(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: ColorsManager.blackColor.withOpacity(0.1),
|
color: ColorsManager.blackColor.withOpacity(0.1),
|
||||||
offset: const Offset(0, -2),
|
offset: const Offset(0, 4),
|
||||||
blurRadius: 4,
|
|
||||||
spreadRadius: 0,
|
|
||||||
),
|
|
||||||
BoxShadow(
|
|
||||||
color: ColorsManager.blackColor.withOpacity(0.1),
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
spreadRadius: 0,
|
spreadRadius: 0,
|
||||||
),
|
),
|
||||||
@ -147,6 +151,7 @@ class __SidebarContentState extends State<_SidebarContent> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
searchController.clear();
|
||||||
context.read<SidebarBloc>().add(ResetSearch());
|
context.read<SidebarBloc>().add(ResetSearch());
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -223,7 +228,7 @@ class _SidebarHeader extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
@ -66,18 +66,28 @@ class _CustomCalendarPageState extends State<CustomCalendarPage> {
|
|||||||
weekdayLabels: const ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'],
|
weekdayLabels: const ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'],
|
||||||
);
|
);
|
||||||
|
|
||||||
return CalendarDatePicker2(
|
return Container(
|
||||||
config: config,
|
decoration: const BoxDecoration(
|
||||||
value: [_selectedDate],
|
border: Border(
|
||||||
onValueChanged: (dates) {
|
top: BorderSide(
|
||||||
final picked = dates.first;
|
color: ColorsManager.textGray,
|
||||||
if (picked != null) {
|
width: 1.0,
|
||||||
setState(() {
|
),
|
||||||
_selectedDate = picked;
|
),
|
||||||
});
|
),
|
||||||
widget.onDateChanged(picked.day, picked.month, picked.year);
|
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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
import 'package:calendar_view/calendar_view.dart';
|
import 'package:calendar_view/calendar_view.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:syncrow_web/pages/access_management/booking_system/domain/models/calendar_event_booking.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class EventTileWidget extends StatelessWidget {
|
class EventTileWidget extends StatelessWidget {
|
||||||
final List<CalendarEventData<Object?>> events;
|
final List<CalendarEventData<Object?>> events;
|
||||||
|
|
||||||
const EventTileWidget({
|
const EventTileWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.events,
|
required this.events,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
@ -18,39 +17,88 @@ class EventTileWidget extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: events.map((event) {
|
children: events.map((event) {
|
||||||
final bool isEventEnded =
|
final booking = event.event is CalendarEventBooking
|
||||||
|
? event.event! as CalendarEventBooking
|
||||||
|
: null;
|
||||||
|
|
||||||
|
final companyName = booking?.user.companyName ?? 'Unknown Company';
|
||||||
|
final startTime = DateFormat('hh:mm a').format(event.startTime!);
|
||||||
|
final endTime = DateFormat('hh:mm a').format(event.endTime!);
|
||||||
|
final isEventEnded =
|
||||||
event.endTime != null && event.endTime!.isBefore(DateTime.now());
|
event.endTime != null && event.endTime!.isBefore(DateTime.now());
|
||||||
|
|
||||||
|
final duration = event.endTime!.difference(event.startTime!);
|
||||||
|
final bool isLongEnough = duration.inMinutes >= 31;
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.all(6),
|
padding: const EdgeInsets.all(5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isEventEnded
|
color: isEventEnded
|
||||||
? ColorsManager.lightGrayBorderColor
|
? ColorsManager.grayColor.withOpacity(0.1)
|
||||||
: ColorsManager.blue1.withOpacity(0.25),
|
: ColorsManager.blue1.withOpacity(0.1),
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
border: Border(
|
||||||
child: Column(
|
left: BorderSide(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
color: isEventEnded
|
||||||
children: [
|
? ColorsManager.grayColor
|
||||||
Text(
|
: ColorsManager.secondaryColor,
|
||||||
DateFormat('h:mm a').format(event.startTime!),
|
width: 4,
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
),
|
||||||
Text(
|
|
||||||
event.title,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
child: isLongEnough
|
||||||
|
? Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'$startTime - $endTime',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: isEventEnded
|
||||||
|
? ColorsManager.grayColor.withOpacity(0.9)
|
||||||
|
: ColorsManager.blackColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(
|
||||||
|
event.title,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: isEventEnded
|
||||||
|
? ColorsManager.grayColor
|
||||||
|
: ColorsManager.blackColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(
|
||||||
|
companyName,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: isEventEnded
|
||||||
|
? ColorsManager.grayColor.withOpacity(0.9)
|
||||||
|
: ColorsManager.blackColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
event.title,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: isEventEnded
|
||||||
|
? ColorsManager.grayColor
|
||||||
|
: ColorsManager.blackColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
|
@ -21,20 +21,24 @@ class RoomListItem extends StatelessWidget {
|
|||||||
groupValue: isSelected ? room.uuid : null,
|
groupValue: isSelected ? room.uuid : null,
|
||||||
visualDensity: const VisualDensity(vertical: -4),
|
visualDensity: const VisualDensity(vertical: -4),
|
||||||
onChanged: (value) => onTap(),
|
onChanged: (value) => onTap(),
|
||||||
activeColor: ColorsManager.primaryColor,
|
activeColor: ColorsManager.secondaryColor,
|
||||||
title: Text(
|
title: Text(
|
||||||
room.spaceName,
|
room.spaceName,
|
||||||
|
maxLines: 2,
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
color: ColorsManager.lightGrayColor,
|
color: ColorsManager.lightGrayColor,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
fontSize: 12),
|
fontSize: 12),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
room.virtualLocation,
|
room.virtualLocation,
|
||||||
|
maxLines: 2,
|
||||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -14,26 +14,33 @@ class WeekDayHeader extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return ColoredBox(
|
||||||
children: [
|
color: isSelectedDay
|
||||||
Text(
|
? ColorsManager.secondaryColor.withOpacity(0.1)
|
||||||
DateFormat('EEE').format(date).toUpperCase(),
|
: Colors.transparent,
|
||||||
style: TextStyle(
|
child: Column(
|
||||||
fontWeight: FontWeight.w400,
|
children: [
|
||||||
fontSize: 14,
|
const SizedBox(
|
||||||
color: isSelectedDay ? Colors.blue : Colors.black,
|
height: 10,
|
||||||
),
|
),
|
||||||
),
|
Text(
|
||||||
Text(
|
DateFormat('EEE').format(date).toUpperCase(),
|
||||||
DateFormat('d').format(date),
|
style: const TextStyle(
|
||||||
style: TextStyle(
|
fontWeight: FontWeight.w400,
|
||||||
fontWeight: FontWeight.w700,
|
fontSize: 14,
|
||||||
fontSize: 20,
|
color: ColorsManager.blackColor,
|
||||||
color:
|
),
|
||||||
isSelectedDay ? ColorsManager.blue1 : ColorsManager.blackColor,
|
|
||||||
),
|
),
|
||||||
),
|
Text(
|
||||||
],
|
DateFormat('d').format(date),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
fontSize: 30,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ class WeekNavigation extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
width: 250,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: ColorsManager.circleRolesBackground,
|
color: ColorsManager.circleRolesBackground,
|
||||||
@ -32,6 +33,8 @@ class WeekNavigation extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
iconSize: 15,
|
iconSize: 15,
|
||||||
@ -40,12 +43,16 @@ class WeekNavigation extends StatelessWidget {
|
|||||||
onPressed: onPreviousWeek,
|
onPressed: onPreviousWeek,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Text(
|
SizedBox(
|
||||||
_getMonthYearText(weekStart, weekEnd),
|
width: 120,
|
||||||
style: const TextStyle(
|
child: Text(
|
||||||
color: ColorsManager.lightGrayColor,
|
_getMonthYearText(weekStart, weekEnd),
|
||||||
fontSize: 14,
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.w400,
|
color: ColorsManager.lightGrayColor,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:calendar_view/calendar_view.dart';
|
import 'package:calendar_view/calendar_view.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/event_tile_widget.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/event_tile_widget.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/hatched_column_background.dart';
|
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/time_line_widget.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/time_line_widget.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/week_day_header.dart';
|
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/week_day_header.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
@ -23,6 +22,12 @@ class WeeklyCalendarPage extends StatelessWidget {
|
|||||||
this.endTime,
|
this.endTime,
|
||||||
this.selectedDateFromSideBarCalender,
|
this.selectedDateFromSideBarCalender,
|
||||||
});
|
});
|
||||||
|
static const double timeLineWidth = 65;
|
||||||
|
static const int totalDays = 7;
|
||||||
|
static const double dayColumnWidth = 220;
|
||||||
|
|
||||||
|
final double calendarContentWidth =
|
||||||
|
timeLineWidth + (totalDays * dayColumnWidth);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -52,154 +57,152 @@ class WeeklyCalendarPage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final weekDays = _getWeekDays(weekStart);
|
const double timeLineWidth = 90;
|
||||||
|
|
||||||
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;
|
|
||||||
final DateTime highlightStart = DateTime(2025, 7, 10);
|
|
||||||
final DateTime highlightEnd = DateTime(2025, 7, 19);
|
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
final double calendarWidth = constraints.maxWidth;
|
|
||||||
final double dayColumnWidth =
|
|
||||||
(calendarWidth - timeLineWidth) / totalDays - 0.1;
|
|
||||||
bool isInRange(DateTime date, DateTime start, DateTime end) {
|
bool isInRange(DateTime date, DateTime start, DateTime end) {
|
||||||
return !date.isBefore(start) && !date.isAfter(end);
|
!date.isBefore(start) && !date.isAfter(end);
|
||||||
|
// remove this line and Check if the date is within the range
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Padding(
|
return SingleChildScrollView(
|
||||||
padding: const EdgeInsets.only(left: 25.0, right: 25.0, top: 25),
|
scrollDirection: Axis.horizontal,
|
||||||
child: Stack(
|
child: SizedBox(
|
||||||
children: [
|
width: calendarContentWidth,
|
||||||
WeekView(
|
child: Padding(
|
||||||
weekDetectorBuilder: ({
|
padding:
|
||||||
required date,
|
const EdgeInsets.only(left: 25.0, right: 25.0, top: 25),
|
||||||
required height,
|
child: Stack(
|
||||||
required heightPerMinute,
|
children: [
|
||||||
required minuteSlotSize,
|
Container(
|
||||||
required width,
|
child: WeekView(
|
||||||
}) {
|
minuteSlotSize: MinuteSlotSize.minutes15,
|
||||||
return isInRange(date, highlightStart, highlightEnd)
|
weekDetectorBuilder: ({
|
||||||
? HatchedColumnBackground(
|
required date,
|
||||||
backgroundColor: ColorsManager.grey800,
|
required height,
|
||||||
lineColor: ColorsManager.textGray,
|
required heightPerMinute,
|
||||||
opacity: 0.3,
|
required minuteSlotSize,
|
||||||
stripeSpacing: 12,
|
required width,
|
||||||
borderRadius: BorderRadius.circular(8),
|
}) {
|
||||||
)
|
final isSelected = isSameDay(date, selectedDate);
|
||||||
: const SizedBox();
|
final isSidebarSelected =
|
||||||
},
|
selectedDateFromSideBarCalender != null &&
|
||||||
pageViewPhysics: const NeverScrollableScrollPhysics(),
|
isSameDay(
|
||||||
key: ValueKey(weekStart),
|
date, selectedDateFromSideBarCalender!);
|
||||||
controller: eventController,
|
if (isSidebarSelected && !isSelected) {
|
||||||
initialDay: weekStart,
|
return Container(
|
||||||
startHour: startHour - 1,
|
height: height,
|
||||||
endHour: endHour,
|
width: width,
|
||||||
heightPerMinute: 1.1,
|
decoration: BoxDecoration(
|
||||||
showLiveTimeLineInAllDays: false,
|
color: Colors.orange.withOpacity(0.13),
|
||||||
showVerticalLines: true,
|
),
|
||||||
emulateVerticalOffsetBy: -80,
|
);
|
||||||
startDay: WeekDays.monday,
|
} else if (isSelected) {
|
||||||
liveTimeIndicatorSettings: const LiveTimeIndicatorSettings(
|
return Container(
|
||||||
showBullet: false,
|
height: height,
|
||||||
height: 0,
|
width: width,
|
||||||
),
|
decoration: BoxDecoration(
|
||||||
weekDayBuilder: (date) {
|
color:
|
||||||
return WeekDayHeader(
|
ColorsManager.spaceColor.withOpacity(0.07),
|
||||||
date: date,
|
),
|
||||||
isSelectedDay: isSameDay(date, selectedDate),
|
);
|
||||||
);
|
}
|
||||||
},
|
return const SizedBox.shrink();
|
||||||
timeLineBuilder: (date) {
|
},
|
||||||
return TimeLineWidget(date: date);
|
|
||||||
},
|
// weekDetectorBuilder: ({
|
||||||
timeLineWidth: timeLineWidth,
|
// required date,
|
||||||
weekPageHeaderBuilder: (start, end) => Container(),
|
// required height,
|
||||||
weekTitleHeight: 60,
|
// required heightPerMinute,
|
||||||
weekNumberBuilder: (firstDayOfWeek) => Padding(
|
// required minuteSlotSize,
|
||||||
padding: const EdgeInsets.only(right: 15, bottom: 10),
|
// required width,
|
||||||
child: Column(
|
// }) {
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
// return isInRange(date, highlightStart, highlightEnd)
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
// ? HatchedColumnBackground(
|
||||||
children: [
|
// backgroundColor: ColorsManager.grey800,
|
||||||
Text(
|
// lineColor: ColorsManager.textGray,
|
||||||
firstDayOfWeek.timeZoneName.replaceAll(':00', ''),
|
// opacity: 0.3,
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
// stripeSpacing: 12,
|
||||||
fontSize: 12,
|
// borderRadius: BorderRadius.circular(8),
|
||||||
color: ColorsManager.blackColor,
|
// )
|
||||||
fontWeight: FontWeight.w400,
|
// : const SizedBox();
|
||||||
),
|
// },
|
||||||
|
pageViewPhysics: const NeverScrollableScrollPhysics(),
|
||||||
|
key: ValueKey(weekStart),
|
||||||
|
controller: eventController,
|
||||||
|
initialDay: weekStart,
|
||||||
|
startHour: startHour - 1,
|
||||||
|
endHour: endHour,
|
||||||
|
heightPerMinute: 1.7,
|
||||||
|
showLiveTimeLineInAllDays: false,
|
||||||
|
showVerticalLines: true,
|
||||||
|
emulateVerticalOffsetBy: -95,
|
||||||
|
startDay: WeekDays.monday,
|
||||||
|
liveTimeIndicatorSettings:
|
||||||
|
const LiveTimeIndicatorSettings(
|
||||||
|
showBullet: false,
|
||||||
|
height: 0,
|
||||||
|
),
|
||||||
|
weekDayBuilder: (date) {
|
||||||
|
return WeekDayHeader(
|
||||||
|
date: date,
|
||||||
|
isSelectedDay: isSameDay(date, selectedDate),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
timeLineBuilder: (date) {
|
||||||
|
return TimeLineWidget(date: date);
|
||||||
|
},
|
||||||
|
timeLineWidth: timeLineWidth,
|
||||||
|
weekPageHeaderBuilder: (start, end) => Container(),
|
||||||
|
weekTitleHeight: 90,
|
||||||
|
weekNumberBuilder: (firstDayOfWeek) => Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 15, bottom: 10),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
firstDayOfWeek.timeZoneName
|
||||||
|
.replaceAll(':00', ''),
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium
|
||||||
|
?.copyWith(
|
||||||
|
fontSize: 12,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
eventTileBuilder: (date, events, boundary, start, end) {
|
||||||
|
return EventTileWidget(
|
||||||
|
events: events,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
eventTileBuilder: (date, events, boundary, start, end) {
|
|
||||||
return EventTileWidget(
|
|
||||||
events: events,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
if (selectedDayIndex >= 0)
|
|
||||||
Positioned(
|
|
||||||
left: (timeLineWidth + 3) +
|
|
||||||
(dayColumnWidth - 8) * (selectedDayIndex - 0.01),
|
|
||||||
top: 0,
|
|
||||||
bottom: 0,
|
|
||||||
width: dayColumnWidth,
|
|
||||||
child: IgnorePointer(
|
|
||||||
child: Container(
|
|
||||||
margin: const EdgeInsets.symmetric(
|
|
||||||
vertical: 0, horizontal: 4),
|
|
||||||
color: ColorsManager.spaceColor.withOpacity(0.07),
|
|
||||||
),
|
),
|
||||||
),
|
Positioned(
|
||||||
),
|
right: 0,
|
||||||
if (selectedSidebarIndex >= 0 &&
|
top: 50,
|
||||||
selectedSidebarIndex != selectedDayIndex)
|
bottom: 0,
|
||||||
Positioned(
|
child: IgnorePointer(
|
||||||
left: (timeLineWidth + 3) +
|
child: Container(
|
||||||
(dayColumnWidth - 8) * (selectedSidebarIndex - 0.01),
|
width: 1,
|
||||||
top: 0,
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
bottom: 0,
|
),
|
||||||
width: dayColumnWidth,
|
),
|
||||||
child: IgnorePointer(
|
|
||||||
child: Container(
|
|
||||||
margin: const EdgeInsets.symmetric(
|
|
||||||
vertical: 0, horizontal: 4),
|
|
||||||
color: Colors.orange.withOpacity(0.14),
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: 0,
|
|
||||||
top: 50,
|
|
||||||
bottom: 0,
|
|
||||||
child: IgnorePointer(
|
|
||||||
child: Container(
|
|
||||||
width: 1,
|
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
));
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DateTime> _getWeekDays(DateTime date) {
|
|
||||||
final int weekday = date.weekday;
|
|
||||||
final DateTime monday = date.subtract(Duration(days: weekday - 1));
|
|
||||||
return List.generate(7, (i) => monday.add(Duration(days: i)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSameDay(DateTime d1, DateTime d2) {
|
bool isSameDay(DateTime d1, DateTime d2) {
|
||||||
|
@ -132,6 +132,8 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
controller: _horizontalScrollController,
|
controller: _horizontalScrollController,
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
|
physics:
|
||||||
|
widget.isEmpty ? const NeverScrollableScrollPhysics() : null,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: _totalTableWidth,
|
width: _totalTableWidth,
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -164,7 +166,6 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: widget.isEmpty
|
child: widget.isEmpty
|
||||||
? _buildEmptyState()
|
? _buildEmptyState()
|
||||||
@ -265,7 +266,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: widget.size.height * 0.5),
|
SizedBox(height: widget.size.height * 0.2),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -46,15 +46,16 @@ class DeviceManagementBloc
|
|||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
|
|
||||||
if (spaceBloc.state.selectedCommunities.isEmpty) {
|
if (spaceBloc.state.selectedCommunities.isEmpty) {
|
||||||
devices = await DevicesManagementApi().fetchDevices('', '', projectUuid);
|
devices = await DevicesManagementApi().fetchDevices(
|
||||||
|
projectUuid,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
for (final community in spaceBloc.state.selectedCommunities) {
|
for (var community in spaceBloc.state.selectedCommunities) {
|
||||||
final spacesList =
|
final spacesList =
|
||||||
spaceBloc.state.selectedCommunityAndSpaces[community] ?? [];
|
spaceBloc.state.selectedCommunityAndSpaces[community] ?? [];
|
||||||
for (final space in spacesList) {
|
devices.addAll(await DevicesManagementApi().fetchDevices(projectUuid,
|
||||||
devices.addAll(await DevicesManagementApi()
|
spacesId: spacesList,
|
||||||
.fetchDevices(community, space, projectUuid));
|
communities: spaceBloc.state.selectedCommunities));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +159,8 @@ class DeviceManagementBloc
|
|||||||
add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
|
add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSelectDevice(SelectDevice event, Emitter<DeviceManagementState> emit) {
|
void _onSelectDevice(
|
||||||
|
SelectDevice event, Emitter<DeviceManagementState> emit) {
|
||||||
final selectedUuid = event.selectedDevice.uuid;
|
final selectedUuid = event.selectedDevice.uuid;
|
||||||
|
|
||||||
if (_selectedDevices.any((device) => device.uuid == selectedUuid)) {
|
if (_selectedDevices.any((device) => device.uuid == selectedUuid)) {
|
||||||
@ -254,7 +256,8 @@ class DeviceManagementBloc
|
|||||||
_onlineCount = _devices.where((device) => device.online == true).length;
|
_onlineCount = _devices.where((device) => device.online == true).length;
|
||||||
_offlineCount = _devices.where((device) => device.online == false).length;
|
_offlineCount = _devices.where((device) => device.online == false).length;
|
||||||
_lowBatteryCount = _devices
|
_lowBatteryCount = _devices
|
||||||
.where((device) => device.batteryLevel != null && device.batteryLevel! < 20)
|
.where((device) =>
|
||||||
|
device.batteryLevel != null && device.batteryLevel! < 20)
|
||||||
.length;
|
.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +274,8 @@ class DeviceManagementBloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSearchDevices(SearchDevices event, Emitter<DeviceManagementState> emit) {
|
void _onSearchDevices(
|
||||||
|
SearchDevices event, Emitter<DeviceManagementState> emit) {
|
||||||
if ((event.community == null || event.community!.isEmpty) &&
|
if ((event.community == null || event.community!.isEmpty) &&
|
||||||
(event.unitName == null || event.unitName!.isEmpty) &&
|
(event.unitName == null || event.unitName!.isEmpty) &&
|
||||||
(event.deviceNameOrProductName == null ||
|
(event.deviceNameOrProductName == null ||
|
||||||
@ -435,8 +439,8 @@ class DeviceManagementBloc
|
|||||||
final selectedDevices = loaded.selectedDevice?.map((device) {
|
final selectedDevices = loaded.selectedDevice?.map((device) {
|
||||||
if (device.uuid == event.deviceId) {
|
if (device.uuid == event.deviceId) {
|
||||||
return device.copyWith(
|
return device.copyWith(
|
||||||
subspace:
|
subspace: device.subspace
|
||||||
device.subspace?.copyWith(subspaceName: event.newSubSpaceName));
|
?.copyWith(subspaceName: event.newSubSpaceName));
|
||||||
}
|
}
|
||||||
return device;
|
return device;
|
||||||
}).toList();
|
}).toList();
|
||||||
|
@ -24,12 +24,12 @@ class DeviceManagementPage extends StatefulWidget with HelperResponsiveLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DeviceManagementPageState extends State<DeviceManagementPage> {
|
class _DeviceManagementPageState extends State<DeviceManagementPage> {
|
||||||
|
@override
|
||||||
@override
|
|
||||||
void initState() {
|
void initState() {
|
||||||
context.read<SpaceTreeBloc>().add(InitialEvent());
|
context.read<SpaceTreeBloc>().add(InitialEvent());
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
@ -90,7 +90,7 @@ class _DeviceManagementPageState extends State<DeviceManagementPage> {
|
|||||||
const TriggerSwitchTabsEvent(isRoutineTab: true));
|
const TriggerSwitchTabsEvent(isRoutineTab: true));
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Routines',
|
'Workflow Automation',
|
||||||
style: context.textTheme.titleMedium?.copyWith(
|
style: context.textTheme.titleMedium?.copyWith(
|
||||||
color: state.routineTab
|
color: state.routineTab
|
||||||
? ColorsManager.whiteColors
|
? ColorsManager.whiteColors
|
||||||
|
@ -6,6 +6,7 @@ import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_
|
|||||||
import 'package:syncrow_web/pages/device_managment/garage_door/helper/garage_door_helper.dart';
|
import 'package:syncrow_web/pages/device_managment/garage_door/helper/garage_door_helper.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_model.dart';
|
import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_model.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/garage_door/schedule_view/schedule_garage_view.dart';
|
import 'package:syncrow_web/pages/device_managment/garage_door/schedule_view/schedule_garage_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||||
@ -94,11 +95,18 @@ class GarageDoorControlView extends StatelessWidget
|
|||||||
FetchGarageDoorSchedulesEvent(
|
FetchGarageDoorSchedulesEvent(
|
||||||
deviceId: deviceId, category: 'doorcontact_state'),
|
deviceId: deviceId, category: 'doorcontact_state'),
|
||||||
);
|
);
|
||||||
showDialog(
|
|
||||||
|
showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => BlocProvider.value(
|
builder: (ctx) => BlocProvider.value(
|
||||||
value: BlocProvider.of<GarageDoorBloc>(context),
|
value: BlocProvider.of<GarageDoorBloc>(context),
|
||||||
child: BuildGarageDoorScheduleView(status: status),
|
child: BuildScheduleView(
|
||||||
|
deviceUuid: deviceId,
|
||||||
|
category: 'Timer',
|
||||||
|
code: 'doorcontact_state',
|
||||||
|
countdownCode: 'Timer',
|
||||||
|
deviceType: 'GD',
|
||||||
|
),
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
name: 'Scheduling',
|
name: 'Scheduling',
|
||||||
|
@ -287,7 +287,8 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
|
|||||||
try {
|
try {
|
||||||
if (state is ScheduleLoaded) {
|
if (state is ScheduleLoaded) {
|
||||||
Status status = Status(code: '', value: '');
|
Status status = Status(code: '', value: '');
|
||||||
if (event.deviceType == 'CUR_2') {
|
if (event.deviceType == 'CUR_2' ||
|
||||||
|
event.deviceType == 'GD' ) {
|
||||||
status = status.copyWith(
|
status = status.copyWith(
|
||||||
code: 'control',
|
code: 'control',
|
||||||
value: event.functionOn == true ? 'open' : 'close');
|
value: event.functionOn == true ? 'open' : 'close');
|
||||||
|
@ -29,7 +29,9 @@ class CountdownModeButtons extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
|
elevation: 2.5,
|
||||||
height: 40,
|
height: 40,
|
||||||
|
borderRadius: 8,
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
backgroundColor: ColorsManager.boxColor,
|
backgroundColor: ColorsManager.boxColor,
|
||||||
child: Text('Cancel', style: context.textTheme.bodyMedium),
|
child: Text('Cancel', style: context.textTheme.bodyMedium),
|
||||||
@ -39,6 +41,8 @@ class CountdownModeButtons extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: isActive
|
child: isActive
|
||||||
? DefaultButton(
|
? DefaultButton(
|
||||||
|
elevation: 2.5,
|
||||||
|
borderRadius: 8,
|
||||||
height: 40,
|
height: 40,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.read<ScheduleBloc>().add(
|
context.read<ScheduleBloc>().add(
|
||||||
@ -49,10 +53,12 @@ class CountdownModeButtons extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
backgroundColor: Colors.red,
|
backgroundColor: ColorsManager.red100,
|
||||||
child: const Text('Stop'),
|
child: const Text('Stop'),
|
||||||
)
|
)
|
||||||
: DefaultButton(
|
: DefaultButton(
|
||||||
|
elevation: 2.5,
|
||||||
|
borderRadius: 8,
|
||||||
height: 40,
|
height: 40,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.read<ScheduleBloc>().add(
|
context.read<ScheduleBloc>().add(
|
||||||
@ -63,7 +69,7 @@ class CountdownModeButtons extends StatelessWidget {
|
|||||||
countDownCode: countDownCode),
|
countDownCode: countDownCode),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
backgroundColor: ColorsManager.primaryColor,
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
child: const Text('Save'),
|
child: const Text('Save'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -226,6 +226,7 @@ class _CountdownInchingViewState extends State<CountdownInchingView> {
|
|||||||
index.toString().padLeft(2, '0'),
|
index.toString().padLeft(2, '0'),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
color: isActive ? ColorsManager.grayColor : Colors.black,
|
color: isActive ? ColorsManager.grayColor : Colors.black,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -240,7 +241,8 @@ class _CountdownInchingViewState extends State<CountdownInchingView> {
|
|||||||
label,
|
label,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: ColorsManager.grayColor,
|
color: ColorsManager.grayColor,
|
||||||
fontSize: 18,
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -63,7 +63,7 @@ class InchingModeButtons extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
backgroundColor: ColorsManager.primaryColor,
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
child: const Text('Save'),
|
child: const Text('Save'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -57,7 +57,7 @@ class BuildScheduleView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const ScheduleHeader(),
|
const ScheduleHeader(),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
if (deviceType == 'CUR_2')
|
if (deviceType == 'CUR_2' || deviceType == 'GD')
|
||||||
const SizedBox()
|
const SizedBox()
|
||||||
else
|
else
|
||||||
ScheduleModeSelector(
|
ScheduleModeSelector(
|
||||||
@ -77,7 +77,7 @@ class BuildScheduleView extends StatelessWidget {
|
|||||||
category: category,
|
category: category,
|
||||||
time: '',
|
time: '',
|
||||||
function: Status(
|
function: Status(
|
||||||
code: code.toString(), value: null),
|
code: code.toString(), value: true),
|
||||||
days: [],
|
days: [],
|
||||||
),
|
),
|
||||||
isEdit: false,
|
isEdit: false,
|
||||||
@ -96,7 +96,7 @@ class BuildScheduleView extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (deviceType != 'CUR_2')
|
if (deviceType != 'CUR_2'|| deviceType != 'GD')
|
||||||
if (state.scheduleMode == ScheduleModes.countdown ||
|
if (state.scheduleMode == ScheduleModes.countdown ||
|
||||||
state.scheduleMode == ScheduleModes.inching)
|
state.scheduleMode == ScheduleModes.inching)
|
||||||
CountdownInchingView(
|
CountdownInchingView(
|
||||||
|
@ -13,9 +13,9 @@ class ScheduleHeader extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
'Scheduling',
|
'Scheduling',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
color: ColorsManager.primaryColorWithOpacity,
|
||||||
fontSize: 22,
|
fontWeight: FontWeight.w700,
|
||||||
color: ColorsManager.dialogBlueTitle,
|
fontSize: 30,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
|
@ -24,12 +24,13 @@ class ScheduleManagementUI extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 170,
|
width: 177,
|
||||||
height: 40,
|
height: 40,
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
borderColor: ColorsManager.boxColor,
|
borderWidth: 4,
|
||||||
padding: 2,
|
borderColor: ColorsManager.neutralGray,
|
||||||
backgroundColor: ColorsManager.graysColor,
|
padding: 8,
|
||||||
|
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||||
borderRadius: 15,
|
borderRadius: 15,
|
||||||
onPressed: onAddSchedule,
|
onPressed: onAddSchedule,
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -19,6 +19,8 @@ class ScheduleModeButtons extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
|
elevation: 2.5,
|
||||||
|
borderRadius: 8,
|
||||||
height: 40,
|
height: 40,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
@ -33,9 +35,11 @@ class ScheduleModeButtons extends StatelessWidget {
|
|||||||
const SizedBox(width: 20),
|
const SizedBox(width: 20),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
|
elevation: 2.5,
|
||||||
|
borderRadius: 8,
|
||||||
height: 40,
|
height: 40,
|
||||||
onPressed: onSave,
|
onPressed: onSave,
|
||||||
backgroundColor: ColorsManager.primaryColor,
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
child: const Text('Save'),
|
child: const Text('Save'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -35,12 +35,12 @@ class ScheduleModeSelector extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: [
|
children: [
|
||||||
_buildRadioTile(
|
_buildRadioTile(
|
||||||
context, 'Countdown', ScheduleModes.countdown, currentMode),
|
context, 'Countdown', ScheduleModes.countdown, currentMode),
|
||||||
_buildRadioTile(
|
_buildRadioTile(
|
||||||
context, 'Schedule', ScheduleModes.schedule, currentMode),
|
context, 'Schedule', ScheduleModes.schedule, currentMode),
|
||||||
|
const Spacer(flex: 1),
|
||||||
// _buildRadioTile(
|
// _buildRadioTile(
|
||||||
// context, 'Circulate', ScheduleModes.circulate, currentMode),
|
// context, 'Circulate', ScheduleModes.circulate, currentMode),
|
||||||
// _buildRadioTile(
|
// _buildRadioTile(
|
||||||
@ -65,6 +65,7 @@ class ScheduleModeSelector extends StatelessWidget {
|
|||||||
style: context.textTheme.bodySmall!.copyWith(
|
style: context.textTheme.bodySmall!.copyWith(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
leading: Radio<ScheduleModes>(
|
leading: Radio<ScheduleModes>(
|
||||||
|
@ -194,7 +194,7 @@ class _ScheduleTableView extends StatelessWidget {
|
|||||||
child: Text(_getSelectedDays(
|
child: Text(_getSelectedDays(
|
||||||
ScheduleModel.parseSelectedDays(schedule.days)))),
|
ScheduleModel.parseSelectedDays(schedule.days)))),
|
||||||
Center(child: Text(formatIsoStringToTime(schedule.time, context))),
|
Center(child: Text(formatIsoStringToTime(schedule.time, context))),
|
||||||
if (deviceType == 'CUR_2')
|
if (deviceType == 'CUR_2' || deviceType == 'GD')
|
||||||
Center(
|
Center(
|
||||||
child: Text(schedule.function.value == true ? 'open' : 'close'))
|
child: Text(schedule.function.value == true ? 'open' : 'close'))
|
||||||
else
|
else
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_buttons.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart';
|
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class ScheduleDialogHelper {
|
class ScheduleDialogHelper {
|
||||||
static const List<String> allDays = [
|
static const List<String> allDays = [
|
||||||
@ -21,7 +23,7 @@ class ScheduleDialogHelper {
|
|||||||
required String deviceType,
|
required String deviceType,
|
||||||
}) {
|
}) {
|
||||||
bool temp;
|
bool temp;
|
||||||
if (deviceType == 'CUR_2') {
|
if (deviceType == 'CUR_2' || deviceType == 'GD') {
|
||||||
temp = schedule!.function.value == 'open' ? true : false;
|
temp = schedule!.function.value == 'open' ? true : false;
|
||||||
} else {
|
} else {
|
||||||
temp = schedule!.function.value;
|
temp = schedule!.function.value;
|
||||||
@ -56,8 +58,9 @@ class ScheduleDialogHelper {
|
|||||||
Text(
|
Text(
|
||||||
isEdit ? 'Edit Schedule' : 'Add Schedule',
|
isEdit ? 'Edit Schedule' : 'Add Schedule',
|
||||||
style: Theme.of(context).textTheme.titleLarge!.copyWith(
|
style: Theme.of(context).textTheme.titleLarge!.copyWith(
|
||||||
color: Colors.blue,
|
color: ColorsManager.primaryColorWithOpacity,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.w700,
|
||||||
|
fontSize: 30,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(),
|
const SizedBox(),
|
||||||
@ -69,9 +72,9 @@ class ScheduleDialogHelper {
|
|||||||
height: 40,
|
height: 40,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Colors.grey[200],
|
backgroundColor: ColorsManager.boxColor,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(15),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@ -110,39 +113,27 @@ class ScheduleDialogHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
SizedBox(
|
ScheduleModeButtons(
|
||||||
width: 100,
|
onSave: () {
|
||||||
child: OutlinedButton(
|
dynamic temp;
|
||||||
onPressed: () {
|
if (deviceType == 'CUR_2' || deviceType == 'GD') {
|
||||||
Navigator.pop(ctx, null);
|
temp = functionOn! ? 'open' : 'close';
|
||||||
},
|
} else {
|
||||||
child: const Text('Cancel'),
|
temp = functionOn;
|
||||||
),
|
}
|
||||||
|
final entry = ScheduleEntry(
|
||||||
|
category: schedule?.category ?? 'switch_1',
|
||||||
|
time: _formatTimeOfDayToISO(selectedTime),
|
||||||
|
function: Status(
|
||||||
|
code: code ?? 'switch_1',
|
||||||
|
value: temp,
|
||||||
|
),
|
||||||
|
days: _convertSelectedDaysToStrings(selectedDays),
|
||||||
|
scheduleId: schedule.scheduleId,
|
||||||
|
);
|
||||||
|
Navigator.pop(ctx, entry);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
SizedBox(
|
|
||||||
width: 100,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
dynamic temp;
|
|
||||||
if (deviceType == 'CUR_2') {
|
|
||||||
temp = functionOn! ? 'open' : 'close';
|
|
||||||
} else {
|
|
||||||
temp = functionOn;
|
|
||||||
}
|
|
||||||
final entry = ScheduleEntry(
|
|
||||||
category: schedule?.category ?? 'switch_1',
|
|
||||||
time: _formatTimeOfDayToISO(selectedTime),
|
|
||||||
function: Status(
|
|
||||||
code: code ?? 'switch_1',
|
|
||||||
value: temp,
|
|
||||||
),
|
|
||||||
days: _convertSelectedDaysToStrings(selectedDays),
|
|
||||||
scheduleId: schedule.scheduleId,
|
|
||||||
);
|
|
||||||
Navigator.pop(ctx, entry);
|
|
||||||
},
|
|
||||||
child: const Text('Save'),
|
|
||||||
)),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -211,18 +202,23 @@ class ScheduleDialogHelper {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Radio<bool>(
|
Radio<bool>(
|
||||||
|
activeColor: ColorsManager.secondaryColor,
|
||||||
|
focusColor: ColorsManager.secondaryColor,
|
||||||
value: true,
|
value: true,
|
||||||
groupValue: isOn,
|
groupValue: isOn,
|
||||||
onChanged: (val) => onChanged(true),
|
onChanged: (val) => onChanged(true),
|
||||||
),
|
),
|
||||||
Text(categor == 'CUR_2' ? 'open' : 'On'),
|
Text(categor == 'CUR_2' || categor == 'GD' ? 'open' : 'On'),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Radio<bool>(
|
Radio<bool>(
|
||||||
|
activeColor: ColorsManager.secondaryColor,
|
||||||
|
focusColor: ColorsManager.secondaryColor,
|
||||||
|
|
||||||
value: false,
|
value: false,
|
||||||
groupValue: isOn,
|
groupValue: isOn,
|
||||||
onChanged: (val) => onChanged(false),
|
onChanged: (val) => onChanged(false),
|
||||||
),
|
),
|
||||||
Text(categor == 'CUR_2' ? 'close' : 'Off'),
|
Text(categor == 'CUR_2' || categor == 'GD' ? 'close' : 'Off'),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -153,6 +153,7 @@ class EditUserModel {
|
|||||||
final String? jobTitle; // can be empty
|
final String? jobTitle; // can be empty
|
||||||
final String roleType; // e.g. "ADMIN"
|
final String roleType; // e.g. "ADMIN"
|
||||||
final List<UserSpaceModel> spaces;
|
final List<UserSpaceModel> spaces;
|
||||||
|
final String? companyName;
|
||||||
|
|
||||||
EditUserModel({
|
EditUserModel({
|
||||||
required this.uuid,
|
required this.uuid,
|
||||||
@ -167,6 +168,7 @@ class EditUserModel {
|
|||||||
required this.jobTitle,
|
required this.jobTitle,
|
||||||
required this.roleType,
|
required this.roleType,
|
||||||
required this.spaces,
|
required this.spaces,
|
||||||
|
this.companyName,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Create a [UserData] from JSON data
|
/// Create a [UserData] from JSON data
|
||||||
@ -182,6 +184,7 @@ class EditUserModel {
|
|||||||
invitedBy: json['invitedBy'] as String,
|
invitedBy: json['invitedBy'] as String,
|
||||||
phoneNumber: json['phoneNumber'] ?? '',
|
phoneNumber: json['phoneNumber'] ?? '',
|
||||||
jobTitle: json['jobTitle'] ?? '',
|
jobTitle: json['jobTitle'] ?? '',
|
||||||
|
companyName: json['companyName'] as String?,
|
||||||
roleType: json['roleType'] as String,
|
roleType: json['roleType'] as String,
|
||||||
spaces: (json['spaces'] as List<dynamic>)
|
spaces: (json['spaces'] as List<dynamic>)
|
||||||
.map((e) => UserSpaceModel.fromJson(e as Map<String, dynamic>))
|
.map((e) => UserSpaceModel.fromJson(e as Map<String, dynamic>))
|
||||||
|
@ -12,7 +12,7 @@ class RolesUserModel {
|
|||||||
final dynamic jobTitle;
|
final dynamic jobTitle;
|
||||||
final dynamic createdDate;
|
final dynamic createdDate;
|
||||||
final dynamic createdTime;
|
final dynamic createdTime;
|
||||||
|
final String? companyName;
|
||||||
RolesUserModel({
|
RolesUserModel({
|
||||||
required this.uuid,
|
required this.uuid,
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
@ -27,6 +27,7 @@ class RolesUserModel {
|
|||||||
this.jobTitle,
|
this.jobTitle,
|
||||||
required this.createdDate,
|
required this.createdDate,
|
||||||
required this.createdTime,
|
required this.createdTime,
|
||||||
|
this.companyName,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory RolesUserModel.fromJson(Map<String, dynamic> json) {
|
factory RolesUserModel.fromJson(Map<String, dynamic> json) {
|
||||||
@ -47,6 +48,7 @@ class RolesUserModel {
|
|||||||
: json['jobTitle'],
|
: json['jobTitle'],
|
||||||
createdDate: json['createdDate'],
|
createdDate: json['createdDate'],
|
||||||
createdTime: json['createdTime'],
|
createdTime: json['createdTime'],
|
||||||
|
companyName: json['companyName'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
final TextEditingController lastNameController = TextEditingController();
|
final TextEditingController lastNameController = TextEditingController();
|
||||||
final TextEditingController emailController = TextEditingController();
|
final TextEditingController emailController = TextEditingController();
|
||||||
final TextEditingController phoneController = TextEditingController();
|
final TextEditingController phoneController = TextEditingController();
|
||||||
final TextEditingController jobTitleController = TextEditingController();
|
final TextEditingController companyNameController = TextEditingController();
|
||||||
final TextEditingController roleSearchController = TextEditingController();
|
final TextEditingController roleSearchController = TextEditingController();
|
||||||
|
|
||||||
bool? isCompleteBasics;
|
bool? isCompleteBasics;
|
||||||
@ -352,7 +352,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
bool res = await UserPermissionApi().sendInviteUser(
|
bool res = await UserPermissionApi().sendInviteUser(
|
||||||
email: emailController.text,
|
email: emailController.text,
|
||||||
firstName: firstNameController.text,
|
firstName: firstNameController.text,
|
||||||
jobTitle: jobTitleController.text,
|
companyName: companyNameController.text,
|
||||||
lastName: lastNameController.text,
|
lastName: lastNameController.text,
|
||||||
phoneNumber: phoneController.text,
|
phoneNumber: phoneController.text,
|
||||||
roleUuid: roleSelected,
|
roleUuid: roleSelected,
|
||||||
@ -405,7 +405,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
bool res = await UserPermissionApi().editInviteUser(
|
bool res = await UserPermissionApi().editInviteUser(
|
||||||
userId: event.userId,
|
userId: event.userId,
|
||||||
firstName: firstNameController.text,
|
firstName: firstNameController.text,
|
||||||
jobTitle: jobTitleController.text,
|
companyName: companyNameController.text,
|
||||||
lastName: lastNameController.text,
|
lastName: lastNameController.text,
|
||||||
phoneNumber: phoneController.text,
|
phoneNumber: phoneController.text,
|
||||||
roleUuid: roleSelected,
|
roleUuid: roleSelected,
|
||||||
@ -455,7 +455,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
Future<void> checkEmail(
|
Future<void> checkEmail(
|
||||||
CheckEmailEvent event, Emitter<UsersState> emit) async {
|
CheckEmailEvent event, Emitter<UsersState> emit) async {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
String? res = await UserPermissionApi().checkEmail(
|
String? res = await UserPermissionApi().checkEmail(
|
||||||
emailController.text,
|
emailController.text,
|
||||||
);
|
);
|
||||||
checkEmailValid = res!;
|
checkEmailValid = res!;
|
||||||
@ -529,7 +529,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
lastNameController.text = res.lastName;
|
lastNameController.text = res.lastName;
|
||||||
emailController.text = res.email;
|
emailController.text = res.email;
|
||||||
phoneController.text = res.phoneNumber ?? '';
|
phoneController.text = res.phoneNumber ?? '';
|
||||||
jobTitleController.text = res.jobTitle ?? '';
|
companyNameController.text = res.companyName ?? '';
|
||||||
res.roleType;
|
res.roleType;
|
||||||
res.spaces.map((space) {
|
res.spaces.map((space) {
|
||||||
selectedIds.add(space.uuid);
|
selectedIds.add(space.uuid);
|
||||||
@ -645,7 +645,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
lastNameController.dispose();
|
lastNameController.dispose();
|
||||||
emailController.dispose();
|
emailController.dispose();
|
||||||
phoneController.dispose();
|
phoneController.dispose();
|
||||||
jobTitleController.dispose();
|
companyNameController.dispose();
|
||||||
roleSearchController.dispose();
|
roleSearchController.dispose();
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
@ -317,7 +317,7 @@ class BasicsView extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Job Title',
|
'Company Name',
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
@ -328,11 +328,11 @@ class BasicsView extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: _blocRole.jobTitleController,
|
controller: _blocRole.companyNameController,
|
||||||
style:
|
style:
|
||||||
const TextStyle(color: ColorsManager.blackColor),
|
const TextStyle(color: ColorsManager.blackColor),
|
||||||
decoration: inputTextFormDeco(
|
decoration: inputTextFormDeco(
|
||||||
hintText: "Job Title (Optional)")
|
hintText: 'Company Name (Optional)')
|
||||||
.copyWith(
|
.copyWith(
|
||||||
hintStyle: context.textTheme.bodyMedium?.copyWith(
|
hintStyle: context.textTheme.bodyMedium?.copyWith(
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
|
@ -411,7 +411,7 @@ class UsersPage extends StatelessWidget {
|
|||||||
titles: const [
|
titles: const [
|
||||||
"Full Name",
|
"Full Name",
|
||||||
"Email Address",
|
"Email Address",
|
||||||
"Job Title",
|
"Company Name",
|
||||||
"Role",
|
"Role",
|
||||||
"Creation Date",
|
"Creation Date",
|
||||||
"Creation Time",
|
"Creation Time",
|
||||||
@ -424,7 +424,7 @@ class UsersPage extends StatelessWidget {
|
|||||||
return [
|
return [
|
||||||
Text('${user.firstName} ${user.lastName}'),
|
Text('${user.firstName} ${user.lastName}'),
|
||||||
Text(user.email),
|
Text(user.email),
|
||||||
Text(user.jobTitle),
|
Center(child: Text(user.companyName ?? '-')),
|
||||||
Text(user.roleType ?? ''),
|
Text(user.roleType ?? ''),
|
||||||
Text(user.createdDate ?? ''),
|
Text(user.createdDate ?? ''),
|
||||||
Text(user.createdTime ?? ''),
|
Text(user.createdTime ?? ''),
|
||||||
|
@ -170,45 +170,45 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onLoadScenes(
|
Future<void> _onLoadScenes(
|
||||||
LoadScenes event, Emitter<RoutineState> emit) async {
|
LoadScenes event, Emitter<RoutineState> emit) async {
|
||||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
List<ScenesModel> scenes = [];
|
List<ScenesModel> scenes = [];
|
||||||
try {
|
try {
|
||||||
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
||||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
if (createRoutineBloc.selectedSpaceId == '' &&
|
if (createRoutineBloc.selectedSpaceId == '' &&
|
||||||
createRoutineBloc.selectedCommunityId == '') {
|
createRoutineBloc.selectedCommunityId == '') {
|
||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||||
List<String> spacesList =
|
List<String> spacesList =
|
||||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||||
for (var spaceId in spacesList) {
|
for (var spaceId in spacesList) {
|
||||||
scenes.addAll(
|
scenes.addAll(
|
||||||
await SceneApi.getScenes(spaceId, communityId, projectUuid));
|
await SceneApi.getScenes(spaceId, communityId, projectUuid));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
scenes.addAll(await SceneApi.getScenes(
|
||||||
|
createRoutineBloc.selectedSpaceId,
|
||||||
|
createRoutineBloc.selectedCommunityId,
|
||||||
|
projectUuid));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
scenes.addAll(await SceneApi.getScenes(
|
|
||||||
createRoutineBloc.selectedSpaceId,
|
|
||||||
createRoutineBloc.selectedCommunityId,
|
|
||||||
projectUuid));
|
|
||||||
}
|
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
scenes: scenes,
|
scenes: scenes,
|
||||||
isLoading: false,
|
|
||||||
));
|
|
||||||
} catch (e) {
|
|
||||||
emit(state.copyWith(
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
loadScenesErrorMessage: 'Failed to load scenes',
|
));
|
||||||
errorMessage: '',
|
} catch (e) {
|
||||||
loadAutomationErrorMessage: '',
|
emit(state.copyWith(
|
||||||
scenes: scenes));
|
isLoading: false,
|
||||||
|
loadScenesErrorMessage: 'Failed to load scenes',
|
||||||
|
errorMessage: '',
|
||||||
|
loadAutomationErrorMessage: '',
|
||||||
|
scenes: scenes));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onLoadAutomation(
|
Future<void> _onLoadAutomation(
|
||||||
LoadAutomation event, Emitter<RoutineState> emit) async {
|
LoadAutomation event, Emitter<RoutineState> emit) async {
|
||||||
@ -936,16 +936,19 @@ Future<void> _onLoadScenes(
|
|||||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||||
List<String> spacesList =
|
List<String> spacesList =
|
||||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||||
for (var spaceId in spacesList) {
|
|
||||||
devices.addAll(await DevicesManagementApi()
|
devices.addAll(await DevicesManagementApi().fetchDevices(
|
||||||
.fetchDevices(communityId, spaceId, projectUuid));
|
projectUuid,
|
||||||
}
|
spacesId: spacesList,
|
||||||
|
communities: spaceBloc.state.selectedCommunities,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
devices.addAll(await DevicesManagementApi().fetchDevices(
|
devices.addAll(await DevicesManagementApi().fetchDevices(
|
||||||
createRoutineBloc.selectedCommunityId,
|
projectUuid,
|
||||||
createRoutineBloc.selectedSpaceId,
|
spacesId: [createRoutineBloc.selectedSpaceId],
|
||||||
projectUuid));
|
communities: spaceBloc.state.selectedCommunities,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(state.copyWith(isLoading: false, devices: devices));
|
emit(state.copyWith(isLoading: false, devices: devices));
|
||||||
|
@ -96,9 +96,7 @@ class _WallPresenceSensorState extends State<FlushPresenceSensor> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
DialogHeader(widget.dialogType == 'THEN'
|
const DialogHeader('Presence Sensor'),
|
||||||
? 'Presence Sensor Functions'
|
|
||||||
: 'Presence Sensor Condition'),
|
|
||||||
Expanded(child: _buildMainContent(context, state)),
|
Expanded(child: _buildMainContent(context, state)),
|
||||||
_buildDialogFooter(context, state),
|
_buildDialogFooter(context, state),
|
||||||
],
|
],
|
||||||
|
@ -2,6 +2,8 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain
|
|||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
|
||||||
abstract final class SpacesRecursiveHelper {
|
abstract final class SpacesRecursiveHelper {
|
||||||
|
const SpacesRecursiveHelper._();
|
||||||
|
|
||||||
static List<SpaceModel> recusrivelyUpdate(
|
static List<SpaceModel> recusrivelyUpdate(
|
||||||
List<SpaceModel> spaces,
|
List<SpaceModel> spaces,
|
||||||
SpaceDetailsModel updatedSpace,
|
SpaceDetailsModel updatedSpace,
|
||||||
@ -40,4 +42,30 @@ abstract final class SpacesRecursiveHelper {
|
|||||||
final nonNullSpaces = updatedSpaces.whereType<SpaceModel>().toList();
|
final nonNullSpaces = updatedSpaces.whereType<SpaceModel>().toList();
|
||||||
return nonNullSpaces;
|
return nonNullSpaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static List<SpaceModel> recursivelyInsert({
|
||||||
|
required List<SpaceModel> spaces,
|
||||||
|
required String parentUuid,
|
||||||
|
required SpaceModel newSpace,
|
||||||
|
}) {
|
||||||
|
return spaces.map((space) {
|
||||||
|
final isParentSpace = space.uuid == parentUuid;
|
||||||
|
if (isParentSpace) {
|
||||||
|
return space.copyWith(
|
||||||
|
children: [...space.children, newSpace],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final hasChildren = space.children.isNotEmpty;
|
||||||
|
if (hasChildren) {
|
||||||
|
return space.copyWith(
|
||||||
|
children: recursivelyInsert(
|
||||||
|
spaces: space.children,
|
||||||
|
parentUuid: parentUuid,
|
||||||
|
newSpace: newSpace,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return space;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
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:syncrow_web/pages/space_management_v2/main_module/helpers/spaces_recursive_helper.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/models/space_connection_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/models/space_connection_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/models/space_reorder_data_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/models/space_reorder_data_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart';
|
||||||
@ -268,7 +269,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
Positioned(
|
Positioned(
|
||||||
left: createButtonX,
|
left: createButtonX,
|
||||||
top: createButtonY,
|
top: createButtonY,
|
||||||
child: CreateSpaceButton(communityUuid: widget.community.uuid),
|
child: CreateSpaceButton(community: widget.community),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -327,6 +328,19 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
onTap: () => SpaceDetailsDialogHelper.showCreate(
|
onTap: () => SpaceDetailsDialogHelper.showCreate(
|
||||||
context,
|
context,
|
||||||
communityUuid: widget.community.uuid,
|
communityUuid: widget.community.uuid,
|
||||||
|
parentUuid: space.uuid,
|
||||||
|
onSuccess: (updatedSpaceModel) {
|
||||||
|
final updatedSpaces = SpacesRecursiveHelper.recursivelyInsert(
|
||||||
|
spaces: widget.community.spaces,
|
||||||
|
parentUuid: space.uuid,
|
||||||
|
newSpace: updatedSpaceModel,
|
||||||
|
);
|
||||||
|
context.read<CommunitiesBloc>().add(
|
||||||
|
CommunitiesUpdateCommunity(
|
||||||
|
widget.community.copyWith(spaces: updatedSpaces),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -20,9 +20,9 @@ class CommunityStructureHeader extends StatelessWidget {
|
|||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: ColorsManager.shadowBlackColor,
|
color: ColorsManager.shadowBlackColor.withValues(alpha: 0.1),
|
||||||
blurRadius: 8,
|
blurRadius: 20,
|
||||||
offset: const Offset(0, 4),
|
offset: const Offset(0, 1),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_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/space_details/presentation/helpers/space_details_dialog_helper.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class CreateSpaceButton extends StatefulWidget {
|
class CreateSpaceButton extends StatefulWidget {
|
||||||
const CreateSpaceButton({
|
const CreateSpaceButton({
|
||||||
required this.communityUuid,
|
required this.community,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String communityUuid;
|
final CommunityModel community;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CreateSpaceButton> createState() => _CreateSpaceButtonState();
|
State<CreateSpaceButton> createState() => _CreateSpaceButtonState();
|
||||||
@ -25,7 +29,21 @@ class _CreateSpaceButtonState extends State<CreateSpaceButton> {
|
|||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => SpaceDetailsDialogHelper.showCreate(
|
onTap: () => SpaceDetailsDialogHelper.showCreate(
|
||||||
context,
|
context,
|
||||||
communityUuid: widget.communityUuid,
|
communityUuid: widget.community.uuid,
|
||||||
|
onSuccess: (updatedSpaceModel) {
|
||||||
|
final newCommunity = widget.community.copyWith(
|
||||||
|
spaces: [...widget.community.spaces, updatedSpaceModel],
|
||||||
|
);
|
||||||
|
context.read<CommunitiesBloc>().add(
|
||||||
|
CommunitiesUpdateCommunity(newCommunity),
|
||||||
|
);
|
||||||
|
context.read<CommunitiesTreeSelectionBloc>().add(
|
||||||
|
SelectSpaceEvent(
|
||||||
|
space: updatedSpaceModel,
|
||||||
|
community: newCommunity,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
onEnter: (_) => setState(() => _isHovered = true),
|
onEnter: (_) => setState(() => _isHovered = true),
|
||||||
|
@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/commun
|
|||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/create_space_button.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/create_space_button.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
||||||
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/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/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||||
|
|
||||||
class SpaceManagementCommunityStructure extends StatelessWidget {
|
class SpaceManagementCommunityStructure extends StatelessWidget {
|
||||||
@ -12,19 +13,35 @@ class SpaceManagementCommunityStructure extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final selectionBloc = context.watch<CommunitiesTreeSelectionBloc>().state;
|
return BlocBuilder<CommunitiesTreeSelectionBloc, CommunitiesTreeSelectionState>(
|
||||||
final selectedCommunity = selectionBloc.selectedCommunity;
|
builder: (context, state) {
|
||||||
final selectedSpace = selectionBloc.selectedSpace;
|
final selectedCommunity = state.selectedCommunity;
|
||||||
return Column(
|
final selectedSpace = state.selectedSpace;
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
if (selectedCommunity == null) {
|
||||||
const CommunityStructureHeader(),
|
return const SizedBox.shrink();
|
||||||
Visibility(
|
}
|
||||||
visible: selectedCommunity!.spaces.isNotEmpty,
|
|
||||||
replacement: _buildEmptyWidget(selectedCommunity),
|
return Column(
|
||||||
child: _buildCanvas(selectedCommunity, selectedSpace),
|
mainAxisSize: MainAxisSize.min,
|
||||||
),
|
children: [
|
||||||
],
|
const CommunityStructureHeader(),
|
||||||
|
BlocBuilder<CommunitiesBloc, CommunitiesState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
final community = state.communities.firstWhere(
|
||||||
|
(element) => element.uuid == selectedCommunity.uuid,
|
||||||
|
orElse: () => selectedCommunity,
|
||||||
|
);
|
||||||
|
return Visibility(
|
||||||
|
visible: community.spaces.isNotEmpty,
|
||||||
|
replacement: _buildEmptyWidget(community),
|
||||||
|
child: _buildCanvas(community, selectedSpace),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,11 +64,7 @@ class SpaceManagementCommunityStructure extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
spacer,
|
spacer,
|
||||||
Expanded(
|
Expanded(child: CreateSpaceButton(community: selectedCommunity)),
|
||||||
child: CreateSpaceButton(
|
|
||||||
communityUuid: selectedCommunity.uuid,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
spacer,
|
spacer,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_space/domain/services/create_space_service.dart';
|
||||||
|
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||||
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
|
||||||
|
final class RemoteCreateSpaceService implements CreateSpaceService {
|
||||||
|
const RemoteCreateSpaceService(this._httpService);
|
||||||
|
|
||||||
|
final HTTPService _httpService;
|
||||||
|
|
||||||
|
static const _defaultErrorMessage = 'Failed to create space';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<SpaceModel> createSpace(CreateSpaceParam param) async {
|
||||||
|
try {
|
||||||
|
final path = await _makeUrl(param);
|
||||||
|
final response = await _httpService.post(
|
||||||
|
path: path,
|
||||||
|
body: param.toJson(),
|
||||||
|
expectedResponseModel: (data) {
|
||||||
|
final response = data as Map<String, dynamic>;
|
||||||
|
final isSuccess = response['success'] as bool;
|
||||||
|
if (!isSuccess) {
|
||||||
|
throw APIException(response['error'] as String);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SpaceModel.fromJson(response['data'] as Map<String, dynamic>);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} on DioException catch (e) {
|
||||||
|
final message = e.response?.data as Map<String, dynamic>?;
|
||||||
|
final error = message?['error'] as Map<String, dynamic>?;
|
||||||
|
final errorMessage = error?['error'] as String? ?? '';
|
||||||
|
final formattedErrorMessage = [
|
||||||
|
_defaultErrorMessage,
|
||||||
|
errorMessage,
|
||||||
|
].join(': ');
|
||||||
|
throw APIException(formattedErrorMessage);
|
||||||
|
} catch (e) {
|
||||||
|
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||||
|
throw APIException(formattedErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> _makeUrl(CreateSpaceParam param) async {
|
||||||
|
final projectUuid = await ProjectManager.getProjectUUID();
|
||||||
|
if (projectUuid == null || projectUuid.isEmpty) {
|
||||||
|
throw APIException('Project UUID is not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
final communityUuid = param.communityUuid;
|
||||||
|
if (communityUuid.isEmpty) {
|
||||||
|
throw APIException('Community UUID is not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
return '/projects/$projectUuid/communities/$communityUuid/spaces';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
|
||||||
|
class CreateSpaceParam {
|
||||||
|
final String communityUuid;
|
||||||
|
final SpaceDetailsModel space;
|
||||||
|
final String? parentUuid;
|
||||||
|
|
||||||
|
const CreateSpaceParam({
|
||||||
|
required this.communityUuid,
|
||||||
|
required this.space,
|
||||||
|
required this.parentUuid,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'parentUuid': parentUuid,
|
||||||
|
...space.toJson(),
|
||||||
|
'x': 0,
|
||||||
|
'y': 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart';
|
||||||
|
|
||||||
|
abstract interface class CreateSpaceService {
|
||||||
|
Future<SpaceModel> createSpace(CreateSpaceParam param);
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_space/domain/services/create_space_service.dart';
|
||||||
|
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||||
|
|
||||||
|
part 'create_space_event.dart';
|
||||||
|
part 'create_space_state.dart';
|
||||||
|
|
||||||
|
class CreateSpaceBloc extends Bloc<CreateSpaceEvent, CreateSpaceState> {
|
||||||
|
CreateSpaceBloc(
|
||||||
|
this._createSpaceService,
|
||||||
|
) : super(const CreateSpaceInitial()) {
|
||||||
|
on<CreateSpace>(_onCreateSpace);
|
||||||
|
}
|
||||||
|
|
||||||
|
final CreateSpaceService _createSpaceService;
|
||||||
|
|
||||||
|
Future<void> _onCreateSpace(
|
||||||
|
CreateSpace event,
|
||||||
|
Emitter<CreateSpaceState> emit,
|
||||||
|
) async {
|
||||||
|
emit(const CreateSpaceLoading());
|
||||||
|
try {
|
||||||
|
final result = await _createSpaceService.createSpace(event.param);
|
||||||
|
emit(CreateSpaceSuccess(result));
|
||||||
|
} on APIException catch (e) {
|
||||||
|
emit(CreateSpaceFailure(e.message));
|
||||||
|
} catch (e) {
|
||||||
|
emit(CreateSpaceFailure(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
part of 'create_space_bloc.dart';
|
||||||
|
|
||||||
|
sealed class CreateSpaceEvent extends Equatable {
|
||||||
|
const CreateSpaceEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class CreateSpace extends CreateSpaceEvent {
|
||||||
|
const CreateSpace(this.param);
|
||||||
|
|
||||||
|
final CreateSpaceParam param;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [param];
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
part of 'create_space_bloc.dart';
|
||||||
|
|
||||||
|
sealed class CreateSpaceState extends Equatable {
|
||||||
|
const CreateSpaceState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class CreateSpaceInitial extends CreateSpaceState {
|
||||||
|
const CreateSpaceInitial();
|
||||||
|
}
|
||||||
|
|
||||||
|
final class CreateSpaceLoading extends CreateSpaceState {
|
||||||
|
const CreateSpaceLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
final class CreateSpaceSuccess extends CreateSpaceState {
|
||||||
|
const CreateSpaceSuccess(this.space);
|
||||||
|
|
||||||
|
final SpaceModel space;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [space];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class CreateSpaceFailure extends CreateSpaceState {
|
||||||
|
const CreateSpaceFailure(this.errorMessage);
|
||||||
|
|
||||||
|
final String errorMessage;
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart';
|
||||||
|
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/products/domain/models/product.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
class ProductAllocation extends Equatable {
|
||||||
|
final String uuid;
|
||||||
|
final Product product;
|
||||||
|
final Tag tag;
|
||||||
|
|
||||||
|
const ProductAllocation({
|
||||||
|
required this.uuid,
|
||||||
|
required this.product,
|
||||||
|
required this.tag,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ProductAllocation.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ProductAllocation(
|
||||||
|
uuid: json['uuid'] as String? ?? const Uuid().v4(),
|
||||||
|
product: Product.fromJson(json['product'] as Map<String, dynamic>),
|
||||||
|
tag: Tag.fromJson(json['tag'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductAllocation copyWith({
|
||||||
|
String? uuid,
|
||||||
|
Product? product,
|
||||||
|
Tag? tag,
|
||||||
|
}) {
|
||||||
|
return ProductAllocation(
|
||||||
|
uuid: uuid ?? this.uuid,
|
||||||
|
product: product ?? this.product,
|
||||||
|
tag: tag ?? this.tag,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final isNewTag = tag.uuid.isEmpty;
|
||||||
|
return <String, dynamic>{
|
||||||
|
if (isNewTag) 'tagName': tag.name else 'tagUuid': tag.uuid,
|
||||||
|
'productUuid': product.uuid,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [uuid, product, tag];
|
||||||
|
}
|
@ -1,8 +1,7 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/products/domain/models/product.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
|
||||||
|
|
||||||
class SpaceDetailsModel extends Equatable {
|
class SpaceDetailsModel extends Equatable {
|
||||||
final String uuid;
|
final String uuid;
|
||||||
@ -26,6 +25,7 @@ class SpaceDetailsModel extends Equatable {
|
|||||||
productAllocations: [],
|
productAllocations: [],
|
||||||
subspaces: [],
|
subspaces: [],
|
||||||
);
|
);
|
||||||
|
|
||||||
factory SpaceDetailsModel.fromJson(Map<String, dynamic> json) {
|
factory SpaceDetailsModel.fromJson(Map<String, dynamic> json) {
|
||||||
return SpaceDetailsModel(
|
return SpaceDetailsModel(
|
||||||
uuid: json['uuid'] as String,
|
uuid: json['uuid'] as String,
|
||||||
@ -56,78 +56,21 @@ class SpaceDetailsModel extends Equatable {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
Map<String, dynamic> toJson() {
|
||||||
List<Object?> get props => [uuid, spaceName, icon, productAllocations, subspaces];
|
return {
|
||||||
}
|
'spaceName': spaceName,
|
||||||
|
'icon': icon,
|
||||||
class ProductAllocation extends Equatable {
|
'subspaces': subspaces.map((e) => e.toJson()).toList(),
|
||||||
final String uuid;
|
'productAllocations': productAllocations.map((e) => e.toJson()).toList(),
|
||||||
final Product product;
|
};
|
||||||
final Tag tag;
|
|
||||||
|
|
||||||
const ProductAllocation({
|
|
||||||
required this.uuid,
|
|
||||||
required this.product,
|
|
||||||
required this.tag,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory ProductAllocation.fromJson(Map<String, dynamic> json) {
|
|
||||||
return ProductAllocation(
|
|
||||||
uuid: json['uuid'] as String? ?? const Uuid().v4(),
|
|
||||||
product: Product.fromJson(json['product'] as Map<String, dynamic>),
|
|
||||||
tag: Tag.fromJson(json['tag'] as Map<String, dynamic>),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ProductAllocation copyWith({
|
|
||||||
String? uuid,
|
|
||||||
Product? product,
|
|
||||||
Tag? tag,
|
|
||||||
}) {
|
|
||||||
return ProductAllocation(
|
|
||||||
uuid: uuid ?? this.uuid,
|
|
||||||
product: product ?? this.product,
|
|
||||||
tag: tag ?? this.tag,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [uuid, product, tag];
|
List<Object?> get props => [
|
||||||
}
|
uuid,
|
||||||
|
spaceName,
|
||||||
class Subspace extends Equatable {
|
icon,
|
||||||
final String uuid;
|
productAllocations,
|
||||||
final String name;
|
subspaces,
|
||||||
final List<ProductAllocation> productAllocations;
|
];
|
||||||
|
|
||||||
const Subspace({
|
|
||||||
required this.uuid,
|
|
||||||
required this.name,
|
|
||||||
required this.productAllocations,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory Subspace.fromJson(Map<String, dynamic> json) {
|
|
||||||
return Subspace(
|
|
||||||
uuid: json['uuid'] as String,
|
|
||||||
name: json['subspaceName'] as String,
|
|
||||||
productAllocations: (json['productAllocations'] as List)
|
|
||||||
.map((e) => ProductAllocation.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Subspace copyWith({
|
|
||||||
String? uuid,
|
|
||||||
String? name,
|
|
||||||
List<ProductAllocation>? productAllocations,
|
|
||||||
}) {
|
|
||||||
return Subspace(
|
|
||||||
uuid: uuid ?? this.uuid,
|
|
||||||
name: name ?? this.name,
|
|
||||||
productAllocations: productAllocations ?? this.productAllocations,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [uuid, name, productAllocations];
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart';
|
||||||
|
|
||||||
|
class Subspace extends Equatable {
|
||||||
|
final String uuid;
|
||||||
|
final String name;
|
||||||
|
final List<ProductAllocation> productAllocations;
|
||||||
|
|
||||||
|
const Subspace({
|
||||||
|
required this.uuid,
|
||||||
|
required this.name,
|
||||||
|
required this.productAllocations,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Subspace.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Subspace(
|
||||||
|
uuid: json['uuid'] as String,
|
||||||
|
name: json['subspaceName'] as String,
|
||||||
|
productAllocations: (json['productAllocations'] as List)
|
||||||
|
.map((e) => ProductAllocation.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final isNewSubspace = uuid.endsWith('-NewTag');
|
||||||
|
return <String, dynamic>{
|
||||||
|
if (!isNewSubspace) 'uuid': uuid,
|
||||||
|
'subspaceName': name,
|
||||||
|
'productAllocations': productAllocations.map((e) => e.toJson()).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Subspace copyWith({
|
||||||
|
String? uuid,
|
||||||
|
String? name,
|
||||||
|
List<ProductAllocation>? productAllocations,
|
||||||
|
}) {
|
||||||
|
return Subspace(
|
||||||
|
uuid: uuid ?? this.uuid,
|
||||||
|
name: name ?? this.name,
|
||||||
|
productAllocations: productAllocations ?? this.productAllocations,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [uuid, name, productAllocations];
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
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:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_space/data/services/remote_create_space_service.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_space/domain/params/create_space_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_space/presentation/bloc/create_space_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/remote_space_details_service.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart';
|
||||||
@ -14,6 +17,8 @@ abstract final class SpaceDetailsDialogHelper {
|
|||||||
static void showCreate(
|
static void showCreate(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required String communityUuid,
|
required String communityUuid,
|
||||||
|
required void Function(SpaceModel updatedSpaceModel)? onSuccess,
|
||||||
|
String? parentUuid,
|
||||||
}) {
|
}) {
|
||||||
showDialog<void>(
|
showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
@ -24,14 +29,41 @@ abstract final class SpaceDetailsDialogHelper {
|
|||||||
RemoteSpaceDetailsService(httpService: HTTPService()),
|
RemoteSpaceDetailsService(httpService: HTTPService()),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => CreateSpaceBloc(
|
||||||
|
RemoteCreateSpaceService(HTTPService()),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) => SpaceDetailsDialog(
|
builder: (context) => BlocListener<CreateSpaceBloc, CreateSpaceState>(
|
||||||
context: context,
|
listener: (context, state) => switch (state) {
|
||||||
title: const SelectableText('Create Space'),
|
CreateSpaceInitial() => null,
|
||||||
spaceModel: SpaceModel.empty(),
|
CreateSpaceLoading() => _onLoading(context),
|
||||||
onSave: (space) {},
|
CreateSpaceSuccess() => _onCreateSuccess(
|
||||||
communityUuid: communityUuid,
|
context,
|
||||||
|
state.space,
|
||||||
|
onSuccess,
|
||||||
|
),
|
||||||
|
CreateSpaceFailure() => _onError(context, state.errorMessage),
|
||||||
|
},
|
||||||
|
child: SpaceDetailsDialog(
|
||||||
|
context: context,
|
||||||
|
title: const SelectableText('Create Space'),
|
||||||
|
spaceModel: SpaceModel.empty(),
|
||||||
|
onSave: (space) {
|
||||||
|
context.read<CreateSpaceBloc>().add(
|
||||||
|
CreateSpace(
|
||||||
|
CreateSpaceParam(
|
||||||
|
communityUuid: communityUuid,
|
||||||
|
space: space,
|
||||||
|
parentUuid: parentUuid,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
communityUuid: communityUuid,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -135,4 +167,14 @@ abstract final class SpaceDetailsDialogHelper {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _onCreateSuccess(
|
||||||
|
BuildContext context,
|
||||||
|
SpaceModel space,
|
||||||
|
void Function(SpaceModel updatedSpaceModel)? onSuccess,
|
||||||
|
) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
onSuccess?.call(space);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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:syncrow_web/common/edit_chip.dart';
|
import 'package:syncrow_web/common/edit_chip.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/button_content_widget.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/button_content_widget.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_name_display_widget.dart';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/sub_spaces_input.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/sub_spaces_input.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_chip.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/subspace_chip.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/products/domain/models/product.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/products/domain/models/product.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart';
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
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:syncrow_web/common/dialog_dropdown.dart';
|
import 'package:syncrow_web/common/dialog_dropdown.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/tags/data/services/remote_tags_service.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/tags/data/services/remote_tags_service.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/models/tag.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/tags/presentation/bloc/tags_bloc.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/tags/presentation/bloc/tags_bloc.dart';
|
||||||
|
@ -34,7 +34,7 @@ class RemoteUpdateSpaceService implements UpdateSpaceService {
|
|||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
final message = e.response?.data as Map<String, dynamic>?;
|
final message = e.response?.data as Map<String, dynamic>?;
|
||||||
final error = message?['error'] as Map<String, dynamic>?;
|
final error = message?['error'] as Map<String, dynamic>?;
|
||||||
final errorMessage = error?['error'] as String? ?? '';
|
final errorMessage = error?['message'] as String? ?? '';
|
||||||
final formattedErrorMessage = [
|
final formattedErrorMessage = [
|
||||||
_defaultErrorMessage,
|
_defaultErrorMessage,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
|
@ -9,34 +9,5 @@ class UpdateSpaceParam {
|
|||||||
final SpaceDetailsModel space;
|
final SpaceDetailsModel space;
|
||||||
final String communityUuid;
|
final String communityUuid;
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() => space.toJson();
|
||||||
return {
|
|
||||||
'spaceName': space.spaceName,
|
|
||||||
'icon': space.icon,
|
|
||||||
'subspaces': space.subspaces.map((e) => e._toJson()).toList(),
|
|
||||||
'productAllocations':
|
|
||||||
space.productAllocations.map((e) => e._toJson()).toList(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension _ProductAllocationToJson on ProductAllocation {
|
|
||||||
Map<String, dynamic> _toJson() {
|
|
||||||
final isNewTag = tag.uuid.isEmpty;
|
|
||||||
return <String, dynamic>{
|
|
||||||
if (isNewTag) 'tagName': tag.name else 'tagUuid': tag.uuid,
|
|
||||||
'productUuid': product.uuid,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension _SubspaceToJson on Subspace {
|
|
||||||
Map<String, dynamic> _toJson() {
|
|
||||||
final isNewSubspace = uuid.endsWith('-NewTag');
|
|
||||||
return <String, dynamic>{
|
|
||||||
if (!isNewSubspace) 'uuid': uuid,
|
|
||||||
'subspaceName': name,
|
|
||||||
'productAllocations': productAllocations.map((e) => e._toJson()).toList(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/product_allocation.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/subspace.dart';
|
||||||
|
|
||||||
part 'space_details_model_event.dart';
|
part 'space_details_model_event.dart';
|
||||||
|
|
||||||
|
@ -12,20 +12,20 @@ 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 DevicesManagementApi {
|
class DevicesManagementApi {
|
||||||
Future<List<AllDevicesModel>> fetchDevices(
|
Future<List<AllDevicesModel>> fetchDevices(String projectId,
|
||||||
String communityId, String spaceId, String projectId) async {
|
{List<String>? spacesId, List<String>? communities}) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: communityId.isNotEmpty && spaceId.isNotEmpty
|
path: ApiEndpoints.getSpaceDevices.replaceAll('{projectId}', projectId),
|
||||||
? ApiEndpoints.getSpaceDevices
|
queryParameters: {
|
||||||
.replaceAll('{spaceUuid}', spaceId)
|
if (spacesId != null && spacesId.isNotEmpty) 'spaces': spacesId,
|
||||||
.replaceAll('{communityUuid}', communityId)
|
if (communities != null && communities.isNotEmpty)
|
||||||
.replaceAll('{projectId}', projectId)
|
'communities': communities,
|
||||||
: ApiEndpoints.getAllDevices.replaceAll('{projectId}', projectId),
|
},
|
||||||
showServerMessage: true,
|
showServerMessage: true,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
List<dynamic> jsonData = json['data'];
|
final List<dynamic> jsonData = json['data'] as List<dynamic>;
|
||||||
List<AllDevicesModel> devicesList = jsonData.map((jsonItem) {
|
final List<AllDevicesModel> devicesList = jsonData.map((jsonItem) {
|
||||||
return AllDevicesModel.fromJson(jsonItem);
|
return AllDevicesModel.fromJson(jsonItem);
|
||||||
}).toList();
|
}).toList();
|
||||||
return devicesList;
|
return devicesList;
|
||||||
@ -416,5 +416,4 @@ class DevicesManagementApi {
|
|||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@ import 'package:syncrow_web/services/api/http_interceptor.dart';
|
|||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
|
||||||
final GetIt serviceLocator = GetIt.instance;
|
final GetIt serviceLocator = GetIt.instance;
|
||||||
//setupLocator() // to search for dependency injection in flutter
|
void initialSetup() {
|
||||||
initialSetup() {
|
|
||||||
serviceLocator.registerSingleton<HTTPInterceptor>(HTTPInterceptor());
|
serviceLocator.registerSingleton<HTTPInterceptor>(HTTPInterceptor());
|
||||||
//Base classes
|
|
||||||
serviceLocator.registerSingleton<Dio>(HTTPService.setupDioClient());
|
serviceLocator.registerSingleton<Dio>(HTTPService.setupDioClient());
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,9 @@ class UserPermissionApi {
|
|||||||
path: ApiEndpoints.roleTypes,
|
path: ApiEndpoints.roleTypes,
|
||||||
showServerMessage: true,
|
showServerMessage: true,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
final List<RoleTypeModel> fetchedRoles =
|
final List<RoleTypeModel> fetchedRoles = (json['data'] as List)
|
||||||
(json['data'] as List).map((item) => RoleTypeModel.fromJson(item)).toList();
|
.map((item) => RoleTypeModel.fromJson(item))
|
||||||
|
.toList();
|
||||||
return fetchedRoles;
|
return fetchedRoles;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -47,7 +48,9 @@ class UserPermissionApi {
|
|||||||
path: ApiEndpoints.permission.replaceAll("roleUuid", roleUuid),
|
path: ApiEndpoints.permission.replaceAll("roleUuid", roleUuid),
|
||||||
showServerMessage: true,
|
showServerMessage: true,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
return (json as List).map((data) => PermissionOption.fromJson(data)).toList();
|
return (json as List)
|
||||||
|
.map((data) => PermissionOption.fromJson(data))
|
||||||
|
.toList();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return response ?? [];
|
return response ?? [];
|
||||||
@ -57,7 +60,7 @@ class UserPermissionApi {
|
|||||||
String? firstName,
|
String? firstName,
|
||||||
String? lastName,
|
String? lastName,
|
||||||
String? email,
|
String? email,
|
||||||
String? jobTitle,
|
String? companyName,
|
||||||
String? phoneNumber,
|
String? phoneNumber,
|
||||||
String? roleUuid,
|
String? roleUuid,
|
||||||
List<String>? spaceUuids,
|
List<String>? spaceUuids,
|
||||||
@ -68,7 +71,7 @@ class UserPermissionApi {
|
|||||||
"firstName": firstName,
|
"firstName": firstName,
|
||||||
"lastName": lastName,
|
"lastName": lastName,
|
||||||
"email": email,
|
"email": email,
|
||||||
"jobTitle": jobTitle != '' ? jobTitle : null,
|
"companyName": companyName != '' ? companyName : null,
|
||||||
"phoneNumber": phoneNumber != '' ? phoneNumber : null,
|
"phoneNumber": phoneNumber != '' ? phoneNumber : null,
|
||||||
"roleUuid": roleUuid,
|
"roleUuid": roleUuid,
|
||||||
"projectUuid": projectUuid,
|
"projectUuid": projectUuid,
|
||||||
@ -140,7 +143,7 @@ class UserPermissionApi {
|
|||||||
String? firstName,
|
String? firstName,
|
||||||
String? userId,
|
String? userId,
|
||||||
String? lastName,
|
String? lastName,
|
||||||
String? jobTitle,
|
String? companyName,
|
||||||
String? phoneNumber,
|
String? phoneNumber,
|
||||||
String? roleUuid,
|
String? roleUuid,
|
||||||
List<String>? spaceUuids,
|
List<String>? spaceUuids,
|
||||||
@ -150,8 +153,8 @@ class UserPermissionApi {
|
|||||||
final body = <String, dynamic>{
|
final body = <String, dynamic>{
|
||||||
"firstName": firstName,
|
"firstName": firstName,
|
||||||
"lastName": lastName,
|
"lastName": lastName,
|
||||||
"jobTitle": jobTitle != '' ? jobTitle : " ",
|
"companyName": companyName != '' ? companyName : ' ',
|
||||||
"phoneNumber": phoneNumber != '' ? phoneNumber : " ",
|
"phoneNumber": phoneNumber != '' ? phoneNumber : ' ',
|
||||||
"roleUuid": roleUuid,
|
"roleUuid": roleUuid,
|
||||||
"projectUuid": projectUuid,
|
"projectUuid": projectUuid,
|
||||||
"spaceUuids": spaceUuids,
|
"spaceUuids": spaceUuids,
|
||||||
@ -190,12 +193,17 @@ class UserPermissionApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> changeUserStatusById(userUuid, status, String projectUuid) async {
|
Future<bool> changeUserStatusById(
|
||||||
|
userUuid, status, String projectUuid) async {
|
||||||
try {
|
try {
|
||||||
Map<String, dynamic> bodya = {"disable": status, "projectUuid": projectUuid};
|
Map<String, dynamic> bodya = {
|
||||||
|
"disable": status,
|
||||||
|
"projectUuid": projectUuid
|
||||||
|
};
|
||||||
|
|
||||||
final response = await _httpService.put(
|
final response = await _httpService.put(
|
||||||
path: ApiEndpoints.changeUserStatus.replaceAll("{invitedUserUuid}", userUuid),
|
path: ApiEndpoints.changeUserStatus
|
||||||
|
.replaceAll("{invitedUserUuid}", userUuid),
|
||||||
body: bodya,
|
body: bodya,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
return json['success'];
|
return json['success'];
|
||||||
|
53
lib/syncrow_app.dart
Normal file
53
lib/syncrow_app.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,31 @@
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/view/access_management.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/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/auth/view/login_page.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/view/device_managment_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/home/view/home_page.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/view/roles_and_permission_page.dart';
|
import 'package:syncrow_web/pages/roles_and_permission/view/roles_and_permission_page.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/view/spaces_management_page.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/views/space_management_page.dart';
|
||||||
import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart';
|
import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart';
|
||||||
import 'package:syncrow_web/utils/constants/routes_const.dart';
|
import 'package:syncrow_web/utils/constants/routes_const.dart';
|
||||||
|
|
||||||
class AppRoutes {
|
abstract final class AppRoutes {
|
||||||
static List<GoRoute> getRoutes() {
|
const AppRoutes._();
|
||||||
return [
|
|
||||||
|
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: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: RoutesConst.auth,
|
path: RoutesConst.auth,
|
||||||
builder: (context, state) => const LoginPage(),
|
builder: (context, state) => const LoginPage(),
|
||||||
@ -43,6 +57,6 @@ class AppRoutes {
|
|||||||
name: 'analytics',
|
name: 'analytics',
|
||||||
builder: (context, state) => const AnalyticsPage(),
|
builder: (context, state) => const AnalyticsPage(),
|
||||||
),
|
),
|
||||||
];
|
],
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
@ -84,5 +84,6 @@ abstract class ColorsManager {
|
|||||||
static const Color minBlueDot = Color(0xFF023DFE);
|
static const Color minBlueDot = Color(0xFF023DFE);
|
||||||
static const Color grey25 = Color(0xFFF9F9F9);
|
static const Color grey25 = Color(0xFFF9F9F9);
|
||||||
static const Color grey50 = Color(0xFF718096);
|
static const Color grey50 = Color(0xFF718096);
|
||||||
|
static const Color red100 = Color(0xFFFE0202);
|
||||||
static const Color grey800 = Color(0xffF8F8F8);
|
static const Color grey800 = Color(0xffF8F8F8);
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,7 @@ abstract class ApiEndpoints {
|
|||||||
////// Devices Management ////////////////
|
////// Devices Management ////////////////
|
||||||
|
|
||||||
static const String getAllDevices = '/projects/{projectId}/devices';
|
static const String getAllDevices = '/projects/{projectId}/devices';
|
||||||
static const String getSpaceDevices =
|
static const String getSpaceDevices = '/projects/{projectId}/devices';
|
||||||
'/projects/{projectId}/communities/{communityUuid}/spaces/{spaceUuid}/devices';
|
|
||||||
static const String getDeviceStatus = '/devices/{uuid}/functions/status';
|
static const String getDeviceStatus = '/devices/{uuid}/functions/status';
|
||||||
static const String getBatchStatus = '/devices/batch';
|
static const String getBatchStatus = '/devices/batch';
|
||||||
|
|
||||||
@ -141,5 +140,5 @@ abstract class ApiEndpoints {
|
|||||||
static const String saveSchedule = '/schedule/{deviceUuid}';
|
static const String saveSchedule = '/schedule/{deviceUuid}';
|
||||||
|
|
||||||
static const String getBookableSpaces = '/bookable-spaces';
|
static const String getBookableSpaces = '/bookable-spaces';
|
||||||
static const String getCalendarEvents = '/api';
|
static const String getBookings = '/bookings?month={mm}-{yyyy}&space={space}';
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user