Implemented tags bloc, services, models, and params

This commit is contained in:
Faris Armoush
2025-06-16 15:27:50 +03:00
parent aa141ef54d
commit ac44af54a3
7 changed files with 196 additions and 0 deletions

View File

@ -0,0 +1,51 @@
import 'package:dio/dio.dart';
import 'package:syncrow_web/pages/space_management_v2/shared/tags/domain/models/tag_model.dart';
import 'package:syncrow_web/pages/space_management_v2/shared/tags/domain/params/load_tags_param.dart';
import 'package:syncrow_web/pages/space_management_v2/shared/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<List<TagModel>> 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<String, dynamic>;
final data = result['data'] as List<dynamic>;
return data
.map((e) => TagModel.fromJson(e as Map<String, dynamic>))
.toList();
},
);
return response;
} on DioException catch (e) {
final message = e.response?.data as Map<String, dynamic>?;
final error = message?['error'] as Map<String, dynamic>?;
final errorMessage = error?['error'] as String? ?? '';
final formattedErrorMessage = [
_defaultErrorMessage,
errorMessage,
].join(': ');
throw APIException(formattedErrorMessage);
} catch (e) {
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
throw APIException(formattedErrorMessage);
}
}
}

View File

@ -0,0 +1,53 @@
import 'package:equatable/equatable.dart';
import 'package:uuid/uuid.dart';
class TagModel extends Equatable {
const TagModel({
required this.uuid,
required this.tag,
required this.internalId,
required this.location,
});
final String? uuid;
final String? tag;
final String? internalId;
final String? location;
factory TagModel.fromJson(Map<String, dynamic> json) {
final internalId = json['internalId'] as String? ?? const Uuid().v4();
final tag = json['tag'] as Map<String, dynamic>?;
final name = json['name'] as String?;
return TagModel(
uuid: name != null ? json['uuid'] as String? : tag?['uuid'] as String?,
internalId: internalId,
tag: name ?? tag?['name'] as String?,
location: json['location'] as String?,
);
}
TagModel copyWith({
String? uuid,
String? tag,
String? location,
String? internalId,
}) {
return TagModel(
uuid: uuid ?? this.uuid,
tag: tag ?? this.tag,
location: location ?? this.location,
internalId: internalId ?? this.internalId,
);
}
Map<String, dynamic> toJson() {
return {
if (uuid != null) 'uuid': uuid,
'name': tag,
};
}
@override
List<Object?> get props => [uuid, tag, internalId, location];
}

View File

@ -0,0 +1,5 @@
class LoadTagsParam {
final String? projectUuid;
const LoadTagsParam({this.projectUuid});
}

View File

@ -0,0 +1,6 @@
import 'package:syncrow_web/pages/space_management_v2/shared/tags/domain/models/tag_model.dart';
import 'package:syncrow_web/pages/space_management_v2/shared/tags/domain/params/load_tags_param.dart';
abstract interface class TagsService {
Future<List<TagModel>> loadTags(LoadTagsParam param);
}

View File

@ -0,0 +1,34 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/space_management_v2/shared/tags/domain/models/tag_model.dart';
import 'package:syncrow_web/pages/space_management_v2/shared/tags/domain/params/load_tags_param.dart';
import 'package:syncrow_web/pages/space_management_v2/shared/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<TagsEvent, TagsState> {
final TagsService _tagsService;
TagsBloc(this._tagsService) : super(TagsInitial()) {
on<LoadTags>(_onLoadTags);
}
Future<void> _onLoadTags(
LoadTags event,
Emitter<TagsState> 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()));
}
}
}

View File

@ -0,0 +1,17 @@
part of 'tags_bloc.dart';
abstract class TagsEvent extends Equatable {
const TagsEvent();
@override
List<Object?> get props => [];
}
class LoadTags extends TagsEvent {
final LoadTagsParam param;
const LoadTags(this.param);
@override
List<Object?> get props => [param];
}

View File

@ -0,0 +1,30 @@
part of 'tags_bloc.dart';
abstract class TagsState extends Equatable {
const TagsState();
@override
List<Object?> get props => [];
}
class TagsInitial extends TagsState {}
class TagsLoading extends TagsState {}
class TagsLoaded extends TagsState {
final List<TagModel> tags;
const TagsLoaded(this.tags);
@override
List<Object?> get props => [tags];
}
class TagsFailure extends TagsState {
final String message;
const TagsFailure(this.message);
@override
List<Object?> get props => [message];
}