diff --git a/lib/pages/space_management_v2/shared/tags/data/services/remote_tags_service.dart b/lib/pages/space_management_v2/shared/tags/data/services/remote_tags_service.dart new file mode 100644 index 00000000..c72cc759 --- /dev/null +++ b/lib/pages/space_management_v2/shared/tags/data/services/remote_tags_service.dart @@ -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> 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) => TagModel.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/shared/tags/domain/models/tag_model.dart b/lib/pages/space_management_v2/shared/tags/domain/models/tag_model.dart new file mode 100644 index 00000000..108aa6b2 --- /dev/null +++ b/lib/pages/space_management_v2/shared/tags/domain/models/tag_model.dart @@ -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 json) { + final internalId = json['internalId'] as String? ?? const Uuid().v4(); + final tag = json['tag'] as Map?; + 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 toJson() { + return { + if (uuid != null) 'uuid': uuid, + 'name': tag, + }; + } + + @override + List get props => [uuid, tag, internalId, location]; +} diff --git a/lib/pages/space_management_v2/shared/tags/domain/params/load_tags_param.dart b/lib/pages/space_management_v2/shared/tags/domain/params/load_tags_param.dart new file mode 100644 index 00000000..00bc341e --- /dev/null +++ b/lib/pages/space_management_v2/shared/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/shared/tags/domain/services/tags_service.dart b/lib/pages/space_management_v2/shared/tags/domain/services/tags_service.dart new file mode 100644 index 00000000..49206e11 --- /dev/null +++ b/lib/pages/space_management_v2/shared/tags/domain/services/tags_service.dart @@ -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> loadTags(LoadTagsParam param); +} diff --git a/lib/pages/space_management_v2/shared/tags/presentation/bloc/tags_bloc.dart b/lib/pages/space_management_v2/shared/tags/presentation/bloc/tags_bloc.dart new file mode 100644 index 00000000..61bb2838 --- /dev/null +++ b/lib/pages/space_management_v2/shared/tags/presentation/bloc/tags_bloc.dart @@ -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 { + 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/shared/tags/presentation/bloc/tags_event.dart b/lib/pages/space_management_v2/shared/tags/presentation/bloc/tags_event.dart new file mode 100644 index 00000000..99134cab --- /dev/null +++ b/lib/pages/space_management_v2/shared/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/shared/tags/presentation/bloc/tags_state.dart b/lib/pages/space_management_v2/shared/tags/presentation/bloc/tags_state.dart new file mode 100644 index 00000000..d6a13c6d --- /dev/null +++ b/lib/pages/space_management_v2/shared/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]; +}