From 4a4f6388d7a55c9f3a655b3878278f27c34380f3 Mon Sep 17 00:00:00 2001 From: efrilm Date: Sun, 17 Aug 2025 12:50:10 +0700 Subject: [PATCH] feat: category --- .../category_loader/category_loader_bloc.dart | 55 + .../category_loader_bloc.freezed.dart | 370 +++++++ .../category_loader_event.dart | 6 + .../category_loader_state.dart | 13 + lib/common/url/api_path.dart | 3 + lib/domain/category/category.dart | 10 + lib/domain/category/category.freezed.dart | 952 ++++++++++++++++++ .../category/entities/category_entity.dart | 33 + .../category/failures/category_failure.dart | 10 + .../repositories/i_auth_repository.dart | 9 + .../category/category_dtos.dart | 8 + .../category/category_dtos.freezed.dart | 368 +++++++ .../category/category_dtos.g.dart | 35 + .../datasource/remote_data_provider.dart | 44 + .../category/dto/category_dto.dart | 31 + .../repositories/category_repository.dart | 41 + lib/injection.config.dart | 16 + .../pages/product/product_page.dart | 45 +- .../product/widgets/category_delegate.dart | 12 +- 19 files changed, 2041 insertions(+), 20 deletions(-) create mode 100644 lib/application/category/category_loader/category_loader_bloc.dart create mode 100644 lib/application/category/category_loader/category_loader_bloc.freezed.dart create mode 100644 lib/application/category/category_loader/category_loader_event.dart create mode 100644 lib/application/category/category_loader/category_loader_state.dart create mode 100644 lib/domain/category/category.dart create mode 100644 lib/domain/category/category.freezed.dart create mode 100644 lib/domain/category/entities/category_entity.dart create mode 100644 lib/domain/category/failures/category_failure.dart create mode 100644 lib/domain/category/repositories/i_auth_repository.dart create mode 100644 lib/infrastructure/category/category_dtos.dart create mode 100644 lib/infrastructure/category/category_dtos.freezed.dart create mode 100644 lib/infrastructure/category/category_dtos.g.dart create mode 100644 lib/infrastructure/category/datasource/remote_data_provider.dart create mode 100644 lib/infrastructure/category/dto/category_dto.dart create mode 100644 lib/infrastructure/category/repositories/category_repository.dart diff --git a/lib/application/category/category_loader/category_loader_bloc.dart b/lib/application/category/category_loader/category_loader_bloc.dart new file mode 100644 index 0000000..884f36c --- /dev/null +++ b/lib/application/category/category_loader/category_loader_bloc.dart @@ -0,0 +1,55 @@ +import 'package:dartz/dartz.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:injectable/injectable.dart'; + +import '../../../domain/category/category.dart'; + +part 'category_loader_event.dart'; +part 'category_loader_state.dart'; +part 'category_loader_bloc.freezed.dart'; + +@injectable +class CategoryLoaderBloc + extends Bloc { + final ICategoryRepository _repository; + CategoryLoaderBloc(this._repository) : super(CategoryLoaderState.initial()) { + on(_onCategoryLoaderEvent); + } + + Future _onCategoryLoaderEvent( + CategoryLoaderEvent event, + Emitter emit, + ) { + return event.map( + fetched: (e) async { + emit(state.copyWith(isFetching: true, failureOptionCategory: none())); + + final result = await _repository.get(); + + result.fold( + (f) { + emit( + state.copyWith( + isFetching: false, + failureOptionCategory: optionOf(f), + ), + ); + }, + (categories) { + // tambahkan "All Data" di awal list + final updatedCategories = [Category.addAllData(), ...categories]; + + emit( + state.copyWith( + isFetching: false, + categories: updatedCategories, + failureOptionCategory: none(), + ), + ); + }, + ); + }, + ); + } +} diff --git a/lib/application/category/category_loader/category_loader_bloc.freezed.dart b/lib/application/category/category_loader/category_loader_bloc.freezed.dart new file mode 100644 index 0000000..5599123 --- /dev/null +++ b/lib/application/category/category_loader/category_loader_bloc.freezed.dart @@ -0,0 +1,370 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'category_loader_bloc.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models', +); + +/// @nodoc +mixin _$CategoryLoaderEvent { + @optionalTypeArgs + TResult when({ + required TResult Function() fetched, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? fetched, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? fetched, + required TResult orElse(), + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(_Fetched value) fetched, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Fetched value)? fetched, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Fetched value)? fetched, + required TResult orElse(), + }) => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $CategoryLoaderEventCopyWith<$Res> { + factory $CategoryLoaderEventCopyWith( + CategoryLoaderEvent value, + $Res Function(CategoryLoaderEvent) then, + ) = _$CategoryLoaderEventCopyWithImpl<$Res, CategoryLoaderEvent>; +} + +/// @nodoc +class _$CategoryLoaderEventCopyWithImpl<$Res, $Val extends CategoryLoaderEvent> + implements $CategoryLoaderEventCopyWith<$Res> { + _$CategoryLoaderEventCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of CategoryLoaderEvent + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$FetchedImplCopyWith<$Res> { + factory _$$FetchedImplCopyWith( + _$FetchedImpl value, + $Res Function(_$FetchedImpl) then, + ) = __$$FetchedImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$FetchedImplCopyWithImpl<$Res> + extends _$CategoryLoaderEventCopyWithImpl<$Res, _$FetchedImpl> + implements _$$FetchedImplCopyWith<$Res> { + __$$FetchedImplCopyWithImpl( + _$FetchedImpl _value, + $Res Function(_$FetchedImpl) _then, + ) : super(_value, _then); + + /// Create a copy of CategoryLoaderEvent + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$FetchedImpl implements _Fetched { + const _$FetchedImpl(); + + @override + String toString() { + return 'CategoryLoaderEvent.fetched()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$FetchedImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({required TResult Function() fetched}) { + return fetched(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({TResult? Function()? fetched}) { + return fetched?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? fetched, + required TResult orElse(), + }) { + if (fetched != null) { + return fetched(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Fetched value) fetched, + }) { + return fetched(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Fetched value)? fetched, + }) { + return fetched?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Fetched value)? fetched, + required TResult orElse(), + }) { + if (fetched != null) { + return fetched(this); + } + return orElse(); + } +} + +abstract class _Fetched implements CategoryLoaderEvent { + const factory _Fetched() = _$FetchedImpl; +} + +/// @nodoc +mixin _$CategoryLoaderState { + List get categories => throw _privateConstructorUsedError; + Option get failureOptionCategory => + throw _privateConstructorUsedError; + bool get isFetching => throw _privateConstructorUsedError; + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $CategoryLoaderStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $CategoryLoaderStateCopyWith<$Res> { + factory $CategoryLoaderStateCopyWith( + CategoryLoaderState value, + $Res Function(CategoryLoaderState) then, + ) = _$CategoryLoaderStateCopyWithImpl<$Res, CategoryLoaderState>; + @useResult + $Res call({ + List categories, + Option failureOptionCategory, + bool isFetching, + }); +} + +/// @nodoc +class _$CategoryLoaderStateCopyWithImpl<$Res, $Val extends CategoryLoaderState> + implements $CategoryLoaderStateCopyWith<$Res> { + _$CategoryLoaderStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? categories = null, + Object? failureOptionCategory = null, + Object? isFetching = null, + }) { + return _then( + _value.copyWith( + categories: null == categories + ? _value.categories + : categories // ignore: cast_nullable_to_non_nullable + as List, + failureOptionCategory: null == failureOptionCategory + ? _value.failureOptionCategory + : failureOptionCategory // ignore: cast_nullable_to_non_nullable + as Option, + isFetching: null == isFetching + ? _value.isFetching + : isFetching // ignore: cast_nullable_to_non_nullable + as bool, + ) + as $Val, + ); + } +} + +/// @nodoc +abstract class _$$CategoryLoaderStateImplCopyWith<$Res> + implements $CategoryLoaderStateCopyWith<$Res> { + factory _$$CategoryLoaderStateImplCopyWith( + _$CategoryLoaderStateImpl value, + $Res Function(_$CategoryLoaderStateImpl) then, + ) = __$$CategoryLoaderStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({ + List categories, + Option failureOptionCategory, + bool isFetching, + }); +} + +/// @nodoc +class __$$CategoryLoaderStateImplCopyWithImpl<$Res> + extends _$CategoryLoaderStateCopyWithImpl<$Res, _$CategoryLoaderStateImpl> + implements _$$CategoryLoaderStateImplCopyWith<$Res> { + __$$CategoryLoaderStateImplCopyWithImpl( + _$CategoryLoaderStateImpl _value, + $Res Function(_$CategoryLoaderStateImpl) _then, + ) : super(_value, _then); + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? categories = null, + Object? failureOptionCategory = null, + Object? isFetching = null, + }) { + return _then( + _$CategoryLoaderStateImpl( + categories: null == categories + ? _value._categories + : categories // ignore: cast_nullable_to_non_nullable + as List, + failureOptionCategory: null == failureOptionCategory + ? _value.failureOptionCategory + : failureOptionCategory // ignore: cast_nullable_to_non_nullable + as Option, + isFetching: null == isFetching + ? _value.isFetching + : isFetching // ignore: cast_nullable_to_non_nullable + as bool, + ), + ); + } +} + +/// @nodoc + +class _$CategoryLoaderStateImpl implements _CategoryLoaderState { + const _$CategoryLoaderStateImpl({ + required final List categories, + required this.failureOptionCategory, + this.isFetching = false, + }) : _categories = categories; + + final List _categories; + @override + List get categories { + if (_categories is EqualUnmodifiableListView) return _categories; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_categories); + } + + @override + final Option failureOptionCategory; + @override + @JsonKey() + final bool isFetching; + + @override + String toString() { + return 'CategoryLoaderState(categories: $categories, failureOptionCategory: $failureOptionCategory, isFetching: $isFetching)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$CategoryLoaderStateImpl && + const DeepCollectionEquality().equals( + other._categories, + _categories, + ) && + (identical(other.failureOptionCategory, failureOptionCategory) || + other.failureOptionCategory == failureOptionCategory) && + (identical(other.isFetching, isFetching) || + other.isFetching == isFetching)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(_categories), + failureOptionCategory, + isFetching, + ); + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$CategoryLoaderStateImplCopyWith<_$CategoryLoaderStateImpl> get copyWith => + __$$CategoryLoaderStateImplCopyWithImpl<_$CategoryLoaderStateImpl>( + this, + _$identity, + ); +} + +abstract class _CategoryLoaderState implements CategoryLoaderState { + const factory _CategoryLoaderState({ + required final List categories, + required final Option failureOptionCategory, + final bool isFetching, + }) = _$CategoryLoaderStateImpl; + + @override + List get categories; + @override + Option get failureOptionCategory; + @override + bool get isFetching; + + /// Create a copy of CategoryLoaderState + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$CategoryLoaderStateImplCopyWith<_$CategoryLoaderStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/application/category/category_loader/category_loader_event.dart b/lib/application/category/category_loader/category_loader_event.dart new file mode 100644 index 0000000..2c154e1 --- /dev/null +++ b/lib/application/category/category_loader/category_loader_event.dart @@ -0,0 +1,6 @@ +part of 'category_loader_bloc.dart'; + +@freezed +class CategoryLoaderEvent with _$CategoryLoaderEvent { + const factory CategoryLoaderEvent.fetched() = _Fetched; +} diff --git a/lib/application/category/category_loader/category_loader_state.dart b/lib/application/category/category_loader/category_loader_state.dart new file mode 100644 index 0000000..e3295ea --- /dev/null +++ b/lib/application/category/category_loader/category_loader_state.dart @@ -0,0 +1,13 @@ +part of 'category_loader_bloc.dart'; + +@freezed +class CategoryLoaderState with _$CategoryLoaderState { + const factory CategoryLoaderState({ + required List categories, + required Option failureOptionCategory, + @Default(false) bool isFetching, + }) = _CategoryLoaderState; + + factory CategoryLoaderState.initial() => + CategoryLoaderState(categories: [], failureOptionCategory: none()); +} diff --git a/lib/common/url/api_path.dart b/lib/common/url/api_path.dart index 114fcb1..17064a4 100644 --- a/lib/common/url/api_path.dart +++ b/lib/common/url/api_path.dart @@ -5,4 +5,7 @@ class ApiPath { // Analytic static const String salesAnalytic = '/api/v1/analytics/sales'; + + // Category + static const String category = '/api/v1/categories'; } diff --git a/lib/domain/category/category.dart b/lib/domain/category/category.dart new file mode 100644 index 0000000..0e50ce0 --- /dev/null +++ b/lib/domain/category/category.dart @@ -0,0 +1,10 @@ +import 'package:dartz/dartz.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +import '../../common/api/api_failure.dart'; + +part 'category.freezed.dart'; + +part 'entities/category_entity.dart'; +part 'failures/category_failure.dart'; +part 'repositories/i_auth_repository.dart'; diff --git a/lib/domain/category/category.freezed.dart b/lib/domain/category/category.freezed.dart new file mode 100644 index 0000000..204a55c --- /dev/null +++ b/lib/domain/category/category.freezed.dart @@ -0,0 +1,952 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'category.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models', +); + +/// @nodoc +mixin _$Category { + String get id => throw _privateConstructorUsedError; + String get organizationId => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + String get description => throw _privateConstructorUsedError; + String get businessType => throw _privateConstructorUsedError; + Map get metadata => throw _privateConstructorUsedError; + DateTime? get createdAt => throw _privateConstructorUsedError; + DateTime? get updatedAt => throw _privateConstructorUsedError; + + /// Create a copy of Category + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $CategoryCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $CategoryCopyWith<$Res> { + factory $CategoryCopyWith(Category value, $Res Function(Category) then) = + _$CategoryCopyWithImpl<$Res, Category>; + @useResult + $Res call({ + String id, + String organizationId, + String name, + String description, + String businessType, + Map metadata, + DateTime? createdAt, + DateTime? updatedAt, + }); +} + +/// @nodoc +class _$CategoryCopyWithImpl<$Res, $Val extends Category> + implements $CategoryCopyWith<$Res> { + _$CategoryCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of Category + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? organizationId = null, + Object? name = null, + Object? description = null, + Object? businessType = null, + Object? metadata = null, + Object? createdAt = freezed, + Object? updatedAt = freezed, + }) { + return _then( + _value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + organizationId: null == organizationId + ? _value.organizationId + : organizationId // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + description: null == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String, + businessType: null == businessType + ? _value.businessType + : businessType // ignore: cast_nullable_to_non_nullable + as String, + metadata: null == metadata + ? _value.metadata + : metadata // ignore: cast_nullable_to_non_nullable + as Map, + createdAt: freezed == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + updatedAt: freezed == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + ) + as $Val, + ); + } +} + +/// @nodoc +abstract class _$$CategoryImplCopyWith<$Res> + implements $CategoryCopyWith<$Res> { + factory _$$CategoryImplCopyWith( + _$CategoryImpl value, + $Res Function(_$CategoryImpl) then, + ) = __$$CategoryImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({ + String id, + String organizationId, + String name, + String description, + String businessType, + Map metadata, + DateTime? createdAt, + DateTime? updatedAt, + }); +} + +/// @nodoc +class __$$CategoryImplCopyWithImpl<$Res> + extends _$CategoryCopyWithImpl<$Res, _$CategoryImpl> + implements _$$CategoryImplCopyWith<$Res> { + __$$CategoryImplCopyWithImpl( + _$CategoryImpl _value, + $Res Function(_$CategoryImpl) _then, + ) : super(_value, _then); + + /// Create a copy of Category + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? organizationId = null, + Object? name = null, + Object? description = null, + Object? businessType = null, + Object? metadata = null, + Object? createdAt = freezed, + Object? updatedAt = freezed, + }) { + return _then( + _$CategoryImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + organizationId: null == organizationId + ? _value.organizationId + : organizationId // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + description: null == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String, + businessType: null == businessType + ? _value.businessType + : businessType // ignore: cast_nullable_to_non_nullable + as String, + metadata: null == metadata + ? _value._metadata + : metadata // ignore: cast_nullable_to_non_nullable + as Map, + createdAt: freezed == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + updatedAt: freezed == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + ), + ); + } +} + +/// @nodoc + +class _$CategoryImpl implements _Category { + const _$CategoryImpl({ + required this.id, + required this.organizationId, + required this.name, + required this.description, + required this.businessType, + required final Map metadata, + this.createdAt, + this.updatedAt, + }) : _metadata = metadata; + + @override + final String id; + @override + final String organizationId; + @override + final String name; + @override + final String description; + @override + final String businessType; + final Map _metadata; + @override + Map get metadata { + if (_metadata is EqualUnmodifiableMapView) return _metadata; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_metadata); + } + + @override + final DateTime? createdAt; + @override + final DateTime? updatedAt; + + @override + String toString() { + return 'Category(id: $id, organizationId: $organizationId, name: $name, description: $description, businessType: $businessType, metadata: $metadata, createdAt: $createdAt, updatedAt: $updatedAt)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$CategoryImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.organizationId, organizationId) || + other.organizationId == organizationId) && + (identical(other.name, name) || other.name == name) && + (identical(other.description, description) || + other.description == description) && + (identical(other.businessType, businessType) || + other.businessType == businessType) && + const DeepCollectionEquality().equals(other._metadata, _metadata) && + (identical(other.createdAt, createdAt) || + other.createdAt == createdAt) && + (identical(other.updatedAt, updatedAt) || + other.updatedAt == updatedAt)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + id, + organizationId, + name, + description, + businessType, + const DeepCollectionEquality().hash(_metadata), + createdAt, + updatedAt, + ); + + /// Create a copy of Category + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$CategoryImplCopyWith<_$CategoryImpl> get copyWith => + __$$CategoryImplCopyWithImpl<_$CategoryImpl>(this, _$identity); +} + +abstract class _Category implements Category { + const factory _Category({ + required final String id, + required final String organizationId, + required final String name, + required final String description, + required final String businessType, + required final Map metadata, + final DateTime? createdAt, + final DateTime? updatedAt, + }) = _$CategoryImpl; + + @override + String get id; + @override + String get organizationId; + @override + String get name; + @override + String get description; + @override + String get businessType; + @override + Map get metadata; + @override + DateTime? get createdAt; + @override + DateTime? get updatedAt; + + /// Create a copy of Category + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$CategoryImplCopyWith<_$CategoryImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +mixin _$CategoryFailure { + @optionalTypeArgs + TResult when({ + required TResult Function(ApiFailure failure) serverError, + required TResult Function() unexpectedError, + required TResult Function() empty, + required TResult Function(String erroMessage) dynamicErrorMessage, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(ApiFailure failure)? serverError, + TResult? Function()? unexpectedError, + TResult? Function()? empty, + TResult? Function(String erroMessage)? dynamicErrorMessage, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(ApiFailure failure)? serverError, + TResult Function()? unexpectedError, + TResult Function()? empty, + TResult Function(String erroMessage)? dynamicErrorMessage, + required TResult orElse(), + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(_ServerError value) serverError, + required TResult Function(_UnexpectedError value) unexpectedError, + required TResult Function(_Empty value) empty, + required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_ServerError value)? serverError, + TResult? Function(_UnexpectedError value)? unexpectedError, + TResult? Function(_Empty value)? empty, + TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage, + }) => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_ServerError value)? serverError, + TResult Function(_UnexpectedError value)? unexpectedError, + TResult Function(_Empty value)? empty, + TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage, + required TResult orElse(), + }) => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $CategoryFailureCopyWith<$Res> { + factory $CategoryFailureCopyWith( + CategoryFailure value, + $Res Function(CategoryFailure) then, + ) = _$CategoryFailureCopyWithImpl<$Res, CategoryFailure>; +} + +/// @nodoc +class _$CategoryFailureCopyWithImpl<$Res, $Val extends CategoryFailure> + implements $CategoryFailureCopyWith<$Res> { + _$CategoryFailureCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of CategoryFailure + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$ServerErrorImplCopyWith<$Res> { + factory _$$ServerErrorImplCopyWith( + _$ServerErrorImpl value, + $Res Function(_$ServerErrorImpl) then, + ) = __$$ServerErrorImplCopyWithImpl<$Res>; + @useResult + $Res call({ApiFailure failure}); + + $ApiFailureCopyWith<$Res> get failure; +} + +/// @nodoc +class __$$ServerErrorImplCopyWithImpl<$Res> + extends _$CategoryFailureCopyWithImpl<$Res, _$ServerErrorImpl> + implements _$$ServerErrorImplCopyWith<$Res> { + __$$ServerErrorImplCopyWithImpl( + _$ServerErrorImpl _value, + $Res Function(_$ServerErrorImpl) _then, + ) : super(_value, _then); + + /// Create a copy of CategoryFailure + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? failure = null}) { + return _then( + _$ServerErrorImpl( + null == failure + ? _value.failure + : failure // ignore: cast_nullable_to_non_nullable + as ApiFailure, + ), + ); + } + + /// Create a copy of CategoryFailure + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $ApiFailureCopyWith<$Res> get failure { + return $ApiFailureCopyWith<$Res>(_value.failure, (value) { + return _then(_value.copyWith(failure: value)); + }); + } +} + +/// @nodoc + +class _$ServerErrorImpl implements _ServerError { + const _$ServerErrorImpl(this.failure); + + @override + final ApiFailure failure; + + @override + String toString() { + return 'CategoryFailure.serverError(failure: $failure)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ServerErrorImpl && + (identical(other.failure, failure) || other.failure == failure)); + } + + @override + int get hashCode => Object.hash(runtimeType, failure); + + /// Create a copy of CategoryFailure + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ServerErrorImplCopyWith<_$ServerErrorImpl> get copyWith => + __$$ServerErrorImplCopyWithImpl<_$ServerErrorImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(ApiFailure failure) serverError, + required TResult Function() unexpectedError, + required TResult Function() empty, + required TResult Function(String erroMessage) dynamicErrorMessage, + }) { + return serverError(failure); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(ApiFailure failure)? serverError, + TResult? Function()? unexpectedError, + TResult? Function()? empty, + TResult? Function(String erroMessage)? dynamicErrorMessage, + }) { + return serverError?.call(failure); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(ApiFailure failure)? serverError, + TResult Function()? unexpectedError, + TResult Function()? empty, + TResult Function(String erroMessage)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (serverError != null) { + return serverError(failure); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_ServerError value) serverError, + required TResult Function(_UnexpectedError value) unexpectedError, + required TResult Function(_Empty value) empty, + required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage, + }) { + return serverError(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_ServerError value)? serverError, + TResult? Function(_UnexpectedError value)? unexpectedError, + TResult? Function(_Empty value)? empty, + TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage, + }) { + return serverError?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_ServerError value)? serverError, + TResult Function(_UnexpectedError value)? unexpectedError, + TResult Function(_Empty value)? empty, + TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (serverError != null) { + return serverError(this); + } + return orElse(); + } +} + +abstract class _ServerError implements CategoryFailure { + const factory _ServerError(final ApiFailure failure) = _$ServerErrorImpl; + + ApiFailure get failure; + + /// Create a copy of CategoryFailure + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ServerErrorImplCopyWith<_$ServerErrorImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$UnexpectedErrorImplCopyWith<$Res> { + factory _$$UnexpectedErrorImplCopyWith( + _$UnexpectedErrorImpl value, + $Res Function(_$UnexpectedErrorImpl) then, + ) = __$$UnexpectedErrorImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$UnexpectedErrorImplCopyWithImpl<$Res> + extends _$CategoryFailureCopyWithImpl<$Res, _$UnexpectedErrorImpl> + implements _$$UnexpectedErrorImplCopyWith<$Res> { + __$$UnexpectedErrorImplCopyWithImpl( + _$UnexpectedErrorImpl _value, + $Res Function(_$UnexpectedErrorImpl) _then, + ) : super(_value, _then); + + /// Create a copy of CategoryFailure + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$UnexpectedErrorImpl implements _UnexpectedError { + const _$UnexpectedErrorImpl(); + + @override + String toString() { + return 'CategoryFailure.unexpectedError()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$UnexpectedErrorImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(ApiFailure failure) serverError, + required TResult Function() unexpectedError, + required TResult Function() empty, + required TResult Function(String erroMessage) dynamicErrorMessage, + }) { + return unexpectedError(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(ApiFailure failure)? serverError, + TResult? Function()? unexpectedError, + TResult? Function()? empty, + TResult? Function(String erroMessage)? dynamicErrorMessage, + }) { + return unexpectedError?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(ApiFailure failure)? serverError, + TResult Function()? unexpectedError, + TResult Function()? empty, + TResult Function(String erroMessage)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (unexpectedError != null) { + return unexpectedError(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_ServerError value) serverError, + required TResult Function(_UnexpectedError value) unexpectedError, + required TResult Function(_Empty value) empty, + required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage, + }) { + return unexpectedError(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_ServerError value)? serverError, + TResult? Function(_UnexpectedError value)? unexpectedError, + TResult? Function(_Empty value)? empty, + TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage, + }) { + return unexpectedError?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_ServerError value)? serverError, + TResult Function(_UnexpectedError value)? unexpectedError, + TResult Function(_Empty value)? empty, + TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (unexpectedError != null) { + return unexpectedError(this); + } + return orElse(); + } +} + +abstract class _UnexpectedError implements CategoryFailure { + const factory _UnexpectedError() = _$UnexpectedErrorImpl; +} + +/// @nodoc +abstract class _$$EmptyImplCopyWith<$Res> { + factory _$$EmptyImplCopyWith( + _$EmptyImpl value, + $Res Function(_$EmptyImpl) then, + ) = __$$EmptyImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$EmptyImplCopyWithImpl<$Res> + extends _$CategoryFailureCopyWithImpl<$Res, _$EmptyImpl> + implements _$$EmptyImplCopyWith<$Res> { + __$$EmptyImplCopyWithImpl( + _$EmptyImpl _value, + $Res Function(_$EmptyImpl) _then, + ) : super(_value, _then); + + /// Create a copy of CategoryFailure + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$EmptyImpl implements _Empty { + const _$EmptyImpl(); + + @override + String toString() { + return 'CategoryFailure.empty()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$EmptyImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(ApiFailure failure) serverError, + required TResult Function() unexpectedError, + required TResult Function() empty, + required TResult Function(String erroMessage) dynamicErrorMessage, + }) { + return empty(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(ApiFailure failure)? serverError, + TResult? Function()? unexpectedError, + TResult? Function()? empty, + TResult? Function(String erroMessage)? dynamicErrorMessage, + }) { + return empty?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(ApiFailure failure)? serverError, + TResult Function()? unexpectedError, + TResult Function()? empty, + TResult Function(String erroMessage)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (empty != null) { + return empty(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_ServerError value) serverError, + required TResult Function(_UnexpectedError value) unexpectedError, + required TResult Function(_Empty value) empty, + required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage, + }) { + return empty(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_ServerError value)? serverError, + TResult? Function(_UnexpectedError value)? unexpectedError, + TResult? Function(_Empty value)? empty, + TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage, + }) { + return empty?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_ServerError value)? serverError, + TResult Function(_UnexpectedError value)? unexpectedError, + TResult Function(_Empty value)? empty, + TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (empty != null) { + return empty(this); + } + return orElse(); + } +} + +abstract class _Empty implements CategoryFailure { + const factory _Empty() = _$EmptyImpl; +} + +/// @nodoc +abstract class _$$DynamicErrorMessageImplCopyWith<$Res> { + factory _$$DynamicErrorMessageImplCopyWith( + _$DynamicErrorMessageImpl value, + $Res Function(_$DynamicErrorMessageImpl) then, + ) = __$$DynamicErrorMessageImplCopyWithImpl<$Res>; + @useResult + $Res call({String erroMessage}); +} + +/// @nodoc +class __$$DynamicErrorMessageImplCopyWithImpl<$Res> + extends _$CategoryFailureCopyWithImpl<$Res, _$DynamicErrorMessageImpl> + implements _$$DynamicErrorMessageImplCopyWith<$Res> { + __$$DynamicErrorMessageImplCopyWithImpl( + _$DynamicErrorMessageImpl _value, + $Res Function(_$DynamicErrorMessageImpl) _then, + ) : super(_value, _then); + + /// Create a copy of CategoryFailure + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? erroMessage = null}) { + return _then( + _$DynamicErrorMessageImpl( + null == erroMessage + ? _value.erroMessage + : erroMessage // ignore: cast_nullable_to_non_nullable + as String, + ), + ); + } +} + +/// @nodoc + +class _$DynamicErrorMessageImpl implements _DynamicErrorMessage { + const _$DynamicErrorMessageImpl(this.erroMessage); + + @override + final String erroMessage; + + @override + String toString() { + return 'CategoryFailure.dynamicErrorMessage(erroMessage: $erroMessage)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$DynamicErrorMessageImpl && + (identical(other.erroMessage, erroMessage) || + other.erroMessage == erroMessage)); + } + + @override + int get hashCode => Object.hash(runtimeType, erroMessage); + + /// Create a copy of CategoryFailure + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$DynamicErrorMessageImplCopyWith<_$DynamicErrorMessageImpl> get copyWith => + __$$DynamicErrorMessageImplCopyWithImpl<_$DynamicErrorMessageImpl>( + this, + _$identity, + ); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(ApiFailure failure) serverError, + required TResult Function() unexpectedError, + required TResult Function() empty, + required TResult Function(String erroMessage) dynamicErrorMessage, + }) { + return dynamicErrorMessage(erroMessage); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(ApiFailure failure)? serverError, + TResult? Function()? unexpectedError, + TResult? Function()? empty, + TResult? Function(String erroMessage)? dynamicErrorMessage, + }) { + return dynamicErrorMessage?.call(erroMessage); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(ApiFailure failure)? serverError, + TResult Function()? unexpectedError, + TResult Function()? empty, + TResult Function(String erroMessage)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (dynamicErrorMessage != null) { + return dynamicErrorMessage(erroMessage); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_ServerError value) serverError, + required TResult Function(_UnexpectedError value) unexpectedError, + required TResult Function(_Empty value) empty, + required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage, + }) { + return dynamicErrorMessage(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_ServerError value)? serverError, + TResult? Function(_UnexpectedError value)? unexpectedError, + TResult? Function(_Empty value)? empty, + TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage, + }) { + return dynamicErrorMessage?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_ServerError value)? serverError, + TResult Function(_UnexpectedError value)? unexpectedError, + TResult Function(_Empty value)? empty, + TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage, + required TResult orElse(), + }) { + if (dynamicErrorMessage != null) { + return dynamicErrorMessage(this); + } + return orElse(); + } +} + +abstract class _DynamicErrorMessage implements CategoryFailure { + const factory _DynamicErrorMessage(final String erroMessage) = + _$DynamicErrorMessageImpl; + + String get erroMessage; + + /// Create a copy of CategoryFailure + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$DynamicErrorMessageImplCopyWith<_$DynamicErrorMessageImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/domain/category/entities/category_entity.dart b/lib/domain/category/entities/category_entity.dart new file mode 100644 index 0000000..a6d351a --- /dev/null +++ b/lib/domain/category/entities/category_entity.dart @@ -0,0 +1,33 @@ +part of '../category.dart'; + +@freezed +class Category with _$Category { + const factory Category({ + required String id, + required String organizationId, + required String name, + required String description, + required String businessType, + required Map metadata, + DateTime? createdAt, + DateTime? updatedAt, + }) = _Category; + + factory Category.empty() => const Category( + id: '', + organizationId: '', + name: '', + description: '', + businessType: '', + metadata: {}, + ); + + factory Category.addAllData() => Category( + id: 'all', + organizationId: '', + name: 'Semua', + description: '', + businessType: '', + metadata: {}, + ); +} diff --git a/lib/domain/category/failures/category_failure.dart b/lib/domain/category/failures/category_failure.dart new file mode 100644 index 0000000..0df9feb --- /dev/null +++ b/lib/domain/category/failures/category_failure.dart @@ -0,0 +1,10 @@ +part of '../category.dart'; + +@freezed +sealed class CategoryFailure with _$CategoryFailure { + const factory CategoryFailure.serverError(ApiFailure failure) = _ServerError; + const factory CategoryFailure.unexpectedError() = _UnexpectedError; + const factory CategoryFailure.empty() = _Empty; + const factory CategoryFailure.dynamicErrorMessage(String erroMessage) = + _DynamicErrorMessage; +} diff --git a/lib/domain/category/repositories/i_auth_repository.dart b/lib/domain/category/repositories/i_auth_repository.dart new file mode 100644 index 0000000..c41a494 --- /dev/null +++ b/lib/domain/category/repositories/i_auth_repository.dart @@ -0,0 +1,9 @@ +part of '../category.dart'; + +abstract class ICategoryRepository { + Future>> get({ + int page = 1, + int limit = 20, + bool isActive = true, + }); +} diff --git a/lib/infrastructure/category/category_dtos.dart b/lib/infrastructure/category/category_dtos.dart new file mode 100644 index 0000000..f07cffc --- /dev/null +++ b/lib/infrastructure/category/category_dtos.dart @@ -0,0 +1,8 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +import '../../domain/category/category.dart'; + +part 'category_dtos.freezed.dart'; +part 'category_dtos.g.dart'; + +part 'dto/category_dto.dart'; diff --git a/lib/infrastructure/category/category_dtos.freezed.dart b/lib/infrastructure/category/category_dtos.freezed.dart new file mode 100644 index 0000000..fb9448a --- /dev/null +++ b/lib/infrastructure/category/category_dtos.freezed.dart @@ -0,0 +1,368 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'category_dtos.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models', +); + +CategoryDto _$CategoryDtoFromJson(Map json) { + return _CategoryDto.fromJson(json); +} + +/// @nodoc +mixin _$CategoryDto { + @JsonKey(name: 'id') + String? get id => throw _privateConstructorUsedError; + @JsonKey(name: 'organization_id') + String? get organizationId => throw _privateConstructorUsedError; + @JsonKey(name: 'name') + String? get name => throw _privateConstructorUsedError; + @JsonKey(name: 'description') + String? get description => throw _privateConstructorUsedError; + @JsonKey(name: 'business_type') + String? get businessType => throw _privateConstructorUsedError; + @JsonKey(name: 'metadata') + Map? get metadata => throw _privateConstructorUsedError; + @JsonKey(name: 'created_at') + DateTime? get createdAt => throw _privateConstructorUsedError; + @JsonKey(name: 'updated_at') + DateTime? get updatedAt => throw _privateConstructorUsedError; + + /// Serializes this CategoryDto to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of CategoryDto + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $CategoryDtoCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $CategoryDtoCopyWith<$Res> { + factory $CategoryDtoCopyWith( + CategoryDto value, + $Res Function(CategoryDto) then, + ) = _$CategoryDtoCopyWithImpl<$Res, CategoryDto>; + @useResult + $Res call({ + @JsonKey(name: 'id') String? id, + @JsonKey(name: 'organization_id') String? organizationId, + @JsonKey(name: 'name') String? name, + @JsonKey(name: 'description') String? description, + @JsonKey(name: 'business_type') String? businessType, + @JsonKey(name: 'metadata') Map? metadata, + @JsonKey(name: 'created_at') DateTime? createdAt, + @JsonKey(name: 'updated_at') DateTime? updatedAt, + }); +} + +/// @nodoc +class _$CategoryDtoCopyWithImpl<$Res, $Val extends CategoryDto> + implements $CategoryDtoCopyWith<$Res> { + _$CategoryDtoCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of CategoryDto + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? organizationId = freezed, + Object? name = freezed, + Object? description = freezed, + Object? businessType = freezed, + Object? metadata = freezed, + Object? createdAt = freezed, + Object? updatedAt = freezed, + }) { + return _then( + _value.copyWith( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + organizationId: freezed == organizationId + ? _value.organizationId + : organizationId // ignore: cast_nullable_to_non_nullable + as String?, + name: freezed == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String?, + description: freezed == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String?, + businessType: freezed == businessType + ? _value.businessType + : businessType // ignore: cast_nullable_to_non_nullable + as String?, + metadata: freezed == metadata + ? _value.metadata + : metadata // ignore: cast_nullable_to_non_nullable + as Map?, + createdAt: freezed == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + updatedAt: freezed == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + ) + as $Val, + ); + } +} + +/// @nodoc +abstract class _$$CategoryDtoImplCopyWith<$Res> + implements $CategoryDtoCopyWith<$Res> { + factory _$$CategoryDtoImplCopyWith( + _$CategoryDtoImpl value, + $Res Function(_$CategoryDtoImpl) then, + ) = __$$CategoryDtoImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({ + @JsonKey(name: 'id') String? id, + @JsonKey(name: 'organization_id') String? organizationId, + @JsonKey(name: 'name') String? name, + @JsonKey(name: 'description') String? description, + @JsonKey(name: 'business_type') String? businessType, + @JsonKey(name: 'metadata') Map? metadata, + @JsonKey(name: 'created_at') DateTime? createdAt, + @JsonKey(name: 'updated_at') DateTime? updatedAt, + }); +} + +/// @nodoc +class __$$CategoryDtoImplCopyWithImpl<$Res> + extends _$CategoryDtoCopyWithImpl<$Res, _$CategoryDtoImpl> + implements _$$CategoryDtoImplCopyWith<$Res> { + __$$CategoryDtoImplCopyWithImpl( + _$CategoryDtoImpl _value, + $Res Function(_$CategoryDtoImpl) _then, + ) : super(_value, _then); + + /// Create a copy of CategoryDto + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? organizationId = freezed, + Object? name = freezed, + Object? description = freezed, + Object? businessType = freezed, + Object? metadata = freezed, + Object? createdAt = freezed, + Object? updatedAt = freezed, + }) { + return _then( + _$CategoryDtoImpl( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + organizationId: freezed == organizationId + ? _value.organizationId + : organizationId // ignore: cast_nullable_to_non_nullable + as String?, + name: freezed == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String?, + description: freezed == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String?, + businessType: freezed == businessType + ? _value.businessType + : businessType // ignore: cast_nullable_to_non_nullable + as String?, + metadata: freezed == metadata + ? _value._metadata + : metadata // ignore: cast_nullable_to_non_nullable + as Map?, + createdAt: freezed == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + updatedAt: freezed == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + ), + ); + } +} + +/// @nodoc +@JsonSerializable() +class _$CategoryDtoImpl extends _CategoryDto { + const _$CategoryDtoImpl({ + @JsonKey(name: 'id') this.id, + @JsonKey(name: 'organization_id') this.organizationId, + @JsonKey(name: 'name') this.name, + @JsonKey(name: 'description') this.description, + @JsonKey(name: 'business_type') this.businessType, + @JsonKey(name: 'metadata') final Map? metadata, + @JsonKey(name: 'created_at') this.createdAt, + @JsonKey(name: 'updated_at') this.updatedAt, + }) : _metadata = metadata, + super._(); + + factory _$CategoryDtoImpl.fromJson(Map json) => + _$$CategoryDtoImplFromJson(json); + + @override + @JsonKey(name: 'id') + final String? id; + @override + @JsonKey(name: 'organization_id') + final String? organizationId; + @override + @JsonKey(name: 'name') + final String? name; + @override + @JsonKey(name: 'description') + final String? description; + @override + @JsonKey(name: 'business_type') + final String? businessType; + final Map? _metadata; + @override + @JsonKey(name: 'metadata') + Map? get metadata { + final value = _metadata; + if (value == null) return null; + if (_metadata is EqualUnmodifiableMapView) return _metadata; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + @override + @JsonKey(name: 'created_at') + final DateTime? createdAt; + @override + @JsonKey(name: 'updated_at') + final DateTime? updatedAt; + + @override + String toString() { + return 'CategoryDto(id: $id, organizationId: $organizationId, name: $name, description: $description, businessType: $businessType, metadata: $metadata, createdAt: $createdAt, updatedAt: $updatedAt)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$CategoryDtoImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.organizationId, organizationId) || + other.organizationId == organizationId) && + (identical(other.name, name) || other.name == name) && + (identical(other.description, description) || + other.description == description) && + (identical(other.businessType, businessType) || + other.businessType == businessType) && + const DeepCollectionEquality().equals(other._metadata, _metadata) && + (identical(other.createdAt, createdAt) || + other.createdAt == createdAt) && + (identical(other.updatedAt, updatedAt) || + other.updatedAt == updatedAt)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + id, + organizationId, + name, + description, + businessType, + const DeepCollectionEquality().hash(_metadata), + createdAt, + updatedAt, + ); + + /// Create a copy of CategoryDto + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$CategoryDtoImplCopyWith<_$CategoryDtoImpl> get copyWith => + __$$CategoryDtoImplCopyWithImpl<_$CategoryDtoImpl>(this, _$identity); + + @override + Map toJson() { + return _$$CategoryDtoImplToJson(this); + } +} + +abstract class _CategoryDto extends CategoryDto { + const factory _CategoryDto({ + @JsonKey(name: 'id') final String? id, + @JsonKey(name: 'organization_id') final String? organizationId, + @JsonKey(name: 'name') final String? name, + @JsonKey(name: 'description') final String? description, + @JsonKey(name: 'business_type') final String? businessType, + @JsonKey(name: 'metadata') final Map? metadata, + @JsonKey(name: 'created_at') final DateTime? createdAt, + @JsonKey(name: 'updated_at') final DateTime? updatedAt, + }) = _$CategoryDtoImpl; + const _CategoryDto._() : super._(); + + factory _CategoryDto.fromJson(Map json) = + _$CategoryDtoImpl.fromJson; + + @override + @JsonKey(name: 'id') + String? get id; + @override + @JsonKey(name: 'organization_id') + String? get organizationId; + @override + @JsonKey(name: 'name') + String? get name; + @override + @JsonKey(name: 'description') + String? get description; + @override + @JsonKey(name: 'business_type') + String? get businessType; + @override + @JsonKey(name: 'metadata') + Map? get metadata; + @override + @JsonKey(name: 'created_at') + DateTime? get createdAt; + @override + @JsonKey(name: 'updated_at') + DateTime? get updatedAt; + + /// Create a copy of CategoryDto + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$CategoryDtoImplCopyWith<_$CategoryDtoImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/infrastructure/category/category_dtos.g.dart b/lib/infrastructure/category/category_dtos.g.dart new file mode 100644 index 0000000..d41549d --- /dev/null +++ b/lib/infrastructure/category/category_dtos.g.dart @@ -0,0 +1,35 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'category_dtos.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$CategoryDtoImpl _$$CategoryDtoImplFromJson(Map json) => + _$CategoryDtoImpl( + id: json['id'] as String?, + organizationId: json['organization_id'] as String?, + name: json['name'] as String?, + description: json['description'] as String?, + businessType: json['business_type'] as String?, + metadata: json['metadata'] as Map?, + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + updatedAt: json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + ); + +Map _$$CategoryDtoImplToJson(_$CategoryDtoImpl instance) => + { + 'id': instance.id, + 'organization_id': instance.organizationId, + 'name': instance.name, + 'description': instance.description, + 'business_type': instance.businessType, + 'metadata': instance.metadata, + 'created_at': instance.createdAt?.toIso8601String(), + 'updated_at': instance.updatedAt?.toIso8601String(), + }; diff --git a/lib/infrastructure/category/datasource/remote_data_provider.dart b/lib/infrastructure/category/datasource/remote_data_provider.dart new file mode 100644 index 0000000..46e1ba9 --- /dev/null +++ b/lib/infrastructure/category/datasource/remote_data_provider.dart @@ -0,0 +1,44 @@ +import 'dart:developer'; + +import 'package:data_channel/data_channel.dart'; +import 'package:injectable/injectable.dart'; + +import '../../../common/api/api_client.dart'; +import '../../../common/api/api_failure.dart'; +import '../../../common/url/api_path.dart'; +import '../../../domain/category/category.dart'; +import '../category_dtos.dart'; + +@injectable +class CategoryRemoteDataProvider { + final ApiClient _apiClient; + final String _logName = 'CategoryRemoteDataProvider'; + + CategoryRemoteDataProvider(this._apiClient); + + Future>> fetch({ + int page = 1, + int limit = 20, + bool isActive = true, + }) async { + try { + final response = await _apiClient.get( + ApiPath.category, + params: {'page': page, 'limit': limit, 'is_active': isActive}, + ); + + if (response.data['data'] == null) { + return DC.error(CategoryFailure.empty()); + } + + final dto = (response.data['data']['categories'] as List) + .map((item) => CategoryDto.fromJson(item)) + .toList(); + + return DC.data(dto); + } on ApiFailure catch (e, s) { + log('fetchCategoryError', name: _logName, error: e, stackTrace: s); + return DC.error(CategoryFailure.serverError(e)); + } + } +} diff --git a/lib/infrastructure/category/dto/category_dto.dart b/lib/infrastructure/category/dto/category_dto.dart new file mode 100644 index 0000000..7894f0c --- /dev/null +++ b/lib/infrastructure/category/dto/category_dto.dart @@ -0,0 +1,31 @@ +part of '../category_dtos.dart'; + +@freezed +class CategoryDto with _$CategoryDto { + const CategoryDto._(); + + const factory CategoryDto({ + @JsonKey(name: 'id') String? id, + @JsonKey(name: 'organization_id') String? organizationId, + @JsonKey(name: 'name') String? name, + @JsonKey(name: 'description') String? description, + @JsonKey(name: 'business_type') String? businessType, + @JsonKey(name: 'metadata') Map? metadata, + @JsonKey(name: 'created_at') DateTime? createdAt, + @JsonKey(name: 'updated_at') DateTime? updatedAt, + }) = _CategoryDto; + + factory CategoryDto.fromJson(Map json) => + _$CategoryDtoFromJson(json); + + Category toDomain() => Category( + id: id ?? '', + organizationId: organizationId ?? '', + name: name ?? '', + description: description ?? '', + businessType: businessType ?? '', + metadata: metadata ?? {}, + createdAt: createdAt, + updatedAt: updatedAt, + ); +} diff --git a/lib/infrastructure/category/repositories/category_repository.dart b/lib/infrastructure/category/repositories/category_repository.dart new file mode 100644 index 0000000..73e96fe --- /dev/null +++ b/lib/infrastructure/category/repositories/category_repository.dart @@ -0,0 +1,41 @@ +import 'dart:developer'; + +import 'package:dartz/dartz.dart'; +import 'package:injectable/injectable.dart'; + +import '../../../domain/category/category.dart'; +import '../datasource/remote_data_provider.dart'; + +@Injectable(as: ICategoryRepository) +class CategoryRepository implements ICategoryRepository { + final CategoryRemoteDataProvider _dataProvider; + final String _logName = 'CategoryRepository'; + + CategoryRepository(this._dataProvider); + + @override + Future>> get({ + int page = 1, + int limit = 20, + bool isActive = true, + }) async { + try { + final result = await _dataProvider.fetch( + page: page, + limit: limit, + isActive: isActive, + ); + + if (result.hasError) { + return left(result.error!); + } + + final auth = result.data!.map((e) => e.toDomain()).toList(); + + return right(auth); + } catch (e, s) { + log('getCategoryError', name: _logName, error: e, stackTrace: s); + return left(const CategoryFailure.unexpectedError()); + } + } +} diff --git a/lib/injection.config.dart b/lib/injection.config.dart index 88c9774..9836760 100644 --- a/lib/injection.config.dart +++ b/lib/injection.config.dart @@ -14,6 +14,8 @@ import 'package:apskel_owner_flutter/application/auth/login_form/login_form_bloc as _i775; import 'package:apskel_owner_flutter/application/auth/logout_form/logout_form_bloc.dart' as _i574; +import 'package:apskel_owner_flutter/application/category/category_loader/category_loader_bloc.dart' + as _i183; import 'package:apskel_owner_flutter/application/language/language_bloc.dart' as _i455; import 'package:apskel_owner_flutter/application/sales/sales_loader/sales_loader_bloc.dart' @@ -30,6 +32,7 @@ import 'package:apskel_owner_flutter/common/network/network_client.dart' import 'package:apskel_owner_flutter/domain/analytic/repositories/i_analytic_repository.dart' as _i477; import 'package:apskel_owner_flutter/domain/auth/auth.dart' as _i49; +import 'package:apskel_owner_flutter/domain/category/category.dart' as _i1020; import 'package:apskel_owner_flutter/env.dart' as _i6; import 'package:apskel_owner_flutter/infrastructure/analytic/datasource/remote_data_provider.dart' as _i866; @@ -41,6 +44,10 @@ import 'package:apskel_owner_flutter/infrastructure/auth/datasources/remote_data as _i17; import 'package:apskel_owner_flutter/infrastructure/auth/repositories/auth_repository.dart' as _i1035; +import 'package:apskel_owner_flutter/infrastructure/category/datasource/remote_data_provider.dart' + as _i333; +import 'package:apskel_owner_flutter/infrastructure/category/repositories/category_repository.dart' + as _i869; import 'package:apskel_owner_flutter/presentation/router/app_router.dart' as _i258; import 'package:connectivity_plus/connectivity_plus.dart' as _i895; @@ -100,6 +107,9 @@ extension GetItInjectableX on _i174.GetIt { gh.factory<_i866.AnalyticRemoteDataProvider>( () => _i866.AnalyticRemoteDataProvider(gh<_i115.ApiClient>()), ); + gh.factory<_i333.CategoryRemoteDataProvider>( + () => _i333.CategoryRemoteDataProvider(gh<_i115.ApiClient>()), + ); gh.factory<_i477.IAnalyticRepository>( () => _i393.AnalyticRepository(gh<_i866.AnalyticRemoteDataProvider>()), ); @@ -109,6 +119,12 @@ extension GetItInjectableX on _i174.GetIt { gh<_i17.AuthRemoteDataProvider>(), ), ); + gh.factory<_i1020.ICategoryRepository>( + () => _i869.CategoryRepository(gh<_i333.CategoryRemoteDataProvider>()), + ); + gh.factory<_i183.CategoryLoaderBloc>( + () => _i183.CategoryLoaderBloc(gh<_i1020.ICategoryRepository>()), + ); gh.factory<_i882.SalesLoaderBloc>( () => _i882.SalesLoaderBloc(gh<_i477.IAnalyticRepository>()), ); diff --git a/lib/presentation/pages/product/product_page.dart b/lib/presentation/pages/product/product_page.dart index 259d0ea..56a1f94 100644 --- a/lib/presentation/pages/product/product_page.dart +++ b/lib/presentation/pages/product/product_page.dart @@ -1,27 +1,37 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:line_icons/line_icons.dart'; +import '../../../application/category/category_loader/category_loader_bloc.dart'; import '../../../common/theme/theme.dart'; +import '../../../domain/category/category.dart'; +import '../../../injection.dart'; import '../../components/appbar/appbar.dart'; import '../../components/button/button.dart'; import 'widgets/category_delegate.dart'; import 'widgets/product_tile.dart'; @RoutePage() -class ProductPage extends StatefulWidget { +class ProductPage extends StatefulWidget implements AutoRouteWrapper { const ProductPage({super.key}); @override State createState() => _ProductPageState(); + + @override + Widget wrappedRoute(BuildContext context) => BlocProvider( + create: (context) => + getIt()..add(CategoryLoaderEvent.fetched()), + child: this, + ); } enum ViewType { grid, list } class _ProductPageState extends State with TickerProviderStateMixin { - String selectedCategory = 'Semua'; - List categories = ['Semua', 'Makanan', 'Minuman', 'Snack', 'Dessert']; + Category selectedCategory = Category.addAllData(); ViewType currentViewType = ViewType.grid; // Sample product data @@ -121,7 +131,8 @@ class _ProductPageState extends State List get filteredProducts { return products.where((product) { bool matchesCategory = - selectedCategory == 'Semua' || product.category == selectedCategory; + selectedCategory.name == 'Semua' || + product.category == selectedCategory.id; return matchesCategory; }).toList(); } @@ -173,17 +184,21 @@ class _ProductPageState extends State } Widget _buildCategoryFilter() { - return SliverPersistentHeader( - pinned: true, - delegate: ProductCategoryHeaderDelegate( - categories: categories, - selectedCategory: selectedCategory, - onCategoryChanged: (category) { - setState(() { - selectedCategory = category; - }); - }, - ), + return BlocBuilder( + builder: (context, state) { + return SliverPersistentHeader( + pinned: true, + delegate: ProductCategoryHeaderDelegate( + categories: state.categories, + selectedCategory: selectedCategory, + onCategoryChanged: (category) { + setState(() { + selectedCategory = category; + }); + }, + ), + ); + }, ); } diff --git a/lib/presentation/pages/product/widgets/category_delegate.dart b/lib/presentation/pages/product/widgets/category_delegate.dart index e0a851b..54c6a37 100644 --- a/lib/presentation/pages/product/widgets/category_delegate.dart +++ b/lib/presentation/pages/product/widgets/category_delegate.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; import '../../../../common/theme/theme.dart'; +import '../../../../domain/category/category.dart'; class ProductCategoryHeaderDelegate extends SliverPersistentHeaderDelegate { - final List categories; - final String selectedCategory; - final ValueChanged onCategoryChanged; + final List categories; + final Category selectedCategory; + final ValueChanged onCategoryChanged; ProductCategoryHeaderDelegate({ required this.categories, @@ -35,7 +36,7 @@ class ProductCategoryHeaderDelegate extends SliverPersistentHeaderDelegate { return Container( margin: const EdgeInsets.only(right: 12.0), child: FilterChip( - label: Text(category), + label: Text(category.name), selected: isSelected, onSelected: (selected) => onCategoryChanged(category), backgroundColor: AppColor.surface, @@ -64,6 +65,7 @@ class ProductCategoryHeaderDelegate extends SliverPersistentHeaderDelegate { @override bool shouldRebuild(ProductCategoryHeaderDelegate oldDelegate) { - return oldDelegate.selectedCategory != selectedCategory; + return oldDelegate.categories != categories || + oldDelegate.selectedCategory != selectedCategory; } }