diff --git a/lib/main.dart b/lib/main.dart index 8eb6ce38..31ae414b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,7 +7,6 @@ import 'package:go_router/go_router.dart'; import 'package:syncrow_web/firebase_options_prod.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'; @@ -21,8 +20,10 @@ import 'package:syncrow_web/utils/theme/theme.dart'; Future main() async { try { - const environment = - String.fromEnvironment('FLAVOR', defaultValue: 'production'); + const environment = String.fromEnvironment( + 'FLAVOR', + defaultValue: 'production', + ); await dotenv.load(fileName: '.env.$environment'); WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( @@ -40,7 +41,7 @@ class MyApp extends StatelessWidget { initialLocation: RoutesConst.auth, routes: AppRoutes.getRoutes(), redirect: (context, state) async { - String checkToken = await AuthBloc.getTokenAndValidate(); + final checkToken = await AuthBloc.getTokenAndValidate(); final loggedIn = checkToken == 'Success'; final goingToLogin = state.uri.toString() == RoutesConst.auth; @@ -58,8 +59,7 @@ class MyApp extends StatelessWidget { BlocProvider( create: (context) => CreateRoutineBloc(), ), - BlocProvider( - create: (context) => HomeBloc()..add(const FetchUserInfo())), + BlocProvider(create: (context) => HomeBloc()), BlocProvider( create: (context) => VisitorPasswordBloc(), ), diff --git a/lib/main_dev.dart b/lib/main_dev.dart index 578b2c30..8813f6ec 100644 --- a/lib/main_dev.dart +++ b/lib/main_dev.dart @@ -7,7 +7,6 @@ import 'package:go_router/go_router.dart'; import 'package:syncrow_web/firebase_options_dev.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'; @@ -21,7 +20,10 @@ import 'package:syncrow_web/utils/theme/theme.dart'; Future main() async { try { - const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development'); + const environment = String.fromEnvironment( + 'FLAVOR', + defaultValue: 'development', + ); await dotenv.load(fileName: '.env.$environment'); WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( @@ -39,7 +41,7 @@ class MyApp extends StatelessWidget { initialLocation: RoutesConst.auth, routes: AppRoutes.getRoutes(), redirect: (context, state) async { - String checkToken = await AuthBloc.getTokenAndValidate(); + final checkToken = await AuthBloc.getTokenAndValidate(); final loggedIn = checkToken == 'Success'; final goingToLogin = state.uri.toString() == RoutesConst.auth; @@ -57,7 +59,7 @@ class MyApp extends StatelessWidget { BlocProvider( create: (context) => CreateRoutineBloc(), ), - BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())), + BlocProvider(create: (context) => HomeBloc()), BlocProvider( create: (context) => VisitorPasswordBloc(), ), diff --git a/lib/main_staging.dart b/lib/main_staging.dart index e7f95c57..d393eb7b 100644 --- a/lib/main_staging.dart +++ b/lib/main_staging.dart @@ -7,7 +7,6 @@ import 'package:go_router/go_router.dart'; import 'package:syncrow_web/firebase_options_prod.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'; @@ -39,7 +38,7 @@ class MyApp extends StatelessWidget { initialLocation: RoutesConst.auth, routes: AppRoutes.getRoutes(), redirect: (context, state) async { - String checkToken = await AuthBloc.getTokenAndValidate(); + final checkToken = await AuthBloc.getTokenAndValidate(); final loggedIn = checkToken == 'Success'; final goingToLogin = state.uri.toString() == RoutesConst.auth; @@ -57,7 +56,7 @@ class MyApp extends StatelessWidget { BlocProvider( create: (context) => CreateRoutineBloc(), ), - BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())), + BlocProvider(create: (context) => HomeBloc()), BlocProvider( create: (context) => VisitorPasswordBloc(), ), diff --git a/lib/pages/home/bloc/home_bloc.dart b/lib/pages/home/bloc/home_bloc.dart index cb3e75f0..c1bcba6a 100644 --- a/lib/pages/home/bloc/home_bloc.dart +++ b/lib/pages/home/bloc/home_bloc.dart @@ -13,29 +13,32 @@ import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart'; import 'package:syncrow_web/services/home_api.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart'; -import 'package:syncrow_web/utils/navigation_service.dart'; class HomeBloc extends Bloc { UserModel? user; String terms = ''; String policy = ''; - HomeBloc() : super((HomeInitial())) { + HomeBloc() : super(HomeInitial()) { on(_fetchUserInfo); on(_fetchTerms); on(_fetchPolicy); on(_confirmUserAgreement); } - Future _fetchUserInfo(FetchUserInfo event, Emitter emit) async { + Future _fetchUserInfo( + FetchUserInfo event, + Emitter emit, + ) async { try { - var uuid = + final uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); - user = await HomeApi().fetchUserInfo(uuid); + if (uuid != null) { + user = await HomeApi().fetchUserInfo(uuid); + } - if (user != null && user!.project != null) { + if (user != null && user?.project != null) { await ProjectManager.setProjectUUID(user!.project!.uuid); - } add(FetchTermEvent()); add(FetchPolicyEvent()); @@ -46,7 +49,7 @@ class HomeBloc extends Bloc { } } - Future _fetchTerms(FetchTermEvent event, Emitter emit) async { + Future _fetchTerms(FetchTermEvent event, Emitter emit) async { try { emit(LoadingHome()); terms = await HomeApi().fetchTerms(); @@ -56,22 +59,22 @@ class HomeBloc extends Bloc { } } - Future _fetchPolicy(FetchPolicyEvent event, Emitter emit) async { + Future _fetchPolicy(FetchPolicyEvent event, Emitter emit) async { try { emit(LoadingHome()); policy = await HomeApi().fetchPolicy(); emit(HomeInitial()); } catch (e) { - debugPrint("Error fetching policy: $e"); + debugPrint('Error fetching policy: $e'); return; } } - Future _confirmUserAgreement( + Future _confirmUserAgreement( ConfirmUserAgreementEvent event, Emitter emit) async { try { emit(LoadingHome()); - var uuid = + final uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); policy = await HomeApi().confirmUserAgreements(uuid); emit(PolicyAgreement()); @@ -80,7 +83,7 @@ class HomeBloc extends Bloc { } } - List homeItems = [ + final List homeItems = [ HomeItemModel( title: 'Access Management', icon: Assets.accessIcon, diff --git a/lib/pages/home/view/home_page.dart b/lib/pages/home/view/home_page.dart index 9159011f..9661cfd9 100644 --- a/lib/pages/home/view/home_page.dart +++ b/lib/pages/home/view/home_page.dart @@ -1,11 +1,25 @@ import 'package:flutter/cupertino.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/home/view/home_page_mobile.dart'; import 'package:syncrow_web/pages/home/view/home_page_web.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -class HomePage extends StatelessWidget with HelperResponsiveLayout { +class HomePage extends StatefulWidget { const HomePage({super.key}); + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State with HelperResponsiveLayout{ + + @override + void initState() { + context.read().add(const FetchUserInfo()); + super.initState(); + } @override Widget build(BuildContext context) { final isSmallScreen = isSmallScreenSize(context); diff --git a/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart b/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart index dd73183a..3fd07834 100644 --- a/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart +++ b/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart @@ -1419,15 +1419,17 @@ Future _onLoadScenes( event.automationId, event.automationStatusUpdate, projectId); if (success) { - final updatedAutomations = await SceneApi.getAutomationByUnitId( - event.automationStatusUpdate.spaceUuid, - event.communityId, - projectId); + // await SceneApi.getAutomationByUnitId( + // event.automationStatusUpdate.spaceUuid, + // event.communityId, + // projectId); // Remove from loading set safely + final updatedLoadingIds = {...state.loadingAutomationIds!} ..remove(event.automationId); - + final updatedAutomations = changeItemStateOnToggelingSceen( + state.automations, event.automationId); emit(state.copyWith( automations: updatedAutomations, loadingAutomationIds: updatedLoadingIds, @@ -1449,4 +1451,24 @@ Future _onLoadScenes( )); } } + + List changeItemStateOnToggelingSceen( + List oldSceen, String automationId) { + return oldSceen.map((scene) { + if (scene.id == automationId) { + return ScenesModel( + id: scene.id, + sceneTuyaId: scene.sceneTuyaId, + name: scene.name, + status: scene.status == 'enable' ? 'disable' : 'enable', + type: scene.type, + spaceName: scene.spaceName, + spaceId: scene.spaceId, + communityId: scene.communityId, + icon: scene.icon, + ); + } + return scene; + }).toList(); + } } diff --git a/lib/pages/space_management_v2/modules/communities/data/services/remote_communities_service.dart b/lib/pages/space_management_v2/modules/communities/data/services/remote_communities_service.dart new file mode 100644 index 00000000..36682bb4 --- /dev/null +++ b/lib/pages/space_management_v2/modules/communities/data/services/remote_communities_service.dart @@ -0,0 +1,34 @@ +import 'package:dio/dio.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/params/load_communities_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/services/communities_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; + +class RemoteCommunitiesService implements CommunitiesService { + const RemoteCommunitiesService(this._httpService); + + final HTTPService _httpService; + + static const _defaultErrorMessage = 'Failed to load communities'; + + @override + Future> getCommunity(LoadCommunitiesParam param) async { + try { + return _httpService.get( + path: '/api/communities/', + expectedResponseModel: (json) => (json as List) + .map((e) => CommunityModel.fromJson(e as Map)) + .toList(), + ); + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + throw APIException(errorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } +} diff --git a/lib/pages/space_management_v2/modules/communities/domain/models/community_model.dart b/lib/pages/space_management_v2/modules/communities/domain/models/community_model.dart new file mode 100644 index 00000000..c6efad9e --- /dev/null +++ b/lib/pages/space_management_v2/modules/communities/domain/models/community_model.dart @@ -0,0 +1,27 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; + +class CommunityModel extends Equatable { + final String uuid; + final String name; + final List spaces; + + const CommunityModel({ + required this.uuid, + required this.name, + required this.spaces, + }); + + factory CommunityModel.fromJson(Map json) { + return CommunityModel( + uuid: json['uuid'] as String, + name: json['name'] as String, + spaces: (json['spaces'] as List) + .map((e) => SpaceModel.fromJson(e as Map)) + .toList(), + ); + } + + @override + List get props => [uuid, name, spaces]; +} diff --git a/lib/pages/space_management_v2/modules/communities/domain/models/space_model.dart b/lib/pages/space_management_v2/modules/communities/domain/models/space_model.dart new file mode 100644 index 00000000..0f8aadb2 --- /dev/null +++ b/lib/pages/space_management_v2/modules/communities/domain/models/space_model.dart @@ -0,0 +1,30 @@ +import 'package:equatable/equatable.dart'; + +class SpaceModel extends Equatable { + final String uuid; + final String spaceName; + final String icon; + final List children; + + const SpaceModel({ + required this.uuid, + required this.spaceName, + required this.icon, + required this.children, + }); + + factory SpaceModel.fromJson(Map json) { + return SpaceModel( + uuid: json['uuid'] as String, + spaceName: json['spaceName'] as String, + icon: json['icon'] as String, + children: (json['children'] as List?) + ?.map((e) => SpaceModel.fromJson(e as Map)) + .toList() ?? + [], + ); + } + + @override + List get props => [uuid, spaceName, icon, children]; +} diff --git a/lib/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart b/lib/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart new file mode 100644 index 00000000..9bdc215c --- /dev/null +++ b/lib/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart @@ -0,0 +1,3 @@ +class LoadCommunitiesParam { + const LoadCommunitiesParam(); +} diff --git a/lib/pages/space_management_v2/modules/communities/domain/services/communities_service.dart b/lib/pages/space_management_v2/modules/communities/domain/services/communities_service.dart new file mode 100644 index 00000000..bccad2ad --- /dev/null +++ b/lib/pages/space_management_v2/modules/communities/domain/services/communities_service.dart @@ -0,0 +1,6 @@ +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/params/load_communities_param.dart'; + +abstract class CommunitiesService { + Future> getCommunity(LoadCommunitiesParam param); +} diff --git a/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart b/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart new file mode 100644 index 00000000..0d85b22f --- /dev/null +++ b/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart @@ -0,0 +1,50 @@ +import 'package:equatable/equatable.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/domain/params/load_communities_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/services/communities_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; + +part 'communities_event.dart'; +part 'communities_state.dart'; + +class CommunitiesBloc extends Bloc { + CommunitiesBloc({ + required CommunitiesService communitiesService, + }) : _communitiesService = communitiesService, + super(const CommunitiesState()) { + on(_onLoadCommunities); + } + + final CommunitiesService _communitiesService; + + Future _onLoadCommunities( + LoadCommunities event, + Emitter emit, + ) async { + try { + emit(const CommunitiesState(status: CommunitiesStatus.loading)); + final communities = await _communitiesService.getCommunity(event.param); + emit( + CommunitiesState( + status: CommunitiesStatus.success, + communities: communities, + ), + ); + } on APIException catch (e) { + emit( + CommunitiesState( + status: CommunitiesStatus.failure, + errorMessage: e.message, + ), + ); + } catch (e) { + emit( + CommunitiesState( + status: CommunitiesStatus.failure, + errorMessage: e.toString(), + ), + ); + } + } +} diff --git a/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_event.dart b/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_event.dart new file mode 100644 index 00000000..ef375c5a --- /dev/null +++ b/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_event.dart @@ -0,0 +1,17 @@ +part of 'communities_bloc.dart'; + +sealed class CommunitiesEvent extends Equatable { + const CommunitiesEvent(); + + @override + List get props => []; +} + +class LoadCommunities extends CommunitiesEvent { + const LoadCommunities(this.param); + + final LoadCommunitiesParam param; + + @override + List get props => [param]; +} diff --git a/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_state.dart b/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_state.dart new file mode 100644 index 00000000..94740f0b --- /dev/null +++ b/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_state.dart @@ -0,0 +1,18 @@ +part of 'communities_bloc.dart'; + +enum CommunitiesStatus { initial, loading, success, failure } + +final class CommunitiesState extends Equatable { + const CommunitiesState({ + this.status = CommunitiesStatus.initial, + this.communities = const [], + this.errorMessage, + }); + + final CommunitiesStatus status; + final List communities; + final String? errorMessage; + + @override + List get props => [status, communities, errorMessage]; +} diff --git a/lib/pages/space_management_v2/modules/create_community/data/services/remote_create_community_service.dart b/lib/pages/space_management_v2/modules/create_community/data/services/remote_create_community_service.dart new file mode 100644 index 00000000..be83124b --- /dev/null +++ b/lib/pages/space_management_v2/modules/create_community/data/services/remote_create_community_service.dart @@ -0,0 +1,39 @@ +import 'package:dio/dio.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/create_community/domain/param/create_community_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/create_community/domain/services/create_community_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; + +class RemoteCreateCommunityService implements CreateCommunityService { + const RemoteCreateCommunityService(this._httpService); + + final HTTPService _httpService; + + static const _defaultErrorMessage = 'Failed to create community'; + + @override + Future createCommunity(CreateCommunityParam param) async { + try { + final response = await _httpService.post( + path: 'endpoint', + expectedResponseModel: (data) => CommunityModel.fromJson( + data as Map, + ), + ); + return response; + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [ + _defaultErrorMessage, + errorMessage, + ].join(': '); + throw APIException(formattedErrorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } +} diff --git a/lib/pages/space_management_v2/modules/create_community/domain/param/create_community_param.dart b/lib/pages/space_management_v2/modules/create_community/domain/param/create_community_param.dart new file mode 100644 index 00000000..3d7c203b --- /dev/null +++ b/lib/pages/space_management_v2/modules/create_community/domain/param/create_community_param.dart @@ -0,0 +1,10 @@ +import 'package:equatable/equatable.dart'; + +class CreateCommunityParam extends Equatable { + const CreateCommunityParam({required this.name}); + + final String name; + + @override + List get props => [name]; +} diff --git a/lib/pages/space_management_v2/modules/create_community/domain/services/create_community_service.dart b/lib/pages/space_management_v2/modules/create_community/domain/services/create_community_service.dart new file mode 100644 index 00000000..ddb1a72d --- /dev/null +++ b/lib/pages/space_management_v2/modules/create_community/domain/services/create_community_service.dart @@ -0,0 +1,6 @@ +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/create_community/domain/param/create_community_param.dart'; + +abstract class CreateCommunityService { + Future createCommunity(CreateCommunityParam param); +} diff --git a/lib/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_bloc.dart b/lib/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_bloc.dart new file mode 100644 index 00000000..817b1e0e --- /dev/null +++ b/lib/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_bloc.dart @@ -0,0 +1,36 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.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/create_community/domain/param/create_community_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/create_community/domain/services/create_community_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; + +part 'create_community_event.dart'; +part 'create_community_state.dart'; + +class CreateCommunityBloc extends Bloc { + final CreateCommunityService _createCommunityService; + + CreateCommunityBloc( + this._createCommunityService, + ) : super(CreateCommunityInitial()) { + on(_onCreateCommunity); + } + + Future _onCreateCommunity( + CreateCommunity event, + Emitter emit, + ) async { + emit(CreateCommunityLoading()); + try { + final createdCommunity = await _createCommunityService.createCommunity( + event.param, + ); + emit(CreateCommunitySuccess(createdCommunity)); + } on APIException catch (e) { + emit(CreateCommunityFailure(e.message)); + } catch (e) { + emit(CreateCommunityFailure(e.toString())); + } + } +} diff --git a/lib/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_event.dart b/lib/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_event.dart new file mode 100644 index 00000000..d27c5752 --- /dev/null +++ b/lib/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_event.dart @@ -0,0 +1,17 @@ +part of 'create_community_bloc.dart'; + +sealed class CreateCommunityEvent extends Equatable { + const CreateCommunityEvent(); + + @override + List get props => []; +} + +final class CreateCommunity extends CreateCommunityEvent { + const CreateCommunity(this.param); + + final CreateCommunityParam param; + + @override + List get props => [param]; +} diff --git a/lib/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_state.dart b/lib/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_state.dart new file mode 100644 index 00000000..56345aab --- /dev/null +++ b/lib/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_state.dart @@ -0,0 +1,30 @@ +part of 'create_community_bloc.dart'; + +sealed class CreateCommunityState extends Equatable { + const CreateCommunityState(); + + @override + List get props => []; +} + +final class CreateCommunityInitial extends CreateCommunityState {} + +final class CreateCommunityLoading extends CreateCommunityState {} + +final class CreateCommunitySuccess extends CreateCommunityState { + const CreateCommunitySuccess(this.community); + + final CommunityModel community; + + @override + List get props => [community]; +} + +final class CreateCommunityFailure extends CreateCommunityState { + final String message; + + const CreateCommunityFailure(this.message); + + @override + List get props => [message]; +} diff --git a/lib/pages/space_management_v2/modules/products/data/services/remote_products_service.dart b/lib/pages/space_management_v2/modules/products/data/services/remote_products_service.dart new file mode 100644 index 00000000..6e501b44 --- /dev/null +++ b/lib/pages/space_management_v2/modules/products/data/services/remote_products_service.dart @@ -0,0 +1,46 @@ +import 'package:dio/dio.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/params/load_products_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/products/domain/services/products_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; + +class RemoteProductsService implements ProductsService { + const RemoteProductsService(this._httpService); + + final HTTPService _httpService; + + static const _defaultErrorMessage = 'Failed to load devices'; + + @override + Future> getProducts(LoadProductsParam param) async { + try { + final response = await _httpService.get( + path: 'devices', + queryParameters: { + 'spaceUuid': param.spaceUuid, + if (param.type != null) 'type': param.type, + if (param.status != null) 'status': param.status, + }, + expectedResponseModel: (data) { + return (data as List) + .map((e) => Product.fromJson(e as Map)) + .toList(); + }, + ); + return response; + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [ + _defaultErrorMessage, + errorMessage, + ].join(': '); + throw APIException(formattedErrorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } +} diff --git a/lib/pages/space_management_v2/modules/products/domain/models/product.dart b/lib/pages/space_management_v2/modules/products/domain/models/product.dart new file mode 100644 index 00000000..cd837121 --- /dev/null +++ b/lib/pages/space_management_v2/modules/products/domain/models/product.dart @@ -0,0 +1,28 @@ +import 'package:equatable/equatable.dart'; + +class Product extends Equatable { + final String uuid; + final String name; + + const Product({ + required this.uuid, + required this.name, + }); + + factory Product.fromJson(Map json) { + return Product( + uuid: json['uuid'] as String, + name: json['name'] as String, + ); + } + + Map toJson() { + return { + 'uuid': uuid, + 'name': name, + }; + } + + @override + List get props => [uuid, name]; +} diff --git a/lib/pages/space_management_v2/modules/products/domain/params/load_products_param.dart b/lib/pages/space_management_v2/modules/products/domain/params/load_products_param.dart new file mode 100644 index 00000000..87194ae7 --- /dev/null +++ b/lib/pages/space_management_v2/modules/products/domain/params/load_products_param.dart @@ -0,0 +1,11 @@ +class LoadProductsParam { + final String spaceUuid; + final String? type; + final String? status; + + const LoadProductsParam({ + required this.spaceUuid, + this.type, + this.status, + }); +} diff --git a/lib/pages/space_management_v2/modules/products/domain/services/products_service.dart b/lib/pages/space_management_v2/modules/products/domain/services/products_service.dart new file mode 100644 index 00000000..18554382 --- /dev/null +++ b/lib/pages/space_management_v2/modules/products/domain/services/products_service.dart @@ -0,0 +1,6 @@ +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/params/load_products_param.dart'; + +abstract class ProductsService { + Future> getProducts(LoadProductsParam param); +} diff --git a/lib/pages/space_management_v2/modules/products/presentation/bloc/products_bloc.dart b/lib/pages/space_management_v2/modules/products/presentation/bloc/products_bloc.dart new file mode 100644 index 00000000..1ce6ae89 --- /dev/null +++ b/lib/pages/space_management_v2/modules/products/presentation/bloc/products_bloc.dart @@ -0,0 +1,32 @@ +import 'package:bloc/bloc.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/products/domain/params/load_products_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/products/domain/services/products_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; + +part 'products_event.dart'; +part 'products_state.dart'; + +class ProductsBloc extends Bloc { + final ProductsService _deviceService; + + ProductsBloc(this._deviceService) : super(ProductsInitial()) { + on(_onLoadProducts); + } + + Future _onLoadProducts( + LoadProducts event, + Emitter emit, + ) async { + emit(ProductsLoading()); + try { + final devices = await _deviceService.getProducts(event.param); + emit(ProductsLoaded(devices)); + } on APIException catch (e) { + emit(ProductsFailure(e.message)); + } catch (e) { + emit(ProductsFailure(e.toString())); + } + } +} diff --git a/lib/pages/space_management_v2/modules/products/presentation/bloc/products_event.dart b/lib/pages/space_management_v2/modules/products/presentation/bloc/products_event.dart new file mode 100644 index 00000000..971b6d27 --- /dev/null +++ b/lib/pages/space_management_v2/modules/products/presentation/bloc/products_event.dart @@ -0,0 +1,17 @@ +part of 'products_bloc.dart'; + +sealed class ProductsEvent extends Equatable { + const ProductsEvent(); + + @override + List get props => []; +} + +final class LoadProducts extends ProductsEvent { + const LoadProducts(this.param); + + final LoadProductsParam param; + + @override + List get props => [param]; +} diff --git a/lib/pages/space_management_v2/modules/products/presentation/bloc/products_state.dart b/lib/pages/space_management_v2/modules/products/presentation/bloc/products_state.dart new file mode 100644 index 00000000..d5622cd3 --- /dev/null +++ b/lib/pages/space_management_v2/modules/products/presentation/bloc/products_state.dart @@ -0,0 +1,30 @@ +part of 'products_bloc.dart'; + +sealed class ProductsState extends Equatable { + const ProductsState(); + + @override + List get props => []; +} + +final class ProductsInitial extends ProductsState {} + +final class ProductsLoading extends ProductsState {} + +final class ProductsLoaded extends ProductsState { + final List products; + + const ProductsLoaded(this.products); + + @override + List get props => [products]; +} + +final class ProductsFailure extends ProductsState { + final String message; + + const ProductsFailure(this.message); + + @override + List get props => [message]; +} diff --git a/lib/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart b/lib/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart new file mode 100644 index 00000000..2e999361 --- /dev/null +++ b/lib/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart @@ -0,0 +1,40 @@ +import 'package:dio/dio.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/params/load_spaces_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; + +class RemoteSpaceDetailsService implements SpaceDetailsService { + final HTTPService _httpService; + + RemoteSpaceDetailsService({ + required HTTPService httpService, + }) : _httpService = httpService; + + static const _defaultErrorMessage = 'Failed to load space details'; + + @override + Future getSpaceDetails(LoadSpacesParam param) async { + try { + final response = await _httpService.get( + path: 'endpoint', + expectedResponseModel: (data) { + return SpaceDetailsModel.fromJson(data as Map); + }, + ); + return response; + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join( + ': ', + ); + throw APIException(formattedErrorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } +} diff --git a/lib/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart b/lib/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart new file mode 100644 index 00000000..891e7eb2 --- /dev/null +++ b/lib/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart @@ -0,0 +1,108 @@ +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'; + +class SpaceDetailsModel extends Equatable { + final String uuid; + final String spaceName; + final String icon; + final List productAllocations; + final List subspaces; + + const SpaceDetailsModel({ + required this.uuid, + required this.spaceName, + required this.icon, + required this.productAllocations, + required this.subspaces, + }); + + factory SpaceDetailsModel.fromJson(Map json) { + return SpaceDetailsModel( + uuid: json['uuid'] as String, + spaceName: json['spaceName'] as String, + icon: json['icon'] as String, + productAllocations: (json['productAllocations'] as List) + .map((e) => ProductAllocation.fromJson(e as Map)) + .toList(), + subspaces: (json['subspaces'] as List) + .map((e) => Subspace.fromJson(e as Map)) + .toList(), + ); + } + + Map toJson() { + return { + 'uuid': uuid, + 'spaceName': spaceName, + 'icon': icon, + 'productAllocations': productAllocations.map((e) => e.toJson()).toList(), + 'subspaces': subspaces.map((e) => e.toJson()).toList(), + }; + } + + @override + List get props => [uuid, spaceName, icon, productAllocations, subspaces]; +} + +class ProductAllocation extends Equatable { + final Product product; + final Tag tag; + final String? location; + + const ProductAllocation({ + required this.product, + required this.tag, + this.location, + }); + + factory ProductAllocation.fromJson(Map json) { + return ProductAllocation( + product: Product.fromJson(json['product'] as Map), + tag: Tag.fromJson(json['tag'] as Map), + ); + } + + Map toJson() { + return { + 'product': product.toJson(), + 'tag': tag.toJson(), + }; + } + + @override + List get props => [product, tag]; +} + +class Subspace extends Equatable { + final String uuid; + final String name; + final List productAllocations; + + const Subspace({ + required this.uuid, + required this.name, + required this.productAllocations, + }); + + factory Subspace.fromJson(Map json) { + return Subspace( + uuid: json['uuid'] as String, + name: json['name'] as String, + productAllocations: (json['productAllocations'] as List) + .map((e) => ProductAllocation.fromJson(e as Map)) + .toList(), + ); + } + + Map toJson() { + return { + 'uuid': uuid, + 'name': name, + 'productAllocations': productAllocations.map((e) => e.toJson()).toList(), + }; + } + + @override + List get props => [uuid, name, productAllocations]; +} diff --git a/lib/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart b/lib/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart new file mode 100644 index 00000000..5324ed98 --- /dev/null +++ b/lib/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart @@ -0,0 +1,3 @@ +class LoadSpacesParam { + const LoadSpacesParam(); +} diff --git a/lib/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart b/lib/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart new file mode 100644 index 00000000..b032560b --- /dev/null +++ b/lib/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart @@ -0,0 +1,6 @@ +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/params/load_spaces_param.dart'; + +abstract class SpaceDetailsService { + Future getSpaceDetails(LoadSpacesParam param); +} diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart b/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart new file mode 100644 index 00000000..59c1a06d --- /dev/null +++ b/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart @@ -0,0 +1,34 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.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/params/load_spaces_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; + +part 'space_details_event.dart'; +part 'space_details_state.dart'; + +class SpaceDetailsBloc extends Bloc { + final SpaceDetailsService _spaceDetailsService; + + SpaceDetailsBloc(this._spaceDetailsService) : super(SpaceDetailsInitial()) { + on(_onLoadSpaceDetails); + } + + Future _onLoadSpaceDetails( + LoadSpaceDetails event, + Emitter emit, + ) async { + emit(SpaceDetailsLoading()); + try { + final spaceDetails = await _spaceDetailsService.getSpaceDetails( + event.param, + ); + emit(SpaceDetailsLoaded(spaceDetails)); + } on APIException catch (e) { + emit(SpaceDetailsFailure(e.message)); + } catch (e) { + emit(SpaceDetailsFailure(e.toString())); + } + } +} diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_event.dart b/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_event.dart new file mode 100644 index 00000000..fe559e26 --- /dev/null +++ b/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_event.dart @@ -0,0 +1,17 @@ +part of 'space_details_bloc.dart'; + +sealed class SpaceDetailsEvent extends Equatable { + const SpaceDetailsEvent(); + + @override + List get props => []; +} + +class LoadSpaceDetails extends SpaceDetailsEvent { + const LoadSpaceDetails(this.param); + + final LoadSpacesParam param; + + @override + List get props => [param]; +} diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_state.dart b/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_state.dart new file mode 100644 index 00000000..c7378f89 --- /dev/null +++ b/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_state.dart @@ -0,0 +1,30 @@ +part of 'space_details_bloc.dart'; + +sealed class SpaceDetailsState extends Equatable { + const SpaceDetailsState(); + + @override + List get props => []; +} + +final class SpaceDetailsInitial extends SpaceDetailsState {} + +final class SpaceDetailsLoading extends SpaceDetailsState {} + +final class SpaceDetailsLoaded extends SpaceDetailsState { + final SpaceDetailsModel spaceDetails; + + const SpaceDetailsLoaded(this.spaceDetails); + + @override + List get props => [spaceDetails]; +} + +final class SpaceDetailsFailure extends SpaceDetailsState { + final String message; + + const SpaceDetailsFailure(this.message); + + @override + List get props => [message]; +} diff --git a/lib/pages/space_management_v2/modules/tags/data/services/remote_tags_service.dart b/lib/pages/space_management_v2/modules/tags/data/services/remote_tags_service.dart new file mode 100644 index 00000000..b5545bd3 --- /dev/null +++ b/lib/pages/space_management_v2/modules/tags/data/services/remote_tags_service.dart @@ -0,0 +1,49 @@ +import 'package:dio/dio.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/params/load_tags_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/services/tags_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + +final class RemoteTagsService implements TagsService { + const RemoteTagsService(this._httpService); + + final HTTPService _httpService; + + static const _defaultErrorMessage = 'Failed to load tags'; + + @override + Future> loadTags(LoadTagsParam param) async { + if (param.projectUuid == null) { + throw Exception('Project UUID is required'); + } + + try { + final response = await _httpService.get( + path: ApiEndpoints.listTags.replaceAll( + '{projectUuid}', + param.projectUuid!, + ), + expectedResponseModel: (json) { + final result = json as Map; + final data = result['data'] as List; + return data.map((e) => Tag.fromJson(e as Map)).toList(); + }, + ); + return response; + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [ + _defaultErrorMessage, + errorMessage, + ].join(': '); + throw APIException(formattedErrorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } +} diff --git a/lib/pages/space_management_v2/modules/tags/domain/models/tag.dart b/lib/pages/space_management_v2/modules/tags/domain/models/tag.dart new file mode 100644 index 00000000..1044d888 --- /dev/null +++ b/lib/pages/space_management_v2/modules/tags/domain/models/tag.dart @@ -0,0 +1,36 @@ +import 'package:equatable/equatable.dart'; + +class Tag extends Equatable { + final String uuid; + final String name; + final String createdAt; + final String updatedAt; + + const Tag({ + required this.uuid, + required this.name, + required this.createdAt, + required this.updatedAt, + }); + + factory Tag.fromJson(Map json) { + return Tag( + uuid: json['uuid'] as String, + name: json['name'] as String, + createdAt: json['createdAt'] as String, + updatedAt: json['updatedAt'] as String, + ); + } + + Map toJson() { + return { + 'uuid': uuid, + 'name': name, + 'createdAt': createdAt, + 'updatedAt': updatedAt, + }; + } + + @override + List get props => [uuid, name, createdAt, updatedAt]; +} diff --git a/lib/pages/space_management_v2/modules/tags/domain/params/load_tags_param.dart b/lib/pages/space_management_v2/modules/tags/domain/params/load_tags_param.dart new file mode 100644 index 00000000..00bc341e --- /dev/null +++ b/lib/pages/space_management_v2/modules/tags/domain/params/load_tags_param.dart @@ -0,0 +1,5 @@ +class LoadTagsParam { + final String? projectUuid; + + const LoadTagsParam({this.projectUuid}); +} diff --git a/lib/pages/space_management_v2/modules/tags/domain/services/tags_service.dart b/lib/pages/space_management_v2/modules/tags/domain/services/tags_service.dart new file mode 100644 index 00000000..ae097020 --- /dev/null +++ b/lib/pages/space_management_v2/modules/tags/domain/services/tags_service.dart @@ -0,0 +1,6 @@ +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/params/load_tags_param.dart'; + +abstract interface class TagsService { + Future> loadTags(LoadTagsParam param); +} diff --git a/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_bloc.dart b/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_bloc.dart new file mode 100644 index 00000000..e51884cb --- /dev/null +++ b/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_bloc.dart @@ -0,0 +1,32 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.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/params/load_tags_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/tags/domain/services/tags_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; + +part 'tags_event.dart'; +part 'tags_state.dart'; + +class TagsBloc extends Bloc { + final TagsService _tagsService; + + TagsBloc(this._tagsService) : super(TagsInitial()) { + on(_onLoadTags); + } + + Future _onLoadTags( + LoadTags event, + Emitter emit, + ) async { + emit(TagsLoading()); + try { + final tags = await _tagsService.loadTags(event.param); + emit(TagsLoaded(tags)); + } on APIException catch (e) { + emit(TagsFailure(e.message)); + } catch (e) { + emit(TagsFailure(e.toString())); + } + } +} diff --git a/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_event.dart b/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_event.dart new file mode 100644 index 00000000..99134cab --- /dev/null +++ b/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_event.dart @@ -0,0 +1,17 @@ +part of 'tags_bloc.dart'; + +abstract class TagsEvent extends Equatable { + const TagsEvent(); + + @override + List get props => []; +} + +class LoadTags extends TagsEvent { + final LoadTagsParam param; + + const LoadTags(this.param); + + @override + List get props => [param]; +} diff --git a/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_state.dart b/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_state.dart new file mode 100644 index 00000000..7afe55c9 --- /dev/null +++ b/lib/pages/space_management_v2/modules/tags/presentation/bloc/tags_state.dart @@ -0,0 +1,30 @@ +part of 'tags_bloc.dart'; + +abstract class TagsState extends Equatable { + const TagsState(); + + @override + List get props => []; +} + +class TagsInitial extends TagsState {} + +class TagsLoading extends TagsState {} + +class TagsLoaded extends TagsState { + final List tags; + + const TagsLoaded(this.tags); + + @override + List get props => [tags]; +} + +class TagsFailure extends TagsState { + final String message; + + const TagsFailure(this.message); + + @override + List get props => [message]; +} diff --git a/lib/pages/space_management_v2/modules/update_community/data/services/remote_update_community_service.dart b/lib/pages/space_management_v2/modules/update_community/data/services/remote_update_community_service.dart new file mode 100644 index 00000000..6c550673 --- /dev/null +++ b/lib/pages/space_management_v2/modules/update_community/data/services/remote_update_community_service.dart @@ -0,0 +1,39 @@ +import 'package:dio/dio.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/update_community/domain/params/update_community_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; + +class RemoteUpdateCommunityService implements UpdateCommunityService { + const RemoteUpdateCommunityService(this._httpService); + + final HTTPService _httpService; + + static const _defaultErrorMessage = 'Failed to update community'; + + @override + Future updateCommunity(UpdateCommunityParam param) async { + try { + final response = await _httpService.put( + path: 'endpoint', + expectedResponseModel: (data) => CommunityModel.fromJson( + data as Map, + ), + ); + return response; + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [ + _defaultErrorMessage, + errorMessage, + ].join(': '); + throw APIException(formattedErrorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } +} diff --git a/lib/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart b/lib/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart new file mode 100644 index 00000000..69dfc4e2 --- /dev/null +++ b/lib/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart @@ -0,0 +1,10 @@ +import 'package:equatable/equatable.dart'; + +class UpdateCommunityParam extends Equatable { + const UpdateCommunityParam({required this.name}); + + final String name; + + @override + List get props => [name]; +} diff --git a/lib/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart b/lib/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart new file mode 100644 index 00000000..9703fdc6 --- /dev/null +++ b/lib/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart @@ -0,0 +1,6 @@ +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart'; + +abstract class UpdateCommunityService { + Future updateCommunity(UpdateCommunityParam param); +} diff --git a/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_bloc.dart b/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_bloc.dart new file mode 100644 index 00000000..4e913c22 --- /dev/null +++ b/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_bloc.dart @@ -0,0 +1,36 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.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/update_community/domain/params/update_community_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; + +part 'update_community_event.dart'; +part 'update_community_state.dart'; + +class UpdateCommunityBloc extends Bloc { + final UpdateCommunityService _updateCommunityService; + + UpdateCommunityBloc( + this._updateCommunityService, + ) : super(UpdateCommunityInitial()) { + on(_onUpdateCommunity); + } + + Future _onUpdateCommunity( + UpdateCommunity event, + Emitter emit, + ) async { + emit(UpdateCommunityLoading()); + try { + final updatedCommunity = await _updateCommunityService.updateCommunity( + event.param, + ); + emit(UpdateCommunitySuccess(updatedCommunity)); + } on APIException catch (e) { + emit(UpdateCommunityFailure(e.message)); + } catch (e) { + emit(UpdateCommunityFailure(e.toString())); + } + } +} diff --git a/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_event.dart b/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_event.dart new file mode 100644 index 00000000..234298a0 --- /dev/null +++ b/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_event.dart @@ -0,0 +1,17 @@ +part of 'update_community_bloc.dart'; + +sealed class UpdateCommunityEvent extends Equatable { + const UpdateCommunityEvent(); + + @override + List get props => []; +} + +final class UpdateCommunity extends UpdateCommunityEvent { + const UpdateCommunity(this.param); + + final UpdateCommunityParam param; + + @override + List get props => [param]; +} diff --git a/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_state.dart b/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_state.dart new file mode 100644 index 00000000..9126be0a --- /dev/null +++ b/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_state.dart @@ -0,0 +1,30 @@ +part of 'update_community_bloc.dart'; + +sealed class UpdateCommunityState extends Equatable { + const UpdateCommunityState(); + + @override + List get props => []; +} + +final class UpdateCommunityInitial extends UpdateCommunityState {} + +final class UpdateCommunityLoading extends UpdateCommunityState {} + +final class UpdateCommunitySuccess extends UpdateCommunityState { + final CommunityModel community; + + const UpdateCommunitySuccess(this.community); + + @override + List get props => [community]; +} + +final class UpdateCommunityFailure extends UpdateCommunityState { + final String message; + + const UpdateCommunityFailure(this.message); + + @override + List get props => [message]; +} diff --git a/lib/pages/space_management_v2/modules/update_space/data/services/remote_update_space_service.dart b/lib/pages/space_management_v2/modules/update_space/data/services/remote_update_space_service.dart new file mode 100644 index 00000000..b15e6095 --- /dev/null +++ b/lib/pages/space_management_v2/modules/update_space/data/services/remote_update_space_service.dart @@ -0,0 +1,40 @@ +import 'package:dio/dio.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/update_space/domain/services/update_space_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; + +class RemoteUpdateSpaceService implements UpdateSpaceService { + const RemoteUpdateSpaceService(this._httpService); + + final HTTPService _httpService; + + static const _defaultErrorMessage = 'Failed to update space'; + + @override + Future updateSpace(SpaceDetailsModel space) async { + try { + final response = await _httpService.put( + path: 'endpoint', + body: space.toJson(), + expectedResponseModel: (data) => SpaceDetailsModel.fromJson( + data as Map, + ), + ); + + return response; + } on DioException catch (e) { + final message = e.response?.data as Map?; + final error = message?['error'] as Map?; + final errorMessage = error?['error'] as String? ?? ''; + final formattedErrorMessage = [ + _defaultErrorMessage, + errorMessage, + ].join(': '); + throw APIException(formattedErrorMessage); + } catch (e) { + final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': '); + throw APIException(formattedErrorMessage); + } + } +} diff --git a/lib/pages/space_management_v2/modules/update_space/domain/services/update_space_service.dart b/lib/pages/space_management_v2/modules/update_space/domain/services/update_space_service.dart new file mode 100644 index 00000000..29bc9419 --- /dev/null +++ b/lib/pages/space_management_v2/modules/update_space/domain/services/update_space_service.dart @@ -0,0 +1,5 @@ +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; + +abstract class UpdateSpaceService { + Future updateSpace(SpaceDetailsModel space); +} diff --git a/lib/pages/space_management_v2/modules/update_space/presentation/bloc/update_space_bloc.dart b/lib/pages/space_management_v2/modules/update_space/presentation/bloc/update_space_bloc.dart new file mode 100644 index 00000000..3bc4e187 --- /dev/null +++ b/lib/pages/space_management_v2/modules/update_space/presentation/bloc/update_space_bloc.dart @@ -0,0 +1,31 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.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/update_space/domain/services/update_space_service.dart'; +import 'package:syncrow_web/services/api/api_exception.dart'; + +part 'update_space_event.dart'; +part 'update_space_state.dart'; + +class UpdateSpaceBloc extends Bloc { + final UpdateSpaceService _updateSpaceService; + + UpdateSpaceBloc(this._updateSpaceService) : super(UpdateSpaceInitial()) { + on(_onUpdateSpace); + } + + Future _onUpdateSpace( + UpdateSpace event, + Emitter emit, + ) async { + emit(UpdateSpaceLoading()); + try { + final updatedSpace = await _updateSpaceService.updateSpace(event.space); + emit(UpdateSpaceSuccess(updatedSpace)); + } on APIException catch (e) { + emit(UpdateSpaceFailure(e.message)); + } catch (e) { + emit(UpdateSpaceFailure(e.toString())); + } + } +} diff --git a/lib/pages/space_management_v2/modules/update_space/presentation/bloc/update_space_event.dart b/lib/pages/space_management_v2/modules/update_space/presentation/bloc/update_space_event.dart new file mode 100644 index 00000000..b7d476af --- /dev/null +++ b/lib/pages/space_management_v2/modules/update_space/presentation/bloc/update_space_event.dart @@ -0,0 +1,17 @@ +part of 'update_space_bloc.dart'; + +sealed class UpdateSpaceEvent extends Equatable { + const UpdateSpaceEvent(); + + @override + List get props => []; +} + +final class UpdateSpace extends UpdateSpaceEvent { + const UpdateSpace(this.space); + + final SpaceDetailsModel space; + + @override + List get props => [space]; +} diff --git a/lib/pages/space_management_v2/modules/update_space/presentation/bloc/update_space_state.dart b/lib/pages/space_management_v2/modules/update_space/presentation/bloc/update_space_state.dart new file mode 100644 index 00000000..f0bc5a2b --- /dev/null +++ b/lib/pages/space_management_v2/modules/update_space/presentation/bloc/update_space_state.dart @@ -0,0 +1,30 @@ +part of 'update_space_bloc.dart'; + +sealed class UpdateSpaceState extends Equatable { + const UpdateSpaceState(); + + @override + List get props => []; +} + +final class UpdateSpaceInitial extends UpdateSpaceState {} + +final class UpdateSpaceLoading extends UpdateSpaceState {} + +final class UpdateSpaceSuccess extends UpdateSpaceState { + final SpaceDetailsModel space; + + const UpdateSpaceSuccess(this.space); + + @override + List get props => [space]; +} + +final class UpdateSpaceFailure extends UpdateSpaceState { + final String message; + + const UpdateSpaceFailure(this.message); + + @override + List get props => [message]; +} diff --git a/lib/services/home_api.dart b/lib/services/home_api.dart index c1e67add..40692a40 100644 --- a/lib/services/home_api.dart +++ b/lib/services/home_api.dart @@ -3,14 +3,20 @@ import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; class HomeApi { - Future fetchUserInfo(userId) async { - final response = await HTTPService().get( - path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!), + Future fetchUserInfo(String userId) async { + try { + final response = await HTTPService().get( + path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId), showServerMessage: true, expectedResponseModel: (json) { - return UserModel.fromJson(json); - }); - return response; + final user = UserModel.fromJson(json as Map); + return user; + }, + ); + return response; + } catch (e) { + return null; + } } Future fetchTerms() async {