From b593e75c6741b4fdee9e579294309414619e36fe Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Fri, 23 May 2025 02:17:23 -0500 Subject: [PATCH] build main structure and build data layer and space managment bloc --- lib/core/network/custom_exceptions.dart | 16 +++ lib/core/network/dio.dart | 36 ++++++ lib/core/network/end_points.dart | 3 + lib/core/network/enums.dart | 50 ++++++++ lib/core/network/request.dart | 121 ++++++++++++++++++ .../communities_bloc/communities_bloc.dart | 37 ++++++ .../communities_bloc/communities_event.dart | 10 ++ .../communities_bloc/communities_state.dart | 30 +++++ .../refactor/data/models/community_model.dart | 21 +++ .../refactor/data/models/device_model.dart | 20 +++ .../refactor/data/models/space_model.dart | 27 ++++ .../refactor/data/models/sub_space_model.dart | 19 +++ .../space_managment_remote_source.dart | 17 +++ .../screens/space_mamagment_screen.dart | 0 14 files changed, 407 insertions(+) create mode 100644 lib/core/network/custom_exceptions.dart create mode 100644 lib/core/network/dio.dart create mode 100644 lib/core/network/end_points.dart create mode 100644 lib/core/network/enums.dart create mode 100644 lib/core/network/request.dart create mode 100644 lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_bloc.dart create mode 100644 lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_event.dart create mode 100644 lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_state.dart create mode 100644 lib/pages/spaces_management/refactor/data/models/community_model.dart create mode 100644 lib/pages/spaces_management/refactor/data/models/device_model.dart create mode 100644 lib/pages/spaces_management/refactor/data/models/space_model.dart create mode 100644 lib/pages/spaces_management/refactor/data/models/sub_space_model.dart create mode 100644 lib/pages/spaces_management/refactor/data/sources/space_managment_remote_source.dart create mode 100644 lib/pages/spaces_management/refactor/presentation/screens/space_mamagment_screen.dart diff --git a/lib/core/network/custom_exceptions.dart b/lib/core/network/custom_exceptions.dart new file mode 100644 index 00000000..23b81a8d --- /dev/null +++ b/lib/core/network/custom_exceptions.dart @@ -0,0 +1,16 @@ + +import 'enums.dart'; + +class GenericException implements Exception { + final ExceptionType type; + final String errorMessage; + const GenericException( + {required this.type, this.errorMessage = "Unknown Error"}); + + @override + String toString() { + return errorMessage; + } + + List get props => [type, errorMessage]; +} diff --git a/lib/core/network/dio.dart b/lib/core/network/dio.dart new file mode 100644 index 00000000..e01fef90 --- /dev/null +++ b/lib/core/network/dio.dart @@ -0,0 +1,36 @@ +import 'package:dio/dio.dart'; + +import '../../utils/constants/api_const.dart'; + +class DioInstance { + Dio? _dio; + + Dio get dio => _dio ?? _instantiate(); + + String? baseUrl; + + DioInstance({this.baseUrl}); + + Dio _instantiate() { + Dio dio = Dio( + BaseOptions( + baseUrl: baseUrl ?? '${ApiEndpoints.baseUrl}/', + receiveDataWhenStatusError: true, + headers: { + 'content_Type': 'application/json', + }, + ), + ); + + dio.interceptors.add( + LogInterceptor( + responseHeader: false, + requestHeader: false, + requestBody: true, + responseBody: true, + ), + ); + + return dio; + } +} diff --git a/lib/core/network/end_points.dart b/lib/core/network/end_points.dart new file mode 100644 index 00000000..3e2afd84 --- /dev/null +++ b/lib/core/network/end_points.dart @@ -0,0 +1,3 @@ +class EndPoints { + static const String fetchCommunities = 'projects/{projectUuid}/communities'; +} diff --git a/lib/core/network/enums.dart b/lib/core/network/enums.dart new file mode 100644 index 00000000..488d7cb9 --- /dev/null +++ b/lib/core/network/enums.dart @@ -0,0 +1,50 @@ +enum RequestType { + get, + post, + delete, + put, +} + +enum ExceptionType { + notAuthenticated, + connectionError, + // related to http status code exceptions + notAuthorized, + notFound, + internalServerException, + serviceUnavailableException, + pageGone, + + // related to bad request status code + // related to auth requests + invalidCredentials, + solutionAlreadySunmitted, + invalidValidation, + // other + other, +} + +enum ExceptionMessage { + NOT_AUTHENTICATED, + INVALID_CREDENTIALS, + The_password_field_must_be_at_least_8_characters, + SOLUTION_ALREADY_SUBMITTED, + you_are_not_authorized, + page_not_found, + page_gone, + INTERNAL_SERVER_ERROR, + service_unavailable, +} + +enum NotificationsType { + payment, + transporation, + product, + zero, +} + +enum ImagesType { + assets, + svg, + network, +} diff --git a/lib/core/network/request.dart b/lib/core/network/request.dart new file mode 100644 index 00000000..c242b8a6 --- /dev/null +++ b/lib/core/network/request.dart @@ -0,0 +1,121 @@ +import 'dart:convert'; + +import 'package:dio/dio.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +import '../../pages/auth/model/token.dart'; +import 'custom_exceptions.dart'; +import 'dio.dart'; +import 'enums.dart'; + +class Request { + String endPoint; + bool? autherized; + bool? isFormData; + RequestType? method; + Map? headers; + final Map? queryParams; + Map? body; + Duration? receiveTimeout; + Request( + this.endPoint, { + this.autherized, + this.isFormData, + this.method, + this.headers, + this.queryParams, + this.body, + this.receiveTimeout, + }) { + headers = { + 'content_Type': 'application/json', + 'Accept': 'application/json', + }; + } + Future> sendRequest() async { + Response? response; + if (autherized != null && autherized!) { + final storage = const FlutterSecureStorage(); + final token = await storage.read(key: Token.loginAccessTokenKey); + if (token != null) { + headers!["authorization"] = "Bearer $token"; + } + } + try { + response = await DioInstance().dio.request( + endPoint, + queryParameters: queryParams, + data: isFormData != null && isFormData == true + ? FormData.fromMap(body!) + : body, + options: Options( + method: method!.name.toUpperCase(), + headers: headers, + contentType: 'application/json', + receiveTimeout: receiveTimeout, + ), + ); + if (response.statusCode! >= 200 && response.statusCode! < 300) { + return response.data; + } + } on DioException catch (error) { + if (error.type == DioExceptionType.badResponse) { + throw badRequestException[error.response!.data["error"]] ?? + const GenericException( + type: ExceptionType.other, + ); + } + if (error.type == DioExceptionType.connectionError || + error.type == DioExceptionType.connectionTimeout || + error.type == DioExceptionType.receiveTimeout || + error.type == DioExceptionType.sendTimeout || + error.type == DioExceptionType.unknown) { + throw const GenericException( + type: ExceptionType.connectionError, + errorMessage: 'no_internet_connection', + ); + } + } + return {}; + } + + Map toJson() => { + 'endPoint': endPoint, + 'method': method!.name.toUpperCase(), + 'body': json.encode(body), + 'headers': headers, + 'queryParams': queryParams, + 'autherized': autherized, + 'isFormData': isFormData, + }; + @override + String toString() { + return jsonEncode(toJson()); + } + + List get props => [ + endPoint, + autherized, + isFormData, + method, + headers, + queryParams, + body, + receiveTimeout, + ]; +} + +Map badRequestException = { + ExceptionMessage.INVALID_CREDENTIALS.name: const GenericException( + type: ExceptionType.invalidCredentials, + errorMessage: "Invalid credentials ...", + ), + "The password field must be at least 8 characters.": const GenericException( + type: ExceptionType.invalidValidation, + errorMessage: 'password must be 8 or more characters', + ), + ExceptionMessage.NOT_AUTHENTICATED.name: const GenericException( + type: ExceptionType.notAuthenticated, + errorMessage: "not authenticated", + ), +}; diff --git a/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_bloc.dart b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_bloc.dart new file mode 100644 index 00000000..286318d5 --- /dev/null +++ b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_bloc.dart @@ -0,0 +1,37 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; + +import '../../data/models/community_model.dart'; +import '../../data/sources/space_managment_remote_source.dart'; + +part 'communities_event.dart'; +part 'communities_state.dart'; + +class CommunitiesBloc extends Bloc { + SpaceManagementRemoteSource spaceManagementRemoteSource = + SpaceManagementRemoteSource(); + CommunitiesBloc() : super(CommunitiesInitial()) { + on((event, emit) { + if (event is FetchCommunitiesEvent) { + _fetchCommunities(emit); + } + }); + } + + Future _fetchCommunities(Emitter emit) async { + String? projectUuid = await ProjectManager.getProjectUUID(); + if (projectUuid == null) { + emit(const CommunitiesError("Project UUID is null")); + return; + } + emit(CommunitiesLoading()); + try { + final communities = + await spaceManagementRemoteSource.fetchCommunities(projectUuid); + emit(CommunitiesLoaded(communities)); + } catch (e) { + emit(CommunitiesError(e.toString())); + } + } +} diff --git a/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_event.dart b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_event.dart new file mode 100644 index 00000000..1d47af23 --- /dev/null +++ b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_event.dart @@ -0,0 +1,10 @@ +part of 'communities_bloc.dart'; + +sealed class CommunitiesEvent extends Equatable { + const CommunitiesEvent(); + + @override + List get props => []; +} + +class FetchCommunitiesEvent extends CommunitiesEvent {} \ No newline at end of file diff --git a/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_state.dart b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_state.dart new file mode 100644 index 00000000..747d8d82 --- /dev/null +++ b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_state.dart @@ -0,0 +1,30 @@ +part of 'communities_bloc.dart'; + +sealed class CommunitiesState extends Equatable { + const CommunitiesState(); + + @override + List get props => []; +} + +final class CommunitiesInitial extends CommunitiesState {} + +final class CommunitiesLoading extends CommunitiesState {} + +final class CommunitiesLoaded extends CommunitiesState { + final List communities; + + const CommunitiesLoaded(this.communities); + + @override + List get props => [communities]; +} + +final class CommunitiesError extends CommunitiesState { + final String message; + + const CommunitiesError(this.message); + + @override + List get props => [message]; +} diff --git a/lib/pages/spaces_management/refactor/data/models/community_model.dart b/lib/pages/spaces_management/refactor/data/models/community_model.dart new file mode 100644 index 00000000..88c62ad0 --- /dev/null +++ b/lib/pages/spaces_management/refactor/data/models/community_model.dart @@ -0,0 +1,21 @@ +import 'package:syncrow_web/pages/spaces_management/refactor/data/models/space_model.dart'; + +class CommunityModel { + String id, name; + List? spaces; + CommunityModel({ + required this.id, + required this.name, + this.spaces, + }); + + factory CommunityModel.fromJson(Map json) => CommunityModel( + id: json['id'], + name: json['name'], + spaces: SpaceModel.fromJsonList(json['spaces']), + ); + + static List fromJsonList(List jsonList) { + return jsonList.map((json) => CommunityModel.fromJson(json)).toList(); + } +} diff --git a/lib/pages/spaces_management/refactor/data/models/device_model.dart b/lib/pages/spaces_management/refactor/data/models/device_model.dart new file mode 100644 index 00000000..1d000a37 --- /dev/null +++ b/lib/pages/spaces_management/refactor/data/models/device_model.dart @@ -0,0 +1,20 @@ +class DeviceModel { + String id, name, tag; + String location; + DeviceModel({ + required this.id, + required this.name, + required this.tag, + required this.location, + }); + factory DeviceModel.fromJson(Map json) => DeviceModel( + id: json['id'], + name: json['name'], + tag: json['tag'], + location: json['location'], + ); + + static List fromJsonList(List jsonList) { + return jsonList.map((json) => DeviceModel.fromJson(json)).toList(); + } +} diff --git a/lib/pages/spaces_management/refactor/data/models/space_model.dart b/lib/pages/spaces_management/refactor/data/models/space_model.dart new file mode 100644 index 00000000..c2b6d73c --- /dev/null +++ b/lib/pages/spaces_management/refactor/data/models/space_model.dart @@ -0,0 +1,27 @@ +import 'device_model.dart'; + +class SpaceModel { + String id, parentId, name; + List? spaces; + List? devices; + + SpaceModel({ + required this.id, + required this.parentId, + required this.name, + this.spaces, + this.devices, + }); + + factory SpaceModel.fromJson(Map json) => SpaceModel( + id: json['id'], + parentId: json['parentId'], + name: json['name'], + spaces: SpaceModel.fromJsonList(json['spaces']), + devices: DeviceModel.fromJsonList(json['devices']), + ); + + static List fromJsonList(List jsonList) { + return jsonList.map((json) => SpaceModel.fromJson(json)).toList(); + } +} diff --git a/lib/pages/spaces_management/refactor/data/models/sub_space_model.dart b/lib/pages/spaces_management/refactor/data/models/sub_space_model.dart new file mode 100644 index 00000000..2893e0ed --- /dev/null +++ b/lib/pages/spaces_management/refactor/data/models/sub_space_model.dart @@ -0,0 +1,19 @@ +import 'device_model.dart'; + +class SubSpaceModel { + String id, name; + List? devices; + SubSpaceModel({ + required this.id, + required this.name, + this.devices, + }); + factory SubSpaceModel.fromJson(Map json) => SubSpaceModel( + id: json['id'], + name: json['name'], + devices: DeviceModel.fromJsonList(json['devices']), + ); + static List fromJsonList(List jsonList) { + return jsonList.map((json) => SubSpaceModel.fromJson(json)).toList(); + } +} diff --git a/lib/pages/spaces_management/refactor/data/sources/space_managment_remote_source.dart b/lib/pages/spaces_management/refactor/data/sources/space_managment_remote_source.dart new file mode 100644 index 00000000..189cabbf --- /dev/null +++ b/lib/pages/spaces_management/refactor/data/sources/space_managment_remote_source.dart @@ -0,0 +1,17 @@ +import 'package:syncrow_web/core/network/end_points.dart'; +import 'package:syncrow_web/core/network/enums.dart'; +import 'package:syncrow_web/core/network/request.dart'; + +import '../models/community_model.dart'; + +class SpaceManagementRemoteSource { + Future> fetchCommunities(String projectUuid) async { + Request request = Request( + EndPoints.fetchCommunities.replaceFirst('{projectUuid}', projectUuid), + method: RequestType.get, + autherized: true, + ); + final response = await request.sendRequest(); + return CommunityModel.fromJsonList((response['data'])); + } +} diff --git a/lib/pages/spaces_management/refactor/presentation/screens/space_mamagment_screen.dart b/lib/pages/spaces_management/refactor/presentation/screens/space_mamagment_screen.dart new file mode 100644 index 00000000..e69de29b