diff --git a/.github/.github/dependabot.yaml b/.github/.github/dependabot.yaml new file mode 100644 index 00000000..8ce92757 --- /dev/null +++ b/.github/.github/dependabot.yaml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "pub" + directory: "/" + schedule: + interval: "daily" 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 ad6ed4d8..c1bcba6a 100644 --- a/lib/pages/home/bloc/home_bloc.dart +++ b/lib/pages/home/bloc/home_bloc.dart @@ -13,30 +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())) { - // on(_createNode); + 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()); @@ -47,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(); @@ -57,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()); @@ -81,7 +83,7 @@ class HomeBloc extends Bloc { } } - List homeItems = [ + final List homeItems = [ HomeItemModel( title: 'Access Management', icon: Assets.accessIcon, @@ -126,41 +128,5 @@ class HomeBloc extends Bloc { }, color: const Color(0xFF023DFE), ), - - // HomeItemModel( - // title: 'Move in', - // icon: Assets.moveinIcon, - // active: false, - // onPress: (context) {}, - // color: ColorsManager.primaryColor, - // ), - // HomeItemModel( - // title: 'Construction', - // icon: Assets.constructionIcon, - // active: false, - // onPress: (context) {}, - // color: ColorsManager.primaryColor, - // ), - // HomeItemModel( - // title: 'Energy', - // icon: Assets.energyIcon, - // active: false, - // onPress: (context) {}, - // color: ColorsManager.slidingBlueColor.withOpacity(0.2), - // ), - // HomeItemModel( - // title: 'Integrations', - // icon: Assets.integrationsIcon, - // active: false, - // onPress: (context) {}, - // color: ColorsManager.slidingBlueColor.withOpacity(0.2), - // ), - // HomeItemModel( - // title: 'Asset', - // icon: Assets.assetIcon, - // active: false, - // onPress: (context) {}, - // color: ColorsManager.slidingBlueColor.withOpacity(0.2), - // ), ]; } 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/roles_and_permission/users_page/users_table/view/users_page.dart b/lib/pages/roles_and_permission/users_page/users_table/view/users_page.dart index 735ce839..767fd9a6 100644 --- a/lib/pages/roles_and_permission/users_page/users_table/view/users_page.dart +++ b/lib/pages/roles_and_permission/users_page/users_table/view/users_page.dart @@ -19,7 +19,6 @@ import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/style.dart'; - class UsersPage extends StatelessWidget { UsersPage({super.key}); diff --git a/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart b/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart index ca8aac06..dd73183a 100644 --- a/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart +++ b/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart @@ -4,8 +4,8 @@ import 'package:bloc/bloc.dart'; import 'package:dio/dio.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_status_update.dart'; import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart'; @@ -27,9 +27,6 @@ import 'package:uuid/uuid.dart'; part 'routine_event.dart'; part 'routine_state.dart'; -// String spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6'; -// String communityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9'; - class RoutineBloc extends Bloc { RoutineBloc() : super(const RoutineState()) { on(_onAddToIfContainer); @@ -173,45 +170,45 @@ class RoutineBloc extends Bloc { } } - Future _onLoadScenes( - LoadScenes event, Emitter emit) async { - emit(state.copyWith(isLoading: true, errorMessage: null)); - List scenes = []; - try { - BuildContext context = NavigationService.navigatorKey.currentContext!; - var createRoutineBloc = context.read(); - final projectUuid = await ProjectManager.getProjectUUID() ?? ''; - if (createRoutineBloc.selectedSpaceId == '' && - createRoutineBloc.selectedCommunityId == '') { - var spaceBloc = context.read(); - for (var communityId in spaceBloc.state.selectedCommunities) { - List spacesList = - spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? []; - for (var spaceId in spacesList) { - scenes.addAll( - await SceneApi.getScenes(spaceId, communityId, projectUuid)); - } +Future _onLoadScenes( + LoadScenes event, Emitter emit) async { + emit(state.copyWith(isLoading: true, errorMessage: null)); + List scenes = []; + try { + BuildContext context = NavigationService.navigatorKey.currentContext!; + var createRoutineBloc = context.read(); + final projectUuid = await ProjectManager.getProjectUUID() ?? ''; + if (createRoutineBloc.selectedSpaceId == '' && + createRoutineBloc.selectedCommunityId == '') { + var spaceBloc = context.read(); + for (var communityId in spaceBloc.state.selectedCommunities) { + List spacesList = + spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? []; + for (var spaceId in spacesList) { + scenes.addAll( + await SceneApi.getScenes(spaceId, communityId, projectUuid)); } - } else { - scenes.addAll(await SceneApi.getScenes( - createRoutineBloc.selectedSpaceId, - createRoutineBloc.selectedCommunityId, - projectUuid)); } - - emit(state.copyWith( - scenes: scenes, - isLoading: false, - )); - } catch (e) { - emit(state.copyWith( - isLoading: false, - loadScenesErrorMessage: 'Failed to load scenes', - errorMessage: '', - loadAutomationErrorMessage: '', - scenes: scenes)); + } else { + scenes.addAll(await SceneApi.getScenes( + createRoutineBloc.selectedSpaceId, + createRoutineBloc.selectedCommunityId, + projectUuid)); } + + emit(state.copyWith( + scenes: scenes, + isLoading: false, + )); + } catch (e) { + emit(state.copyWith( + isLoading: false, + loadScenesErrorMessage: 'Failed to load scenes', + errorMessage: '', + loadAutomationErrorMessage: '', + scenes: scenes)); } +} Future _onLoadAutomation( LoadAutomation event, Emitter emit) async { @@ -1163,8 +1160,8 @@ class RoutineBloc extends Bloc { if (result['success']) { add(ResetRoutineState()); - add(LoadAutomation()); - add(LoadScenes()); + add(const LoadAutomation()); + add(const LoadScenes()); } else { emit(state.copyWith( isLoading: false, 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 { diff --git a/lib/services/user_permission.dart b/lib/services/user_permission.dart index 3f02663d..90a82921 100644 --- a/lib/services/user_permission.dart +++ b/lib/services/user_permission.dart @@ -34,9 +34,8 @@ class UserPermissionApi { path: ApiEndpoints.roleTypes, showServerMessage: true, expectedResponseModel: (json) { - final List fetchedRoles = (json['data'] as List) - .map((item) => RoleTypeModel.fromJson(item)) - .toList(); + final List fetchedRoles = + (json['data'] as List).map((item) => RoleTypeModel.fromJson(item)).toList(); return fetchedRoles; }, ); @@ -48,9 +47,7 @@ class UserPermissionApi { path: ApiEndpoints.permission.replaceAll("roleUuid", roleUuid), showServerMessage: true, expectedResponseModel: (json) { - return (json as List) - .map((data) => PermissionOption.fromJson(data)) - .toList(); + return (json as List).map((data) => PermissionOption.fromJson(data)).toList(); }, ); return response ?? []; @@ -195,14 +192,10 @@ class UserPermissionApi { Future changeUserStatusById(userUuid, status, String projectUuid) async { try { - Map bodya = { - "disable": status, - "projectUuid": projectUuid - }; + Map bodya = {"disable": status, "projectUuid": projectUuid}; final response = await _httpService.put( - path: ApiEndpoints.changeUserStatus - .replaceAll("{invitedUserUuid}", userUuid), + path: ApiEndpoints.changeUserStatus.replaceAll("{invitedUserUuid}", userUuid), body: bodya, expectedResponseModel: (json) { return json['success']; diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 411e72a5..d58d0f28 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -1,7 +1,7 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; abstract class ApiEndpoints { - static const String projectUuid = "0e62577c-06fa-41b9-8a92-99a21fbaf51c"; + static const String projectUuid = "bcda711e-9fc2-4168-a05e-171b4026d1ff"; static String baseUrl = dotenv.env['BASE_URL'] ?? ''; static const String signUp = '/authentication/user/signup'; static const String login = '/authentication/user/login'; diff --git a/lib/utils/constants/temp_const.dart b/lib/utils/constants/temp_const.dart index e5847b98..f7bb3b85 100644 --- a/lib/utils/constants/temp_const.dart +++ b/lib/utils/constants/temp_const.dart @@ -1,3 +1,3 @@ class TempConst { - static const projectId = '0e62577c-06fa-41b9-8a92-99a21fbaf51c'; + static const projectId = 'bcda711e-9fc2-4168-a05e-171b4026d1ff'; }