build main structure and build data layer and space managment bloc

This commit is contained in:
Rafeek Alkhoudare
2025-05-23 02:17:23 -05:00
parent 9eaa367d32
commit b593e75c67
14 changed files with 407 additions and 0 deletions

View File

@ -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<Object?> get props => [type, errorMessage];
}

36
lib/core/network/dio.dart Normal file
View File

@ -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;
}
}

View File

@ -0,0 +1,3 @@
class EndPoints {
static const String fetchCommunities = 'projects/{projectUuid}/communities';
}

View File

@ -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,
}

View File

@ -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<String, dynamic>? headers;
final Map<String, dynamic>? queryParams;
Map<String, dynamic>? 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<Map<String, dynamic>> 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<String, dynamic> 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<Object?> get props => [
endPoint,
autherized,
isFormData,
method,
headers,
queryParams,
body,
receiveTimeout,
];
}
Map<String, GenericException> 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",
),
};

View File

@ -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<CommunitiesEvent, CommunitiesState> {
SpaceManagementRemoteSource spaceManagementRemoteSource =
SpaceManagementRemoteSource();
CommunitiesBloc() : super(CommunitiesInitial()) {
on<CommunitiesEvent>((event, emit) {
if (event is FetchCommunitiesEvent) {
_fetchCommunities(emit);
}
});
}
Future<void> _fetchCommunities(Emitter<CommunitiesState> 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()));
}
}
}

View File

@ -0,0 +1,10 @@
part of 'communities_bloc.dart';
sealed class CommunitiesEvent extends Equatable {
const CommunitiesEvent();
@override
List<Object> get props => [];
}
class FetchCommunitiesEvent extends CommunitiesEvent {}

View File

@ -0,0 +1,30 @@
part of 'communities_bloc.dart';
sealed class CommunitiesState extends Equatable {
const CommunitiesState();
@override
List<Object> get props => [];
}
final class CommunitiesInitial extends CommunitiesState {}
final class CommunitiesLoading extends CommunitiesState {}
final class CommunitiesLoaded extends CommunitiesState {
final List<CommunityModel> communities;
const CommunitiesLoaded(this.communities);
@override
List<Object> get props => [communities];
}
final class CommunitiesError extends CommunitiesState {
final String message;
const CommunitiesError(this.message);
@override
List<Object> get props => [message];
}

View File

@ -0,0 +1,21 @@
import 'package:syncrow_web/pages/spaces_management/refactor/data/models/space_model.dart';
class CommunityModel {
String id, name;
List<SpaceModel>? spaces;
CommunityModel({
required this.id,
required this.name,
this.spaces,
});
factory CommunityModel.fromJson(Map<String, dynamic> json) => CommunityModel(
id: json['id'],
name: json['name'],
spaces: SpaceModel.fromJsonList(json['spaces']),
);
static List<CommunityModel> fromJsonList(List<dynamic> jsonList) {
return jsonList.map((json) => CommunityModel.fromJson(json)).toList();
}
}

View File

@ -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<String, dynamic> json) => DeviceModel(
id: json['id'],
name: json['name'],
tag: json['tag'],
location: json['location'],
);
static List<DeviceModel> fromJsonList(List<dynamic> jsonList) {
return jsonList.map((json) => DeviceModel.fromJson(json)).toList();
}
}

View File

@ -0,0 +1,27 @@
import 'device_model.dart';
class SpaceModel {
String id, parentId, name;
List<SpaceModel>? spaces;
List<DeviceModel>? devices;
SpaceModel({
required this.id,
required this.parentId,
required this.name,
this.spaces,
this.devices,
});
factory SpaceModel.fromJson(Map<String, dynamic> json) => SpaceModel(
id: json['id'],
parentId: json['parentId'],
name: json['name'],
spaces: SpaceModel.fromJsonList(json['spaces']),
devices: DeviceModel.fromJsonList(json['devices']),
);
static List<SpaceModel> fromJsonList(List<dynamic> jsonList) {
return jsonList.map((json) => SpaceModel.fromJson(json)).toList();
}
}

View File

@ -0,0 +1,19 @@
import 'device_model.dart';
class SubSpaceModel {
String id, name;
List<DeviceModel>? devices;
SubSpaceModel({
required this.id,
required this.name,
this.devices,
});
factory SubSpaceModel.fromJson(Map<String, dynamic> json) => SubSpaceModel(
id: json['id'],
name: json['name'],
devices: DeviceModel.fromJsonList(json['devices']),
);
static List<SubSpaceModel> fromJsonList(List<dynamic> jsonList) {
return jsonList.map((json) => SubSpaceModel.fromJson(json)).toList();
}
}

View File

@ -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<List<CommunityModel>> 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']));
}
}