diff --git a/.env.development b/.env.development
index e77609dc..1983668b 100644
--- a/.env.development
+++ b/.env.development
@@ -1,2 +1,3 @@
ENV_NAME=development
-BASE_URL=https://syncrow-dev.azurewebsites.net
\ No newline at end of file
+BASE_URL=https://syncrow-dev.azurewebsites.net
+RTDB_URL=https://syncrow-dev-79446.asia-southeast1.firebasedatabase.app/
diff --git a/.env.production b/.env.production
index 4e9dcb81..3ab08819 100644
--- a/.env.production
+++ b/.env.production
@@ -1,2 +1,3 @@
ENV_NAME=production
-BASE_URL=https://syncrow-staging.azurewebsites.net
\ No newline at end of file
+BASE_URL=https://syncrow-staging.azurewebsites.net
+RTDB_URL=https://syncrow-prod-79446.asia-southeast1.firebasedatabase.app/
diff --git a/.env.staging b/.env.staging
index 9565b426..bfd878b1 100644
--- a/.env.staging
+++ b/.env.staging
@@ -1,2 +1,3 @@
ENV_NAME=staging
-BASE_URL=https://syncrow-staging.azurewebsites.net
\ No newline at end of file
+BASE_URL=https://syncrow-staging.azurewebsites.net
+RTDB_URL=https://syncrow-staging-79446.asia-southeast1.firebasedatabase.app/
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 4aceb26d..4fd4333b 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -1,67 +1,49 @@
{
- "configurations": [
-
- {
-
- "name": "DEVELOPMENT",
-
- "request": "launch",
-
- "type": "dart",
-
- "args": [
- "-d",
- "chrome",
- "--web-port",
- "3000",
- "-t",
- "lib/main_dev.dart",
- "--web-experimental-hot-reload",
- ],
-
- "flutterMode": "debug"
-
- },{
-
- "name": "STAGING",
-
- "request": "launch",
-
- "type": "dart",
-
- "args": [
- "-d",
- "chrome",
- "--web-port",
- "3000",
- "-t",
- "lib/main_staging.dart",
- "--web-experimental-hot-reload",
- ],
-
- "flutterMode": "debug"
-
- },{
-
- "name": "PRODUCTION",
-
- "request": "launch",
-
- "type": "dart",
-
- "args": [
- "-d",
- "chrome",
- "--web-port",
- "3000",
- "-t",
- "lib/main.dart",
- "--web-experimental-hot-reload",
- ],
-
- "flutterMode": "debug"
-
- },
-
- ]
+ "configurations": [
+ {
+ "name": "DEVELOPMENT",
+ "request": "launch",
+ "type": "dart",
+ "args": [
+ "-d",
+ "chrome",
+ "--web-port",
+ "3000",
+ "-t",
+ "lib/main_dev.dart",
+ "--web-experimental-hot-reload"
+ ],
+ "flutterMode": "debug"
+ },
+ {
+ "name": "STAGING",
+ "request": "launch",
+ "type": "dart",
+ "args": [
+ "-d",
+ "chrome",
+ "--web-port",
+ "3000",
+ "-t",
+ "lib/main_staging.dart",
+ "--web-experimental-hot-reload"
+ ],
+ "flutterMode": "debug"
+ },
+ {
+ "name": "PRODUCTION",
+ "request": "launch",
+ "type": "dart",
+ "args": [
+ "-d",
+ "chrome",
+ "--web-port",
+ "3000",
+ "-t",
+ "lib/main.dart",
+ "--web-experimental-hot-reload"
+ ],
+ "flutterMode": "debug"
+ }
+ ]
}
\ No newline at end of file
diff --git a/assets/icons/group_icon.svg b/assets/icons/group_icon.svg
new file mode 100644
index 00000000..efca14dd
--- /dev/null
+++ b/assets/icons/group_icon.svg
@@ -0,0 +1,15 @@
+
diff --git a/assets/icons/home_icon.svg b/assets/icons/home_icon.svg
new file mode 100644
index 00000000..35080c4e
--- /dev/null
+++ b/assets/icons/home_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/firebase.json b/firebase.json
index fa1105a3..e22eee5e 100644
--- a/firebase.json
+++ b/firebase.json
@@ -1 +1 @@
-{"flutter":{"platforms":{"android":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:android:2bc36fbe82994a3e0c7e6d","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:ios:14346b200780dc760c7e6d","uploadDebugSymbols":true,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:ios:14346b200780dc760c7e6d","uploadDebugSymbols":true,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"test2-8a3d2","configurations":{"android":"1:427332280600:android:2bc36fbe82994a3e0c7e6d","ios":"1:427332280600:ios:14346b200780dc760c7e6d","macos":"1:427332280600:ios:14346b200780dc760c7e6d","web":"1:427332280600:web:ad50516a87a35a1a0c7e6d","windows":"1:427332280600:web:f7a25537ccd5a7bd0c7e6d"}}}}}}
\ No newline at end of file
+{"flutter":{"platforms":{"android":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:android:2bc36fbe82994a3e0c7e6d","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:ios:14346b200780dc760c7e6d","uploadDebugSymbols":true,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:ios:14346b200780dc760c7e6d","uploadDebugSymbols":true,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"syncrow-prod-79446","configurations":{"web":"1:255001682464:web:a03e2d6214c13101561245"}}}}}}
\ No newline at end of file
diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart
new file mode 100644
index 00000000..0f5fbf8c
--- /dev/null
+++ b/lib/firebase_options.dart
@@ -0,0 +1,16 @@
+import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
+
+final class DefaultFirebaseOptions extends FirebaseOptions {
+ const DefaultFirebaseOptions({
+ required String databaseUrl,
+ }) : super(
+ apiKey: 'AIzaSyDgq5ywsnFVbbQO-Xz1Z4sR5bBcuiDaS9g',
+ appId: '1:255001682464:web:a03e2d6214c13101561245',
+ messagingSenderId: '255001682464',
+ projectId: 'syncrow-prod-79446',
+ authDomain: 'syncrow-prod-79446.firebaseapp.com',
+ storageBucket: 'syncrow-prod-79446.firebasestorage.app',
+ databaseURL: databaseUrl,
+ measurementId: 'G-1850Q89RMK',
+ );
+}
diff --git a/lib/firebase_options_dev.dart b/lib/firebase_options_dev.dart
deleted file mode 100644
index 93e8600c..00000000
--- a/lib/firebase_options_dev.dart
+++ /dev/null
@@ -1,93 +0,0 @@
-// File generated by FlutterFire CLI.
-// ignore_for_file: type=lint
-import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
-import 'package:flutter/foundation.dart'
- show defaultTargetPlatform, kIsWeb, TargetPlatform;
-
-/// Default [FirebaseOptions] for use with your Firebase apps.
-///
-/// Example:
-/// ```dart
-/// import 'firebase_options.dart';
-/// // ...
-/// await Firebase.initializeApp(
-/// options: DefaultFirebaseOptions.currentPlatform,
-/// );
-/// ```
-class DefaultFirebaseOptionsDev {
- static FirebaseOptions get currentPlatform {
- if (kIsWeb) {
- return web;
- }
- switch (defaultTargetPlatform) {
- case TargetPlatform.android:
- return android;
- case TargetPlatform.iOS:
- return ios;
- case TargetPlatform.macOS:
- return macos;
- case TargetPlatform.windows:
- return windows;
- case TargetPlatform.linux:
- throw UnsupportedError(
- 'DefaultFirebaseOptions have not been configured for linux - '
- 'you can reconfigure this by running the FlutterFire CLI again.',
- );
- default:
- throw UnsupportedError(
- 'DefaultFirebaseOptions are not supported for this platform.',
- );
- }
- }
-
- static const FirebaseOptions web = FirebaseOptions(
- apiKey: 'AIzaSyCVEvKsJYzhWDFM-9Od68FE0nPpP933st0',
- appId: '1:427332280600:web:ad50516a87a35a1a0c7e6d',
- messagingSenderId: '427332280600',
- projectId: 'test2-8a3d2',
- authDomain: 'test2-8a3d2.firebaseapp.com',
- databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
- storageBucket: 'test2-8a3d2.firebasestorage.app',
- measurementId: 'G-Z1RTTTV5H9',
- );
-
- static const FirebaseOptions android = FirebaseOptions(
- apiKey: 'AIzaSyA5qOErxdm0zJmoHIB0TixfebYEsNRpwV0',
- appId: '1:427332280600:android:2bc36fbe82994a3e0c7e6d',
- messagingSenderId: '427332280600',
- projectId: 'test2-8a3d2',
- databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
- storageBucket: 'test2-8a3d2.firebasestorage.app',
- );
-
- static const FirebaseOptions ios = FirebaseOptions(
- apiKey: 'AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw',
- appId: '1:427332280600:ios:14346b200780dc760c7e6d',
- messagingSenderId: '427332280600',
- projectId: 'test2-8a3d2',
- databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
- storageBucket: 'test2-8a3d2.firebasestorage.app',
- iosBundleId: 'com.example.syncrowWeb',
- );
-
- static const FirebaseOptions macos = FirebaseOptions(
- apiKey: 'AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw',
- appId: '1:427332280600:ios:14346b200780dc760c7e6d',
- messagingSenderId: '427332280600',
- projectId: 'test2-8a3d2',
- databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
- storageBucket: 'test2-8a3d2.firebasestorage.app',
- iosBundleId: 'com.example.syncrowWeb',
- );
-
- static const FirebaseOptions windows = FirebaseOptions(
- apiKey: 'AIzaSyDizKjPC5rdkEjDxwXjM-RU5unB0Ziq3iw',
- appId: '1:427332280600:web:f7a25537ccd5a7bd0c7e6d',
- messagingSenderId: '427332280600',
- projectId: 'test2-8a3d2',
- authDomain: 'test2-8a3d2.firebaseapp.com',
- databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
- storageBucket: 'test2-8a3d2.firebasestorage.app',
- measurementId: 'G-4LFVXEXWKY',
- );
-}
diff --git a/lib/firebase_options_prod.dart b/lib/firebase_options_prod.dart
deleted file mode 100644
index 485696b8..00000000
--- a/lib/firebase_options_prod.dart
+++ /dev/null
@@ -1,77 +0,0 @@
-// File generated by FlutterFire CLI.
-// ignore_for_file: type=lint
-import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
-import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb, TargetPlatform;
-
-/// Default [FirebaseOptions] for use with your Firebase apps.
-///
-/// Example:
-/// ```dart
-/// import 'firebase_options.dart';
-/// // ...
-/// await Firebase.initializeApp(
-/// options: DefaultFirebaseOptions.currentPlatform,
-/// );
-/// ```
-class DefaultFirebaseOptionsStaging {
- static FirebaseOptions get currentPlatform {
- if (kIsWeb) {
- return web;
- }
- switch (defaultTargetPlatform) {
- case TargetPlatform.android:
- return android;
- case TargetPlatform.iOS:
- return ios;
- case TargetPlatform.macOS:
- throw UnsupportedError(
- 'DefaultFirebaseOptions have not been configured for macos - '
- 'you can reconfigure this by running the FlutterFire CLI again.',
- );
- case TargetPlatform.windows:
- throw UnsupportedError(
- 'DefaultFirebaseOptions have not been configured for windows - '
- 'you can reconfigure this by running the FlutterFire CLI again.',
- );
- case TargetPlatform.linux:
- throw UnsupportedError(
- 'DefaultFirebaseOptions have not been configured for linux - '
- 'you can reconfigure this by running the FlutterFire CLI again.',
- );
- default:
- throw UnsupportedError(
- 'DefaultFirebaseOptions are not supported for this platform.',
- );
- }
- }
-
- static const FirebaseOptions android = FirebaseOptions(
- apiKey: 'AIzaSyDP9GpYfLE8gHTj3kZ1hW8fx_FkJqOqSQk',
- appId: '1:786692570726:android:0ef7079c2b978d4417b7a7',
- messagingSenderId: '786692570726',
- projectId: 'syncrow-staging',
- databaseURL: 'https://syncrow-staging-default-rtdb.firebaseio.com',
- storageBucket: 'syncrow-staging.appspot.com',
- );
-
- static const FirebaseOptions ios = FirebaseOptions(
- apiKey: 'AIzaSyAWlRiuJ75FMlf2_UDdri1voWKvkaSHtRg',
- appId: '1:786692570726:ios:455a6fcff77e130f17b7a7',
- messagingSenderId: '786692570726',
- projectId: 'syncrow-staging',
- databaseURL: 'https://syncrow-staging-default-rtdb.firebaseio.com',
- storageBucket: 'syncrow-staging.appspot.com',
- iosBundleId: 'com.example.syncrow.app',
- );
-
- static const FirebaseOptions web = FirebaseOptions(
- apiKey: 'AIzaSyDyGaQ3sZhb4meaY6sGke-YglhdhJ2is8Q',
- appId: '1:786692570726:web:93c931e6701797b317b7a7',
- messagingSenderId: '786692570726',
- projectId: 'syncrow-staging',
- authDomain: 'syncrow-staging.firebaseapp.com',
- databaseURL: 'https://syncrow-staging-default-rtdb.firebaseio.com',
- storageBucket: 'syncrow-staging.appspot.com',
- measurementId: 'G-CZ3J3G6LMQ',
- );
-}
diff --git a/lib/main.dart b/lib/main.dart
index 219fc41d..a50d2615 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
-import 'package:syncrow_web/firebase_options_prod.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';
@@ -27,7 +27,9 @@ Future main() async {
await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
- options: DefaultFirebaseOptionsStaging.currentPlatform,
+ options: DefaultFirebaseOptions(
+ databaseUrl: dotenv.env['RTDB_URL']!,
+ ),
);
initialSetup();
} catch (_) {}
@@ -59,7 +61,7 @@ class MyApp extends StatelessWidget {
BlocProvider(
create: (context) => CreateRoutineBloc(),
),
- BlocProvider(create: (context) => HomeBloc()..add(FetchUserInfo())),
+ BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider(
create: (context) => VisitorPasswordBloc(),
),
diff --git a/lib/main_dev.dart b/lib/main_dev.dart
index 410110d1..284e2f30 100644
--- a/lib/main_dev.dart
+++ b/lib/main_dev.dart
@@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
-import 'package:syncrow_web/firebase_options_dev.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';
@@ -27,7 +27,9 @@ Future main() async {
await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
- options: DefaultFirebaseOptionsDev.currentPlatform,
+ options: DefaultFirebaseOptions(
+ databaseUrl: dotenv.env['RTDB_URL']!,
+ ),
);
initialSetup();
} catch (_) {}
@@ -59,7 +61,7 @@ class MyApp extends StatelessWidget {
BlocProvider(
create: (context) => CreateRoutineBloc(),
),
- BlocProvider(create: (context) => HomeBloc()..add(FetchUserInfo())),
+ BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider(
create: (context) => VisitorPasswordBloc(),
),
diff --git a/lib/main_staging.dart b/lib/main_staging.dart
index 70c968c0..6389c53a 100644
--- a/lib/main_staging.dart
+++ b/lib/main_staging.dart
@@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
-import 'package:syncrow_web/firebase_options_prod.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';
@@ -24,7 +24,9 @@ Future main() async {
await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
- options: DefaultFirebaseOptionsStaging.currentPlatform,
+ options: DefaultFirebaseOptions(
+ databaseUrl: dotenv.env['RTDB_URL']!,
+ ),
);
initialSetup();
} catch (_) {}
@@ -56,7 +58,7 @@ class MyApp extends StatelessWidget {
BlocProvider(
create: (context) => CreateRoutineBloc(),
),
- BlocProvider(create: (context) => HomeBloc()..add(FetchUserInfo())),
+ BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider(
create: (context) => VisitorPasswordBloc(),
),
diff --git a/lib/pages/access_management/bloc/access_bloc.dart b/lib/pages/access_management/bloc/access_bloc.dart
index dd82d739..11c42f3b 100644
--- a/lib/pages/access_management/bloc/access_bloc.dart
+++ b/lib/pages/access_management/bloc/access_bloc.dart
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_event.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_state.dart';
-import 'package:syncrow_web/pages/access_management/model/password_model.dart';
+import 'package:syncrow_web/pages/access_management/booking_system/presentation/model/password_model.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/common/hour_picker_dialog.dart';
import 'package:syncrow_web/services/access_mang_api.dart';
diff --git a/lib/pages/access_management/bloc/access_state.dart b/lib/pages/access_management/bloc/access_state.dart
index 0790a735..cdaf9aad 100644
--- a/lib/pages/access_management/bloc/access_state.dart
+++ b/lib/pages/access_management/bloc/access_state.dart
@@ -1,5 +1,5 @@
import 'package:equatable/equatable.dart';
-import 'package:syncrow_web/pages/access_management/model/password_model.dart';
+import 'package:syncrow_web/pages/access_management/booking_system/presentation/model/password_model.dart';
abstract class AccessState extends Equatable {
const AccessState();
diff --git a/lib/pages/access_management/booking_system/data/services/remote_bookable_spaces_service.dart b/lib/pages/access_management/booking_system/data/services/remote_bookable_spaces_service.dart
new file mode 100644
index 00000000..3c2610db
--- /dev/null
+++ b/lib/pages/access_management/booking_system/data/services/remote_bookable_spaces_service.dart
@@ -0,0 +1,52 @@
+import 'package:dio/dio.dart';
+import 'package:syncrow_web/pages/access_management/booking_system/domain/load_bookable_spaces_param.dart';
+import 'package:syncrow_web/pages/access_management/booking_system/domain/models/paginated_bookable_spaces.dart';
+import 'package:syncrow_web/pages/access_management/booking_system/domain/services/bookable_system_service.dart';
+import 'package:syncrow_web/services/api/api_exception.dart';
+import 'package:syncrow_web/services/api/http_service.dart';
+import 'package:syncrow_web/utils/constants/api_const.dart';
+
+class RemoteBookableSpacesService implements BookableSystemService {
+ const RemoteBookableSpacesService(this._httpService);
+
+ final HTTPService _httpService;
+ static const _defaultErrorMessage = 'Failed to load bookable spaces';
+
+ @override
+ Future getBookableSpaces({
+ required LoadBookableSpacesParam param,
+ }) async {
+ try {
+ final response = await _httpService.get(
+ path: ApiEndpoints.getBookableSpaces,
+ queryParameters: {
+ 'page': param.page,
+ 'size': param.size,
+ 'active': true,
+ 'configured': true,
+ if (param.search != null &&
+ param.search.isNotEmpty &&
+ param.search != 'null')
+ 'search': param.search,
+ },
+ expectedResponseModel: (json) {
+ return PaginatedBookableSpaces.fromJson(
+ json as Map,
+ );
+ },
+ );
+ return response;
+ } on DioException catch (e) {
+ final responseData = e.response?.data;
+ if (responseData is Map) {
+ 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()}');
+ }
+ }
+}
diff --git a/lib/pages/access_management/booking_system/data/services/remote_calendar_service.dart b/lib/pages/access_management/booking_system/data/services/remote_calendar_service.dart
new file mode 100644
index 00000000..aa3307d3
--- /dev/null
+++ b/lib/pages/access_management/booking_system/data/services/remote_calendar_service.dart
@@ -0,0 +1,170 @@
+import 'package:dio/dio.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/services/api/api_exception.dart';
+import 'package:syncrow_web/services/api/http_service.dart';
+import 'package:syncrow_web/utils/constants/api_const.dart';
+
+class RemoteCalendarService implements CalendarSystemService {
+ const RemoteCalendarService(this._httpService);
+
+ final HTTPService _httpService;
+ static const _defaultErrorMessage = 'Failed to load Calendar';
+
+ @override
+ Future getCalendarEvents({
+ required String spaceId,
+ }) async {
+ try {
+ final response = await _httpService.get(
+ path: ApiEndpoints.getCalendarEvents,
+ queryParameters: {
+ 'spaceId': spaceId,
+ },
+ expectedResponseModel: (json) {
+ return CalendarEventsResponse.fromJson(
+ json as Map,
+ );
+ },
+ );
+
+ return CalendarEventsResponse.fromJson(response as Map);
+ } on DioException catch (e) {
+ final responseData = e.response?.data;
+ if (responseData is Map) {
+ 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 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,
+ );
+ },
+ );
+
+ return CalendarEventsResponse.fromJson(response as Map);
+ } on DioException catch (e) {
+ final responseData = e.response?.data;
+ if (responseData is Map) {
+ 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()}');
+ }
+ }
+}
diff --git a/lib/pages/access_management/booking_system/domain/load_bookable_spaces_param.dart b/lib/pages/access_management/booking_system/domain/load_bookable_spaces_param.dart
new file mode 100644
index 00000000..f2b2e5fe
--- /dev/null
+++ b/lib/pages/access_management/booking_system/domain/load_bookable_spaces_param.dart
@@ -0,0 +1,36 @@
+import 'package:equatable/equatable.dart';
+
+class LoadBookableSpacesParam extends Equatable {
+ const LoadBookableSpacesParam({
+ this.page = 1,
+ this.size = 25,
+ this.search = '',
+ this.active = true,
+ this.configured = true,
+ });
+
+ final int page;
+ final int size;
+ final String search;
+ final bool active;
+ final bool configured;
+
+ LoadBookableSpacesParam copyWith({
+ int? page,
+ int? size,
+ String? search,
+ bool? active,
+ bool? configured,
+ }) {
+ return LoadBookableSpacesParam(
+ page: page ?? this.page,
+ size: size ?? this.size,
+ search: search ?? this.search,
+ active: active ?? this.active,
+ configured: configured ?? this.configured,
+ );
+ }
+
+ @override
+ List