Compare commits
No commits in common. "dea5de8828bb86451bacfd832bcc7c79a01954a9" and "13b1b6e6b8c80c99c5bf5055b58911579c7bbbcb" have entirely different histories.
dea5de8828
...
13b1b6e6b8
@ -1,312 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:dartz/dartz.dart';
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
|
|
||||||
import '../../../domain/category/category.dart';
|
|
||||||
|
|
||||||
part 'category_loader_event.dart';
|
|
||||||
part 'category_loader_state.dart';
|
|
||||||
part 'category_loader_bloc.freezed.dart';
|
|
||||||
|
|
||||||
class CategoryLoaderBloc
|
|
||||||
extends Bloc<CategoryLoaderEvent, CategoryLoaderState> {
|
|
||||||
final ICategoryRepository _categoryRepository;
|
|
||||||
|
|
||||||
Timer? _searchDebounce;
|
|
||||||
bool _isLoadingMore = false;
|
|
||||||
|
|
||||||
CategoryLoaderBloc(this._categoryRepository)
|
|
||||||
: super(CategoryLoaderState.initial()) {
|
|
||||||
on<CategoryLoaderEvent>(_onCategoryLoaderEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onCategoryLoaderEvent(
|
|
||||||
CategoryLoaderEvent event,
|
|
||||||
Emitter<CategoryLoaderState> emit,
|
|
||||||
) {
|
|
||||||
return event.map(
|
|
||||||
getCategories: (e) async {
|
|
||||||
emit(state.copyWith(isLoadingMore: true));
|
|
||||||
|
|
||||||
log(
|
|
||||||
'📱 Loading categories - isActive: ${e.isActive}, forceRemote: ${e.forceRemote}',
|
|
||||||
);
|
|
||||||
|
|
||||||
final result = await _categoryRepository.getCategories(
|
|
||||||
page: 1,
|
|
||||||
limit: 50,
|
|
||||||
isActive: e.isActive,
|
|
||||||
search: e.search,
|
|
||||||
forceRemote: e.forceRemote,
|
|
||||||
);
|
|
||||||
|
|
||||||
await result.fold(
|
|
||||||
(failure) async {
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
isLoadingMore: false,
|
|
||||||
failureOptionCategory: optionOf(failure),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(response) async {
|
|
||||||
final categories = [Category.all(), ...response.categories];
|
|
||||||
|
|
||||||
final totalPages = response.totalPages;
|
|
||||||
final hasReachedMax = categories.length < 50 || 1 >= totalPages;
|
|
||||||
|
|
||||||
log(
|
|
||||||
'✅ Categories loaded: ${categories.length}, hasReachedMax: $hasReachedMax',
|
|
||||||
);
|
|
||||||
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
categories: categories,
|
|
||||||
page: 1,
|
|
||||||
hasReachedMax: hasReachedMax,
|
|
||||||
isLoadingMore: false,
|
|
||||||
failureOptionCategory: none(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
loadMore: (e) async {
|
|
||||||
final currentState = state;
|
|
||||||
|
|
||||||
// ❌ HAPUS pengecekan is! _Loaded karena state cuma 1 class doang
|
|
||||||
if (currentState.hasReachedMax ||
|
|
||||||
_isLoadingMore ||
|
|
||||||
currentState.isLoadingMore) {
|
|
||||||
log(
|
|
||||||
'⏹️ Load more blocked - hasReachedMax: ${currentState.hasReachedMax}, isLoadingMore: $_isLoadingMore',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_isLoadingMore = true;
|
|
||||||
emit(currentState.copyWith(isLoadingMore: true));
|
|
||||||
|
|
||||||
final nextPage = currentState.page + 1; // ✅ Ganti currentPage jadi page
|
|
||||||
log('📄 Loading more categories - page: $nextPage');
|
|
||||||
|
|
||||||
try {
|
|
||||||
final result = await _categoryRepository.getCategories(
|
|
||||||
page: nextPage,
|
|
||||||
limit: 10,
|
|
||||||
isActive: true,
|
|
||||||
search: currentState.searchQuery,
|
|
||||||
);
|
|
||||||
|
|
||||||
await result.fold(
|
|
||||||
(failure) async {
|
|
||||||
log('❌ Error loading more categories: $failure');
|
|
||||||
emit(currentState.copyWith(isLoadingMore: false));
|
|
||||||
},
|
|
||||||
(response) async {
|
|
||||||
final newCategories = response.categories;
|
|
||||||
final totalPages = response.totalPages;
|
|
||||||
|
|
||||||
// Prevent duplicate categories
|
|
||||||
final currentCategoryIds = currentState.categories
|
|
||||||
.map((c) => c.id)
|
|
||||||
.toSet();
|
|
||||||
final filteredNewCategories = newCategories
|
|
||||||
.where(
|
|
||||||
(category) => !currentCategoryIds.contains(category.id),
|
|
||||||
)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
final allCategories = List<Category>.from(currentState.categories)
|
|
||||||
..addAll(filteredNewCategories);
|
|
||||||
|
|
||||||
final hasReachedMax =
|
|
||||||
newCategories.length < 10 || nextPage >= totalPages;
|
|
||||||
|
|
||||||
log(
|
|
||||||
'✅ More categories loaded: ${filteredNewCategories.length} new, total: ${allCategories.length}',
|
|
||||||
);
|
|
||||||
|
|
||||||
emit(
|
|
||||||
currentState.copyWith(
|
|
||||||
categories: allCategories,
|
|
||||||
hasReachedMax: hasReachedMax,
|
|
||||||
page: nextPage, // ✅ Update page
|
|
||||||
isLoadingMore: false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
log('❌ Exception loading more categories: $e');
|
|
||||||
emit(currentState.copyWith(isLoadingMore: false));
|
|
||||||
} finally {
|
|
||||||
_isLoadingMore = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
refresh: (e) async {
|
|
||||||
final currentState = state;
|
|
||||||
bool isActive = true;
|
|
||||||
String? searchQuery = currentState.searchQuery;
|
|
||||||
|
|
||||||
_isLoadingMore = false;
|
|
||||||
_searchDebounce?.cancel();
|
|
||||||
|
|
||||||
log('🔄 Refreshing categories');
|
|
||||||
|
|
||||||
// Clear local cache
|
|
||||||
_categoryRepository.clearCache();
|
|
||||||
|
|
||||||
add(
|
|
||||||
CategoryLoaderEvent.getCategories(
|
|
||||||
isActive: isActive,
|
|
||||||
search: searchQuery,
|
|
||||||
forceRemote: true, // Force remote refresh
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
search: (e) async {
|
|
||||||
// Cancel previous search
|
|
||||||
_searchDebounce?.cancel();
|
|
||||||
|
|
||||||
// Debounce search for better UX
|
|
||||||
_searchDebounce = Timer(Duration(milliseconds: 300), () async {
|
|
||||||
emit(state.copyWith(isLoadingMore: true));
|
|
||||||
_isLoadingMore = false;
|
|
||||||
|
|
||||||
log('🔍 Searching categories: "${e.query}"');
|
|
||||||
|
|
||||||
final result = await _categoryRepository.getCategories(
|
|
||||||
page: 1,
|
|
||||||
limit: 20, // More results for search
|
|
||||||
isActive: e.isActive,
|
|
||||||
search: e.query,
|
|
||||||
);
|
|
||||||
|
|
||||||
await result.fold(
|
|
||||||
(failure) async {
|
|
||||||
log('❌ Search error: $failure');
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
isLoadingMore: false,
|
|
||||||
failureOptionCategory: optionOf(failure),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(response) async {
|
|
||||||
final categories = [Category.all(), ...response.categories];
|
|
||||||
final totalPages = response.totalPages;
|
|
||||||
final hasReachedMax = categories.length < 20 || 1 >= totalPages;
|
|
||||||
|
|
||||||
log('✅ Search results: ${categories.length} categories found');
|
|
||||||
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
categories: categories,
|
|
||||||
hasReachedMax: hasReachedMax,
|
|
||||||
page: 1,
|
|
||||||
isLoadingMore: false,
|
|
||||||
failureOptionCategory: none(),
|
|
||||||
searchQuery: e.query,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
syncAll: (e) async {
|
|
||||||
emit(state.copyWith(isLoadingMore: true));
|
|
||||||
|
|
||||||
log('🔄 Starting full category sync...');
|
|
||||||
|
|
||||||
final result = await _categoryRepository.syncAllCategories();
|
|
||||||
|
|
||||||
await result.fold(
|
|
||||||
(failure) async {
|
|
||||||
log('❌ Sync failed: $failure');
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
isLoadingMore: false,
|
|
||||||
failureOptionCategory: optionOf(failure),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// After sync error, try to load local data
|
|
||||||
Timer(Duration(seconds: 2), () {
|
|
||||||
add(const CategoryLoaderEvent.getCategories());
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(successMessage) async {
|
|
||||||
log('✅ Sync completed: $successMessage');
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
isLoadingMore: false,
|
|
||||||
failureOptionCategory: none(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// After successful sync, load the updated data
|
|
||||||
Timer(Duration(seconds: 1), () {
|
|
||||||
add(const CategoryLoaderEvent.getCategories());
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
getAllCategories: (e) async {
|
|
||||||
try {
|
|
||||||
log('📋 Loading all categories for dropdown...');
|
|
||||||
|
|
||||||
// final categories = await _categoryRepository.getAllCategories();
|
|
||||||
|
|
||||||
// emit(
|
|
||||||
// state.copyWith(
|
|
||||||
// categories: categories,
|
|
||||||
// isLoadingMore: false,
|
|
||||||
// failureOptionCategory: none(),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// log('✅ All categories loaded: ${categories.length}');
|
|
||||||
} catch (e) {
|
|
||||||
log('❌ Error loading all categories: $e');
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
isLoadingMore: false,
|
|
||||||
failureOptionCategory: optionOf(
|
|
||||||
CategoryFailure.dynamicErrorMessage(
|
|
||||||
'Gagal memuat semua kategori: $e',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getDatabaseStats: (e) async {
|
|
||||||
try {
|
|
||||||
final stats = await _categoryRepository.getDatabaseStats();
|
|
||||||
log('📊 Category database stats retrieved: $stats');
|
|
||||||
|
|
||||||
// You can emit a special state here if needed for UI updates
|
|
||||||
// For now, just log the stats
|
|
||||||
} catch (e) {
|
|
||||||
log('❌ Error getting category database stats: $e');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clearCache: (e) async {
|
|
||||||
log('🧹 Manually clearing category cache');
|
|
||||||
_categoryRepository.clearCache();
|
|
||||||
|
|
||||||
// Refresh current data after cache clear
|
|
||||||
add(const CategoryLoaderEvent.refresh());
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> close() {
|
|
||||||
_searchDebounce?.cancel();
|
|
||||||
return super.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,27 +0,0 @@
|
|||||||
part of 'category_loader_bloc.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class CategoryLoaderEvent with _$CategoryLoaderEvent {
|
|
||||||
const factory CategoryLoaderEvent.getCategories({
|
|
||||||
@Default(true) bool isActive,
|
|
||||||
String? search,
|
|
||||||
@Default(false) bool forceRemote,
|
|
||||||
}) = _GetCategories;
|
|
||||||
|
|
||||||
const factory CategoryLoaderEvent.loadMore() = _LoadMore;
|
|
||||||
|
|
||||||
const factory CategoryLoaderEvent.refresh() = _Refresh;
|
|
||||||
|
|
||||||
const factory CategoryLoaderEvent.search({
|
|
||||||
required String query,
|
|
||||||
@Default(true) bool isActive,
|
|
||||||
}) = _Search;
|
|
||||||
|
|
||||||
const factory CategoryLoaderEvent.syncAll() = _SyncAll;
|
|
||||||
|
|
||||||
const factory CategoryLoaderEvent.getAllCategories() = _GetAllCategories;
|
|
||||||
|
|
||||||
const factory CategoryLoaderEvent.getDatabaseStats() = _GetDatabaseStats;
|
|
||||||
|
|
||||||
const factory CategoryLoaderEvent.clearCache() = _ClearCache;
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
part of 'category_loader_bloc.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class CategoryLoaderState with _$CategoryLoaderState {
|
|
||||||
factory CategoryLoaderState({
|
|
||||||
required List<Category> categories,
|
|
||||||
required Option<CategoryFailure> failureOptionCategory,
|
|
||||||
@Default(false) bool hasReachedMax,
|
|
||||||
@Default(1) int page,
|
|
||||||
@Default(false) bool isLoadingMore,
|
|
||||||
String? searchQuery,
|
|
||||||
}) = _CategoryLoaderState;
|
|
||||||
|
|
||||||
factory CategoryLoaderState.initial() =>
|
|
||||||
CategoryLoaderState(categories: [], failureOptionCategory: none());
|
|
||||||
}
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:dartz/dartz.dart';
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
import 'package:injectable/injectable.dart';
|
|
||||||
|
|
||||||
import '../../../domain/outlet/outlet.dart';
|
|
||||||
|
|
||||||
part 'outlet_loader_event.dart';
|
|
||||||
part 'outlet_loader_state.dart';
|
|
||||||
part 'outlet_loader_bloc.freezed.dart';
|
|
||||||
|
|
||||||
@injectable
|
|
||||||
class OutletLoaderBloc extends Bloc<OutletLoaderEvent, OutletLoaderState> {
|
|
||||||
final IOutletRepository _outletRepository;
|
|
||||||
|
|
||||||
OutletLoaderBloc(this._outletRepository)
|
|
||||||
: super(OutletLoaderState.initial()) {
|
|
||||||
on<OutletLoaderEvent>(_onOutletLoaderEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onOutletLoaderEvent(
|
|
||||||
OutletLoaderEvent event,
|
|
||||||
Emitter<OutletLoaderState> emit,
|
|
||||||
) {
|
|
||||||
return event.map(
|
|
||||||
fetched: (e) async {
|
|
||||||
var newState = state;
|
|
||||||
|
|
||||||
if (e.isRefresh) {
|
|
||||||
newState = state.copyWith(isFetching: true);
|
|
||||||
|
|
||||||
emit(newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
newState = await _mapFetchedToState(state, isRefresh: e.isRefresh);
|
|
||||||
|
|
||||||
emit(newState);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<OutletLoaderState> _mapFetchedToState(
|
|
||||||
OutletLoaderState state, {
|
|
||||||
bool isRefresh = false,
|
|
||||||
}) async {
|
|
||||||
state = state.copyWith(isFetching: false);
|
|
||||||
|
|
||||||
if (state.hasReachedMax && state.outlets.isNotEmpty && !isRefresh) {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRefresh) {
|
|
||||||
state = state.copyWith(
|
|
||||||
page: 1,
|
|
||||||
failureOptionOutlet: none(),
|
|
||||||
hasReachedMax: false,
|
|
||||||
outlets: [],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final failureOrOutlet = await _outletRepository.getOutlets(
|
|
||||||
page: state.page,
|
|
||||||
);
|
|
||||||
|
|
||||||
state = failureOrOutlet.fold(
|
|
||||||
(f) {
|
|
||||||
if (state.outlets.isNotEmpty) {
|
|
||||||
return state.copyWith(hasReachedMax: true);
|
|
||||||
}
|
|
||||||
return state.copyWith(failureOptionOutlet: optionOf(f));
|
|
||||||
},
|
|
||||||
(outlets) {
|
|
||||||
return state.copyWith(
|
|
||||||
outlets: List.from(state.outlets)..addAll(outlets),
|
|
||||||
failureOptionOutlet: none(),
|
|
||||||
page: state.page + 1,
|
|
||||||
hasReachedMax: outlets.length < 10,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,479 +0,0 @@
|
|||||||
// 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 'outlet_loader_bloc.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// FreezedGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
T _$identity<T>(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 _$OutletLoaderEvent {
|
|
||||||
bool get isRefresh => throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult when<TResult extends Object?>({
|
|
||||||
required TResult Function(bool isRefresh) fetched,
|
|
||||||
}) => throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(bool isRefresh)? fetched,
|
|
||||||
}) => throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
|
||||||
TResult Function(bool isRefresh)? fetched,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) => throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult map<TResult extends Object?>({
|
|
||||||
required TResult Function(_Fetched value) fetched,
|
|
||||||
}) => throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(_Fetched value)? fetched,
|
|
||||||
}) => throw _privateConstructorUsedError;
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeMap<TResult extends Object?>({
|
|
||||||
TResult Function(_Fetched value)? fetched,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
/// Create a copy of OutletLoaderEvent
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
$OutletLoaderEventCopyWith<OutletLoaderEvent> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $OutletLoaderEventCopyWith<$Res> {
|
|
||||||
factory $OutletLoaderEventCopyWith(
|
|
||||||
OutletLoaderEvent value,
|
|
||||||
$Res Function(OutletLoaderEvent) then,
|
|
||||||
) = _$OutletLoaderEventCopyWithImpl<$Res, OutletLoaderEvent>;
|
|
||||||
@useResult
|
|
||||||
$Res call({bool isRefresh});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$OutletLoaderEventCopyWithImpl<$Res, $Val extends OutletLoaderEvent>
|
|
||||||
implements $OutletLoaderEventCopyWith<$Res> {
|
|
||||||
_$OutletLoaderEventCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
/// Create a copy of OutletLoaderEvent
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({Object? isRefresh = null}) {
|
|
||||||
return _then(
|
|
||||||
_value.copyWith(
|
|
||||||
isRefresh: null == isRefresh
|
|
||||||
? _value.isRefresh
|
|
||||||
: isRefresh // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
)
|
|
||||||
as $Val,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$FetchedImplCopyWith<$Res>
|
|
||||||
implements $OutletLoaderEventCopyWith<$Res> {
|
|
||||||
factory _$$FetchedImplCopyWith(
|
|
||||||
_$FetchedImpl value,
|
|
||||||
$Res Function(_$FetchedImpl) then,
|
|
||||||
) = __$$FetchedImplCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call({bool isRefresh});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$FetchedImplCopyWithImpl<$Res>
|
|
||||||
extends _$OutletLoaderEventCopyWithImpl<$Res, _$FetchedImpl>
|
|
||||||
implements _$$FetchedImplCopyWith<$Res> {
|
|
||||||
__$$FetchedImplCopyWithImpl(
|
|
||||||
_$FetchedImpl _value,
|
|
||||||
$Res Function(_$FetchedImpl) _then,
|
|
||||||
) : super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of OutletLoaderEvent
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({Object? isRefresh = null}) {
|
|
||||||
return _then(
|
|
||||||
_$FetchedImpl(
|
|
||||||
isRefresh: null == isRefresh
|
|
||||||
? _value.isRefresh
|
|
||||||
: isRefresh // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
|
|
||||||
class _$FetchedImpl implements _Fetched {
|
|
||||||
const _$FetchedImpl({this.isRefresh = false});
|
|
||||||
|
|
||||||
@override
|
|
||||||
@JsonKey()
|
|
||||||
final bool isRefresh;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'OutletLoaderEvent.fetched(isRefresh: $isRefresh)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$FetchedImpl &&
|
|
||||||
(identical(other.isRefresh, isRefresh) ||
|
|
||||||
other.isRefresh == isRefresh));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, isRefresh);
|
|
||||||
|
|
||||||
/// Create a copy of OutletLoaderEvent
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$$FetchedImplCopyWith<_$FetchedImpl> get copyWith =>
|
|
||||||
__$$FetchedImplCopyWithImpl<_$FetchedImpl>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult when<TResult extends Object?>({
|
|
||||||
required TResult Function(bool isRefresh) fetched,
|
|
||||||
}) {
|
|
||||||
return fetched(isRefresh);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? whenOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(bool isRefresh)? fetched,
|
|
||||||
}) {
|
|
||||||
return fetched?.call(isRefresh);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeWhen<TResult extends Object?>({
|
|
||||||
TResult Function(bool isRefresh)? fetched,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (fetched != null) {
|
|
||||||
return fetched(isRefresh);
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult map<TResult extends Object?>({
|
|
||||||
required TResult Function(_Fetched value) fetched,
|
|
||||||
}) {
|
|
||||||
return fetched(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult? mapOrNull<TResult extends Object?>({
|
|
||||||
TResult? Function(_Fetched value)? fetched,
|
|
||||||
}) {
|
|
||||||
return fetched?.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@optionalTypeArgs
|
|
||||||
TResult maybeMap<TResult extends Object?>({
|
|
||||||
TResult Function(_Fetched value)? fetched,
|
|
||||||
required TResult orElse(),
|
|
||||||
}) {
|
|
||||||
if (fetched != null) {
|
|
||||||
return fetched(this);
|
|
||||||
}
|
|
||||||
return orElse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _Fetched implements OutletLoaderEvent {
|
|
||||||
const factory _Fetched({final bool isRefresh}) = _$FetchedImpl;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool get isRefresh;
|
|
||||||
|
|
||||||
/// Create a copy of OutletLoaderEvent
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
_$$FetchedImplCopyWith<_$FetchedImpl> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$OutletLoaderState {
|
|
||||||
List<Outlet> get outlets => throw _privateConstructorUsedError;
|
|
||||||
Option<OutletFailure> get failureOptionOutlet =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
bool get isFetching => throw _privateConstructorUsedError;
|
|
||||||
bool get hasReachedMax => throw _privateConstructorUsedError;
|
|
||||||
int get page => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
/// Create a copy of OutletLoaderState
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
$OutletLoaderStateCopyWith<OutletLoaderState> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $OutletLoaderStateCopyWith<$Res> {
|
|
||||||
factory $OutletLoaderStateCopyWith(
|
|
||||||
OutletLoaderState value,
|
|
||||||
$Res Function(OutletLoaderState) then,
|
|
||||||
) = _$OutletLoaderStateCopyWithImpl<$Res, OutletLoaderState>;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
List<Outlet> outlets,
|
|
||||||
Option<OutletFailure> failureOptionOutlet,
|
|
||||||
bool isFetching,
|
|
||||||
bool hasReachedMax,
|
|
||||||
int page,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$OutletLoaderStateCopyWithImpl<$Res, $Val extends OutletLoaderState>
|
|
||||||
implements $OutletLoaderStateCopyWith<$Res> {
|
|
||||||
_$OutletLoaderStateCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
/// Create a copy of OutletLoaderState
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? outlets = null,
|
|
||||||
Object? failureOptionOutlet = null,
|
|
||||||
Object? isFetching = null,
|
|
||||||
Object? hasReachedMax = null,
|
|
||||||
Object? page = null,
|
|
||||||
}) {
|
|
||||||
return _then(
|
|
||||||
_value.copyWith(
|
|
||||||
outlets: null == outlets
|
|
||||||
? _value.outlets
|
|
||||||
: outlets // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<Outlet>,
|
|
||||||
failureOptionOutlet: null == failureOptionOutlet
|
|
||||||
? _value.failureOptionOutlet
|
|
||||||
: failureOptionOutlet // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Option<OutletFailure>,
|
|
||||||
isFetching: null == isFetching
|
|
||||||
? _value.isFetching
|
|
||||||
: isFetching // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
hasReachedMax: null == hasReachedMax
|
|
||||||
? _value.hasReachedMax
|
|
||||||
: hasReachedMax // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
page: null == page
|
|
||||||
? _value.page
|
|
||||||
: page // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
)
|
|
||||||
as $Val,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$OutletLoaderStateImplCopyWith<$Res>
|
|
||||||
implements $OutletLoaderStateCopyWith<$Res> {
|
|
||||||
factory _$$OutletLoaderStateImplCopyWith(
|
|
||||||
_$OutletLoaderStateImpl value,
|
|
||||||
$Res Function(_$OutletLoaderStateImpl) then,
|
|
||||||
) = __$$OutletLoaderStateImplCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
List<Outlet> outlets,
|
|
||||||
Option<OutletFailure> failureOptionOutlet,
|
|
||||||
bool isFetching,
|
|
||||||
bool hasReachedMax,
|
|
||||||
int page,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$OutletLoaderStateImplCopyWithImpl<$Res>
|
|
||||||
extends _$OutletLoaderStateCopyWithImpl<$Res, _$OutletLoaderStateImpl>
|
|
||||||
implements _$$OutletLoaderStateImplCopyWith<$Res> {
|
|
||||||
__$$OutletLoaderStateImplCopyWithImpl(
|
|
||||||
_$OutletLoaderStateImpl _value,
|
|
||||||
$Res Function(_$OutletLoaderStateImpl) _then,
|
|
||||||
) : super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of OutletLoaderState
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? outlets = null,
|
|
||||||
Object? failureOptionOutlet = null,
|
|
||||||
Object? isFetching = null,
|
|
||||||
Object? hasReachedMax = null,
|
|
||||||
Object? page = null,
|
|
||||||
}) {
|
|
||||||
return _then(
|
|
||||||
_$OutletLoaderStateImpl(
|
|
||||||
outlets: null == outlets
|
|
||||||
? _value._outlets
|
|
||||||
: outlets // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<Outlet>,
|
|
||||||
failureOptionOutlet: null == failureOptionOutlet
|
|
||||||
? _value.failureOptionOutlet
|
|
||||||
: failureOptionOutlet // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Option<OutletFailure>,
|
|
||||||
isFetching: null == isFetching
|
|
||||||
? _value.isFetching
|
|
||||||
: isFetching // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
hasReachedMax: null == hasReachedMax
|
|
||||||
? _value.hasReachedMax
|
|
||||||
: hasReachedMax // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
page: null == page
|
|
||||||
? _value.page
|
|
||||||
: page // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
|
|
||||||
class _$OutletLoaderStateImpl implements _OutletLoaderState {
|
|
||||||
const _$OutletLoaderStateImpl({
|
|
||||||
required final List<Outlet> outlets,
|
|
||||||
required this.failureOptionOutlet,
|
|
||||||
this.isFetching = false,
|
|
||||||
this.hasReachedMax = false,
|
|
||||||
this.page = 1,
|
|
||||||
}) : _outlets = outlets;
|
|
||||||
|
|
||||||
final List<Outlet> _outlets;
|
|
||||||
@override
|
|
||||||
List<Outlet> get outlets {
|
|
||||||
if (_outlets is EqualUnmodifiableListView) return _outlets;
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(_outlets);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
final Option<OutletFailure> failureOptionOutlet;
|
|
||||||
@override
|
|
||||||
@JsonKey()
|
|
||||||
final bool isFetching;
|
|
||||||
@override
|
|
||||||
@JsonKey()
|
|
||||||
final bool hasReachedMax;
|
|
||||||
@override
|
|
||||||
@JsonKey()
|
|
||||||
final int page;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'OutletLoaderState(outlets: $outlets, failureOptionOutlet: $failureOptionOutlet, isFetching: $isFetching, hasReachedMax: $hasReachedMax, page: $page)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$OutletLoaderStateImpl &&
|
|
||||||
const DeepCollectionEquality().equals(other._outlets, _outlets) &&
|
|
||||||
(identical(other.failureOptionOutlet, failureOptionOutlet) ||
|
|
||||||
other.failureOptionOutlet == failureOptionOutlet) &&
|
|
||||||
(identical(other.isFetching, isFetching) ||
|
|
||||||
other.isFetching == isFetching) &&
|
|
||||||
(identical(other.hasReachedMax, hasReachedMax) ||
|
|
||||||
other.hasReachedMax == hasReachedMax) &&
|
|
||||||
(identical(other.page, page) || other.page == page));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
const DeepCollectionEquality().hash(_outlets),
|
|
||||||
failureOptionOutlet,
|
|
||||||
isFetching,
|
|
||||||
hasReachedMax,
|
|
||||||
page,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Create a copy of OutletLoaderState
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$$OutletLoaderStateImplCopyWith<_$OutletLoaderStateImpl> get copyWith =>
|
|
||||||
__$$OutletLoaderStateImplCopyWithImpl<_$OutletLoaderStateImpl>(
|
|
||||||
this,
|
|
||||||
_$identity,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _OutletLoaderState implements OutletLoaderState {
|
|
||||||
const factory _OutletLoaderState({
|
|
||||||
required final List<Outlet> outlets,
|
|
||||||
required final Option<OutletFailure> failureOptionOutlet,
|
|
||||||
final bool isFetching,
|
|
||||||
final bool hasReachedMax,
|
|
||||||
final int page,
|
|
||||||
}) = _$OutletLoaderStateImpl;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Outlet> get outlets;
|
|
||||||
@override
|
|
||||||
Option<OutletFailure> get failureOptionOutlet;
|
|
||||||
@override
|
|
||||||
bool get isFetching;
|
|
||||||
@override
|
|
||||||
bool get hasReachedMax;
|
|
||||||
@override
|
|
||||||
int get page;
|
|
||||||
|
|
||||||
/// Create a copy of OutletLoaderState
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
_$$OutletLoaderStateImplCopyWith<_$OutletLoaderStateImpl> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
part of 'outlet_loader_bloc.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class OutletLoaderEvent with _$OutletLoaderEvent {
|
|
||||||
const factory OutletLoaderEvent.fetched({@Default(false) bool isRefresh}) =
|
|
||||||
_Fetched;
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
part of 'outlet_loader_bloc.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class OutletLoaderState with _$OutletLoaderState {
|
|
||||||
const factory OutletLoaderState({
|
|
||||||
required List<Outlet> outlets,
|
|
||||||
required Option<OutletFailure> failureOptionOutlet,
|
|
||||||
@Default(false) bool isFetching,
|
|
||||||
@Default(false) bool hasReachedMax,
|
|
||||||
@Default(1) int page,
|
|
||||||
}) = _OutletLoaderState;
|
|
||||||
|
|
||||||
factory OutletLoaderState.initial() =>
|
|
||||||
OutletLoaderState(outlets: [], failureOptionOutlet: none());
|
|
||||||
}
|
|
||||||
@ -1,5 +1,3 @@
|
|||||||
class AppConstant {
|
class AppConstant {
|
||||||
static const String appName = "Apskel POS";
|
static const String appName = "Apskel POS";
|
||||||
|
|
||||||
static const int cacheExpire = 10; // in minutes
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,158 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'package:sqflite/sqflite.dart';
|
|
||||||
import 'package:path/path.dart';
|
|
||||||
|
|
||||||
class DatabaseHelper {
|
|
||||||
static Database? _database;
|
|
||||||
|
|
||||||
Future<Database> get database async {
|
|
||||||
_database ??= await _initDatabase();
|
|
||||||
return _database!;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Database> _initDatabase() async {
|
|
||||||
String path = join(await getDatabasesPath(), 'db_pos.db');
|
|
||||||
|
|
||||||
return await openDatabase(
|
|
||||||
path,
|
|
||||||
version: 1, // Updated version for categories table
|
|
||||||
onCreate: _onCreate,
|
|
||||||
onUpgrade: _onUpgrade,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onCreate(Database db, int version) async {
|
|
||||||
// Products table
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE products (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
organization_id TEXT,
|
|
||||||
category_id TEXT,
|
|
||||||
sku TEXT,
|
|
||||||
name TEXT,
|
|
||||||
description TEXT,
|
|
||||||
price INTEGER,
|
|
||||||
cost INTEGER,
|
|
||||||
business_type TEXT,
|
|
||||||
image_url TEXT,
|
|
||||||
printer_type TEXT,
|
|
||||||
metadata TEXT,
|
|
||||||
is_active INTEGER,
|
|
||||||
created_at TEXT,
|
|
||||||
updated_at TEXT
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
|
|
||||||
// Product Variants table
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE product_variants (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
product_id TEXT,
|
|
||||||
name TEXT,
|
|
||||||
price_modifier INTEGER,
|
|
||||||
cost INTEGER,
|
|
||||||
metadata TEXT,
|
|
||||||
created_at TEXT,
|
|
||||||
updated_at TEXT,
|
|
||||||
FOREIGN KEY (product_id) REFERENCES products (id) ON DELETE CASCADE
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
|
|
||||||
// Categories table - NEW
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE categories (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
organization_id TEXT,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
description TEXT,
|
|
||||||
business_type TEXT,
|
|
||||||
metadata TEXT,
|
|
||||||
is_active INTEGER DEFAULT 1,
|
|
||||||
created_at TEXT,
|
|
||||||
updated_at TEXT
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
|
|
||||||
// Printer table
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE printers (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
code TEXT UNIQUE NOT NULL,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
address TEXT,
|
|
||||||
paper TEXT,
|
|
||||||
type TEXT,
|
|
||||||
created_at TEXT,
|
|
||||||
updated_at TEXT
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
|
|
||||||
// Create indexes for better performance
|
|
||||||
await db.execute(
|
|
||||||
'CREATE INDEX idx_products_category_id ON products(category_id)',
|
|
||||||
);
|
|
||||||
await db.execute('CREATE INDEX idx_products_name ON products(name)');
|
|
||||||
await db.execute('CREATE INDEX idx_products_sku ON products(sku)');
|
|
||||||
await db.execute('CREATE INDEX idx_categories_name ON categories(name)');
|
|
||||||
await db.execute(
|
|
||||||
'CREATE INDEX idx_categories_organization_id ON categories(organization_id)',
|
|
||||||
);
|
|
||||||
await db.execute(
|
|
||||||
'CREATE INDEX idx_categories_is_active ON categories(is_active)',
|
|
||||||
);
|
|
||||||
await db.execute('CREATE INDEX idx_printers_code ON printers(code)');
|
|
||||||
await db.execute('CREATE INDEX idx_printers_type ON printers(type)');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
|
||||||
if (oldVersion < 2) {
|
|
||||||
// Add printer table in version 2
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE printers (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
code TEXT UNIQUE NOT NULL,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
address TEXT,
|
|
||||||
paper TEXT,
|
|
||||||
type TEXT,
|
|
||||||
created_at TEXT,
|
|
||||||
updated_at TEXT
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
|
|
||||||
await db.execute('CREATE INDEX idx_printers_code ON printers(code)');
|
|
||||||
await db.execute('CREATE INDEX idx_printers_type ON printers(type)');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldVersion < 3) {
|
|
||||||
// Add categories table in version 3
|
|
||||||
await db.execute('''
|
|
||||||
CREATE TABLE categories (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
organization_id TEXT,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
description TEXT,
|
|
||||||
business_type TEXT,
|
|
||||||
metadata TEXT,
|
|
||||||
is_active INTEGER DEFAULT 1,
|
|
||||||
created_at TEXT,
|
|
||||||
updated_at TEXT
|
|
||||||
)
|
|
||||||
''');
|
|
||||||
|
|
||||||
await db.execute('CREATE INDEX idx_categories_name ON categories(name)');
|
|
||||||
await db.execute(
|
|
||||||
'CREATE INDEX idx_categories_organization_id ON categories(organization_id)',
|
|
||||||
);
|
|
||||||
await db.execute(
|
|
||||||
'CREATE INDEX idx_categories_is_active ON categories(is_active)',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> close() async {
|
|
||||||
final db = await database;
|
|
||||||
await db.close();
|
|
||||||
_database = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import 'package:injectable/injectable.dart';
|
|
||||||
|
|
||||||
import '../database/database_helper.dart';
|
|
||||||
|
|
||||||
@module
|
|
||||||
abstract class DatabaseDi {
|
|
||||||
@singleton
|
|
||||||
DatabaseHelper get databaseHelper => DatabaseHelper();
|
|
||||||
}
|
|
||||||
@ -12,7 +12,7 @@ class AppColor {
|
|||||||
static const Color secondaryDark = Color(0xFF388E3C);
|
static const Color secondaryDark = Color(0xFF388E3C);
|
||||||
|
|
||||||
// Background Colors
|
// Background Colors
|
||||||
static const Color background = Color(0xFFF1F5F9);
|
static const Color background = Color(0xFFF8F9FA);
|
||||||
static const Color backgroundLight = Color(0xFFFFFFFF);
|
static const Color backgroundLight = Color(0xFFFFFFFF);
|
||||||
static const Color backgroundDark = Color(0xFF1A1A1A);
|
static const Color backgroundDark = Color(0xFF1A1A1A);
|
||||||
static const Color surface = Color(0xFFFFFFFF);
|
static const Color surface = Color(0xFFFFFFFF);
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
class ApiPath {
|
class ApiPath {
|
||||||
static const String login = '/api/v1/auth/login';
|
static const String login = '/api/v1/auth/login';
|
||||||
static const String outlets = '/api/v1/outlets';
|
static const String outlets = '/api/v1/outlets';
|
||||||
static const String categories = '/api/v1/categories';
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
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_category_repository.dart';
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,59 +0,0 @@
|
|||||||
part of '../category.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class ListCategory with _$ListCategory {
|
|
||||||
const factory ListCategory({
|
|
||||||
required List<Category> categories,
|
|
||||||
required int totalCount,
|
|
||||||
required int page,
|
|
||||||
required int limit,
|
|
||||||
required int totalPages,
|
|
||||||
}) = _ListCategory;
|
|
||||||
|
|
||||||
factory ListCategory.empty() => const ListCategory(
|
|
||||||
categories: [],
|
|
||||||
totalCount: 0,
|
|
||||||
page: 0,
|
|
||||||
limit: 0,
|
|
||||||
totalPages: 0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class Category with _$Category {
|
|
||||||
const factory Category({
|
|
||||||
required String id,
|
|
||||||
required String organizationId,
|
|
||||||
required String name,
|
|
||||||
required String description,
|
|
||||||
required String businessType,
|
|
||||||
required int order,
|
|
||||||
required Map<String, dynamic> metadata,
|
|
||||||
required String createdAt,
|
|
||||||
required String updatedAt,
|
|
||||||
}) = _Category;
|
|
||||||
|
|
||||||
factory Category.empty() => const Category(
|
|
||||||
id: '',
|
|
||||||
organizationId: '',
|
|
||||||
name: '',
|
|
||||||
description: '',
|
|
||||||
businessType: '',
|
|
||||||
order: 0,
|
|
||||||
metadata: {},
|
|
||||||
createdAt: '',
|
|
||||||
updatedAt: '',
|
|
||||||
);
|
|
||||||
|
|
||||||
factory Category.all() => const Category(
|
|
||||||
id: 'all',
|
|
||||||
organizationId: '',
|
|
||||||
name: 'Semua',
|
|
||||||
businessType: 'restaurant',
|
|
||||||
metadata: {},
|
|
||||||
createdAt: '',
|
|
||||||
updatedAt: '',
|
|
||||||
description: '',
|
|
||||||
order: 1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
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.localStorageError(String erroMessage) =
|
|
||||||
_LocalStorageError;
|
|
||||||
const factory CategoryFailure.dynamicErrorMessage(String erroMessage) =
|
|
||||||
_DynamicErrorMessage;
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
part of '../category.dart';
|
|
||||||
|
|
||||||
abstract class ICategoryRepository {
|
|
||||||
Future<Either<CategoryFailure, ListCategory>> getCategories({
|
|
||||||
int page = 1,
|
|
||||||
int limit = 10,
|
|
||||||
bool isActive = true,
|
|
||||||
String? search,
|
|
||||||
bool forceRemote = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<Either<CategoryFailure, Category>> getCategoryById(String id);
|
|
||||||
|
|
||||||
Future<Either<CategoryFailure, String>> syncAllCategories();
|
|
||||||
|
|
||||||
Future<Either<CategoryFailure, ListCategory>> refreshCategories({
|
|
||||||
bool isActive = true,
|
|
||||||
String? search,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<bool> hasLocalCategories();
|
|
||||||
|
|
||||||
Future<Either<CategoryFailure, Map<String, dynamic>>> getDatabaseStats();
|
|
||||||
|
|
||||||
void clearCache();
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
|
|
||||||
import '../../domain/category/category.dart';
|
|
||||||
|
|
||||||
part 'category_dtos.freezed.dart';
|
|
||||||
part 'category_dtos.g.dart';
|
|
||||||
|
|
||||||
part 'dtos/category_dto.dart';
|
|
||||||
@ -1,675 +0,0 @@
|
|||||||
// 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>(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',
|
|
||||||
);
|
|
||||||
|
|
||||||
ListCategoryDto _$ListCategoryDtoFromJson(Map<String, dynamic> json) {
|
|
||||||
return _ListCategoryDto.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$ListCategoryDto {
|
|
||||||
@JsonKey(name: "categories")
|
|
||||||
List<CategoryDto>? get categories => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(name: "total_count")
|
|
||||||
int? get totalCount => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(name: "page")
|
|
||||||
int? get page => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(name: "limit")
|
|
||||||
int? get limit => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(name: "total_pages")
|
|
||||||
int? get totalPages => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
/// Serializes this ListCategoryDto to a JSON map.
|
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
/// Create a copy of ListCategoryDto
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
$ListCategoryDtoCopyWith<ListCategoryDto> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $ListCategoryDtoCopyWith<$Res> {
|
|
||||||
factory $ListCategoryDtoCopyWith(
|
|
||||||
ListCategoryDto value,
|
|
||||||
$Res Function(ListCategoryDto) then,
|
|
||||||
) = _$ListCategoryDtoCopyWithImpl<$Res, ListCategoryDto>;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
@JsonKey(name: "categories") List<CategoryDto>? categories,
|
|
||||||
@JsonKey(name: "total_count") int? totalCount,
|
|
||||||
@JsonKey(name: "page") int? page,
|
|
||||||
@JsonKey(name: "limit") int? limit,
|
|
||||||
@JsonKey(name: "total_pages") int? totalPages,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$ListCategoryDtoCopyWithImpl<$Res, $Val extends ListCategoryDto>
|
|
||||||
implements $ListCategoryDtoCopyWith<$Res> {
|
|
||||||
_$ListCategoryDtoCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
/// Create a copy of ListCategoryDto
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? categories = freezed,
|
|
||||||
Object? totalCount = freezed,
|
|
||||||
Object? page = freezed,
|
|
||||||
Object? limit = freezed,
|
|
||||||
Object? totalPages = freezed,
|
|
||||||
}) {
|
|
||||||
return _then(
|
|
||||||
_value.copyWith(
|
|
||||||
categories: freezed == categories
|
|
||||||
? _value.categories
|
|
||||||
: categories // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<CategoryDto>?,
|
|
||||||
totalCount: freezed == totalCount
|
|
||||||
? _value.totalCount
|
|
||||||
: totalCount // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int?,
|
|
||||||
page: freezed == page
|
|
||||||
? _value.page
|
|
||||||
: page // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int?,
|
|
||||||
limit: freezed == limit
|
|
||||||
? _value.limit
|
|
||||||
: limit // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int?,
|
|
||||||
totalPages: freezed == totalPages
|
|
||||||
? _value.totalPages
|
|
||||||
: totalPages // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int?,
|
|
||||||
)
|
|
||||||
as $Val,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$ListCategoryDtoImplCopyWith<$Res>
|
|
||||||
implements $ListCategoryDtoCopyWith<$Res> {
|
|
||||||
factory _$$ListCategoryDtoImplCopyWith(
|
|
||||||
_$ListCategoryDtoImpl value,
|
|
||||||
$Res Function(_$ListCategoryDtoImpl) then,
|
|
||||||
) = __$$ListCategoryDtoImplCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
@JsonKey(name: "categories") List<CategoryDto>? categories,
|
|
||||||
@JsonKey(name: "total_count") int? totalCount,
|
|
||||||
@JsonKey(name: "page") int? page,
|
|
||||||
@JsonKey(name: "limit") int? limit,
|
|
||||||
@JsonKey(name: "total_pages") int? totalPages,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$ListCategoryDtoImplCopyWithImpl<$Res>
|
|
||||||
extends _$ListCategoryDtoCopyWithImpl<$Res, _$ListCategoryDtoImpl>
|
|
||||||
implements _$$ListCategoryDtoImplCopyWith<$Res> {
|
|
||||||
__$$ListCategoryDtoImplCopyWithImpl(
|
|
||||||
_$ListCategoryDtoImpl _value,
|
|
||||||
$Res Function(_$ListCategoryDtoImpl) _then,
|
|
||||||
) : super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of ListCategoryDto
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? categories = freezed,
|
|
||||||
Object? totalCount = freezed,
|
|
||||||
Object? page = freezed,
|
|
||||||
Object? limit = freezed,
|
|
||||||
Object? totalPages = freezed,
|
|
||||||
}) {
|
|
||||||
return _then(
|
|
||||||
_$ListCategoryDtoImpl(
|
|
||||||
categories: freezed == categories
|
|
||||||
? _value._categories
|
|
||||||
: categories // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<CategoryDto>?,
|
|
||||||
totalCount: freezed == totalCount
|
|
||||||
? _value.totalCount
|
|
||||||
: totalCount // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int?,
|
|
||||||
page: freezed == page
|
|
||||||
? _value.page
|
|
||||||
: page // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int?,
|
|
||||||
limit: freezed == limit
|
|
||||||
? _value.limit
|
|
||||||
: limit // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int?,
|
|
||||||
totalPages: freezed == totalPages
|
|
||||||
? _value.totalPages
|
|
||||||
: totalPages // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int?,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _$ListCategoryDtoImpl extends _ListCategoryDto {
|
|
||||||
const _$ListCategoryDtoImpl({
|
|
||||||
@JsonKey(name: "categories") final List<CategoryDto>? categories,
|
|
||||||
@JsonKey(name: "total_count") this.totalCount,
|
|
||||||
@JsonKey(name: "page") this.page,
|
|
||||||
@JsonKey(name: "limit") this.limit,
|
|
||||||
@JsonKey(name: "total_pages") this.totalPages,
|
|
||||||
}) : _categories = categories,
|
|
||||||
super._();
|
|
||||||
|
|
||||||
factory _$ListCategoryDtoImpl.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$$ListCategoryDtoImplFromJson(json);
|
|
||||||
|
|
||||||
final List<CategoryDto>? _categories;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "categories")
|
|
||||||
List<CategoryDto>? get categories {
|
|
||||||
final value = _categories;
|
|
||||||
if (value == null) return null;
|
|
||||||
if (_categories is EqualUnmodifiableListView) return _categories;
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "total_count")
|
|
||||||
final int? totalCount;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "page")
|
|
||||||
final int? page;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "limit")
|
|
||||||
final int? limit;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "total_pages")
|
|
||||||
final int? totalPages;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'ListCategoryDto(categories: $categories, totalCount: $totalCount, page: $page, limit: $limit, totalPages: $totalPages)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$ListCategoryDtoImpl &&
|
|
||||||
const DeepCollectionEquality().equals(
|
|
||||||
other._categories,
|
|
||||||
_categories,
|
|
||||||
) &&
|
|
||||||
(identical(other.totalCount, totalCount) ||
|
|
||||||
other.totalCount == totalCount) &&
|
|
||||||
(identical(other.page, page) || other.page == page) &&
|
|
||||||
(identical(other.limit, limit) || other.limit == limit) &&
|
|
||||||
(identical(other.totalPages, totalPages) ||
|
|
||||||
other.totalPages == totalPages));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
const DeepCollectionEquality().hash(_categories),
|
|
||||||
totalCount,
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
totalPages,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Create a copy of ListCategoryDto
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$$ListCategoryDtoImplCopyWith<_$ListCategoryDtoImpl> get copyWith =>
|
|
||||||
__$$ListCategoryDtoImplCopyWithImpl<_$ListCategoryDtoImpl>(
|
|
||||||
this,
|
|
||||||
_$identity,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$$ListCategoryDtoImplToJson(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _ListCategoryDto extends ListCategoryDto {
|
|
||||||
const factory _ListCategoryDto({
|
|
||||||
@JsonKey(name: "categories") final List<CategoryDto>? categories,
|
|
||||||
@JsonKey(name: "total_count") final int? totalCount,
|
|
||||||
@JsonKey(name: "page") final int? page,
|
|
||||||
@JsonKey(name: "limit") final int? limit,
|
|
||||||
@JsonKey(name: "total_pages") final int? totalPages,
|
|
||||||
}) = _$ListCategoryDtoImpl;
|
|
||||||
const _ListCategoryDto._() : super._();
|
|
||||||
|
|
||||||
factory _ListCategoryDto.fromJson(Map<String, dynamic> json) =
|
|
||||||
_$ListCategoryDtoImpl.fromJson;
|
|
||||||
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "categories")
|
|
||||||
List<CategoryDto>? get categories;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "total_count")
|
|
||||||
int? get totalCount;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "page")
|
|
||||||
int? get page;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "limit")
|
|
||||||
int? get limit;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "total_pages")
|
|
||||||
int? get totalPages;
|
|
||||||
|
|
||||||
/// Create a copy of ListCategoryDto
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
_$$ListCategoryDtoImplCopyWith<_$ListCategoryDtoImpl> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
CategoryDto _$CategoryDtoFromJson(Map<String, dynamic> 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: "order")
|
|
||||||
int? get order => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(name: "metadata")
|
|
||||||
Map<String, dynamic>? get metadata => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(name: "created_at")
|
|
||||||
String? get createdAt => throw _privateConstructorUsedError;
|
|
||||||
@JsonKey(name: "updated_at")
|
|
||||||
String? get updatedAt => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
/// Serializes this CategoryDto to a JSON map.
|
|
||||||
Map<String, dynamic> 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<CategoryDto> 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: "order") int? order,
|
|
||||||
@JsonKey(name: "metadata") Map<String, dynamic>? metadata,
|
|
||||||
@JsonKey(name: "created_at") String? createdAt,
|
|
||||||
@JsonKey(name: "updated_at") String? 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? order = 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?,
|
|
||||||
order: freezed == order
|
|
||||||
? _value.order
|
|
||||||
: order // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int?,
|
|
||||||
metadata: freezed == metadata
|
|
||||||
? _value.metadata
|
|
||||||
: metadata // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Map<String, dynamic>?,
|
|
||||||
createdAt: freezed == createdAt
|
|
||||||
? _value.createdAt
|
|
||||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,
|
|
||||||
updatedAt: freezed == updatedAt
|
|
||||||
? _value.updatedAt
|
|
||||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,
|
|
||||||
)
|
|
||||||
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: "order") int? order,
|
|
||||||
@JsonKey(name: "metadata") Map<String, dynamic>? metadata,
|
|
||||||
@JsonKey(name: "created_at") String? createdAt,
|
|
||||||
@JsonKey(name: "updated_at") String? 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? order = 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?,
|
|
||||||
order: freezed == order
|
|
||||||
? _value.order
|
|
||||||
: order // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int?,
|
|
||||||
metadata: freezed == metadata
|
|
||||||
? _value._metadata
|
|
||||||
: metadata // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Map<String, dynamic>?,
|
|
||||||
createdAt: freezed == createdAt
|
|
||||||
? _value.createdAt
|
|
||||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,
|
|
||||||
updatedAt: freezed == updatedAt
|
|
||||||
? _value.updatedAt
|
|
||||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @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: "order") this.order,
|
|
||||||
@JsonKey(name: "metadata") final Map<String, dynamic>? metadata,
|
|
||||||
@JsonKey(name: "created_at") this.createdAt,
|
|
||||||
@JsonKey(name: "updated_at") this.updatedAt,
|
|
||||||
}) : _metadata = metadata,
|
|
||||||
super._();
|
|
||||||
|
|
||||||
factory _$CategoryDtoImpl.fromJson(Map<String, dynamic> 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;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "order")
|
|
||||||
final int? order;
|
|
||||||
final Map<String, dynamic>? _metadata;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "metadata")
|
|
||||||
Map<String, dynamic>? 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 String? createdAt;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "updated_at")
|
|
||||||
final String? updatedAt;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'CategoryDto(id: $id, organizationId: $organizationId, name: $name, description: $description, businessType: $businessType, order: $order, 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) &&
|
|
||||||
(identical(other.order, order) || other.order == order) &&
|
|
||||||
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,
|
|
||||||
order,
|
|
||||||
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<String, dynamic> 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: "order") final int? order,
|
|
||||||
@JsonKey(name: "metadata") final Map<String, dynamic>? metadata,
|
|
||||||
@JsonKey(name: "created_at") final String? createdAt,
|
|
||||||
@JsonKey(name: "updated_at") final String? updatedAt,
|
|
||||||
}) = _$CategoryDtoImpl;
|
|
||||||
const _CategoryDto._() : super._();
|
|
||||||
|
|
||||||
factory _CategoryDto.fromJson(Map<String, dynamic> 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: "order")
|
|
||||||
int? get order;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "metadata")
|
|
||||||
Map<String, dynamic>? get metadata;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "created_at")
|
|
||||||
String? get createdAt;
|
|
||||||
@override
|
|
||||||
@JsonKey(name: "updated_at")
|
|
||||||
String? 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;
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'category_dtos.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// JsonSerializableGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
_$ListCategoryDtoImpl _$$ListCategoryDtoImplFromJson(
|
|
||||||
Map<String, dynamic> json,
|
|
||||||
) => _$ListCategoryDtoImpl(
|
|
||||||
categories: (json['categories'] as List<dynamic>?)
|
|
||||||
?.map((e) => CategoryDto.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
|
||||||
totalCount: (json['total_count'] as num?)?.toInt(),
|
|
||||||
page: (json['page'] as num?)?.toInt(),
|
|
||||||
limit: (json['limit'] as num?)?.toInt(),
|
|
||||||
totalPages: (json['total_pages'] as num?)?.toInt(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$$ListCategoryDtoImplToJson(
|
|
||||||
_$ListCategoryDtoImpl instance,
|
|
||||||
) => <String, dynamic>{
|
|
||||||
'categories': instance.categories,
|
|
||||||
'total_count': instance.totalCount,
|
|
||||||
'page': instance.page,
|
|
||||||
'limit': instance.limit,
|
|
||||||
'total_pages': instance.totalPages,
|
|
||||||
};
|
|
||||||
|
|
||||||
_$CategoryDtoImpl _$$CategoryDtoImplFromJson(Map<String, dynamic> 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?,
|
|
||||||
order: (json['order'] as num?)?.toInt(),
|
|
||||||
metadata: json['metadata'] as Map<String, dynamic>?,
|
|
||||||
createdAt: json['created_at'] as String?,
|
|
||||||
updatedAt: json['updated_at'] as String?,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$$CategoryDtoImplToJson(_$CategoryDtoImpl instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'id': instance.id,
|
|
||||||
'organization_id': instance.organizationId,
|
|
||||||
'name': instance.name,
|
|
||||||
'description': instance.description,
|
|
||||||
'business_type': instance.businessType,
|
|
||||||
'order': instance.order,
|
|
||||||
'metadata': instance.metadata,
|
|
||||||
'created_at': instance.createdAt,
|
|
||||||
'updated_at': instance.updatedAt,
|
|
||||||
};
|
|
||||||
@ -1,349 +0,0 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:data_channel/data_channel.dart';
|
|
||||||
import 'package:injectable/injectable.dart';
|
|
||||||
import 'package:sqflite/sqflite.dart';
|
|
||||||
|
|
||||||
import '../../../common/constant/app_constant.dart';
|
|
||||||
import '../../../common/database/database_helper.dart';
|
|
||||||
import '../../../domain/category/category.dart';
|
|
||||||
import '../category_dtos.dart';
|
|
||||||
|
|
||||||
@injectable
|
|
||||||
class CategoryLocalDataProvider {
|
|
||||||
final DatabaseHelper _databaseHelper;
|
|
||||||
final _logName = 'CategoryLocalDataProvider';
|
|
||||||
|
|
||||||
CategoryLocalDataProvider(this._databaseHelper);
|
|
||||||
|
|
||||||
final Map<String, List<CategoryDto>> _queryCache = {};
|
|
||||||
final Duration _cacheExpiry = Duration(minutes: AppConstant.cacheExpire);
|
|
||||||
final Map<String, DateTime> _cacheTimestamps = {};
|
|
||||||
|
|
||||||
Future<DC<CategoryFailure, void>> saveCategoriesBatch(
|
|
||||||
List<CategoryDto> categories, {
|
|
||||||
bool clearFirst = false,
|
|
||||||
}) async {
|
|
||||||
final db = await _databaseHelper.database;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await db.transaction((txn) async {
|
|
||||||
if (clearFirst) {
|
|
||||||
log('🗑️ Clearing existing categories...', name: _logName);
|
|
||||||
await txn.delete('categories');
|
|
||||||
}
|
|
||||||
|
|
||||||
log(
|
|
||||||
'💾 Batch saving ${categories.length} categories...',
|
|
||||||
name: _logName,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Batch insert categories
|
|
||||||
final batch = txn.batch();
|
|
||||||
for (final category in categories) {
|
|
||||||
batch.insert(
|
|
||||||
'categories',
|
|
||||||
category.toMap(),
|
|
||||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
await batch.commit(noResult: true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear cache after update
|
|
||||||
clearCache();
|
|
||||||
log(
|
|
||||||
'✅ Successfully batch saved ${categories.length} categories',
|
|
||||||
name: _logName,
|
|
||||||
);
|
|
||||||
|
|
||||||
return DC.data(null);
|
|
||||||
} catch (e, s) {
|
|
||||||
log(
|
|
||||||
'❌ Error batch saving categories',
|
|
||||||
name: _logName,
|
|
||||||
error: e,
|
|
||||||
stackTrace: s,
|
|
||||||
);
|
|
||||||
return DC.error(CategoryFailure.dynamicErrorMessage(e.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<DC<CategoryFailure, List<CategoryDto>>> getCachedCategories({
|
|
||||||
int page = 1,
|
|
||||||
int limit = 10,
|
|
||||||
bool isActive = true,
|
|
||||||
String? search,
|
|
||||||
}) async {
|
|
||||||
final cacheKey = _generateCacheKey(page, limit, isActive, search);
|
|
||||||
final now = DateTime.now();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check cache first
|
|
||||||
if (_queryCache.containsKey(cacheKey) &&
|
|
||||||
_cacheTimestamps.containsKey(cacheKey)) {
|
|
||||||
final cacheTime = _cacheTimestamps[cacheKey]!;
|
|
||||||
if (now.difference(cacheTime) < _cacheExpiry) {
|
|
||||||
log(
|
|
||||||
'🚀 Cache HIT: $cacheKey (${_queryCache[cacheKey]!.length} categories)',
|
|
||||||
name: _logName,
|
|
||||||
);
|
|
||||||
return DC.data(_queryCache[cacheKey]!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log('📀 Cache MISS: $cacheKey, querying database...', name: _logName);
|
|
||||||
|
|
||||||
// Cache miss, query database
|
|
||||||
final result = await getCategories(
|
|
||||||
page: page,
|
|
||||||
limit: limit,
|
|
||||||
isActive: isActive,
|
|
||||||
search: search,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check if result has data or error
|
|
||||||
if (result.hasData) {
|
|
||||||
final categories = result.data!;
|
|
||||||
|
|
||||||
// Store in cache
|
|
||||||
_queryCache[cacheKey] = categories;
|
|
||||||
_cacheTimestamps[cacheKey] = now;
|
|
||||||
|
|
||||||
log(
|
|
||||||
'💾 Cached ${categories.length} categories for key: $cacheKey',
|
|
||||||
name: _logName,
|
|
||||||
);
|
|
||||||
|
|
||||||
return DC.data(categories);
|
|
||||||
} else {
|
|
||||||
// Return error from database query
|
|
||||||
return DC.error(result.error!);
|
|
||||||
}
|
|
||||||
} catch (e, s) {
|
|
||||||
log(
|
|
||||||
'❌ Error getting cached categories',
|
|
||||||
name: _logName,
|
|
||||||
error: e,
|
|
||||||
stackTrace: s,
|
|
||||||
);
|
|
||||||
return DC.error(CategoryFailure.localStorageError(e.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<DC<CategoryFailure, List<CategoryDto>>> getCategories({
|
|
||||||
int page = 1,
|
|
||||||
int limit = 10,
|
|
||||||
bool isActive = true,
|
|
||||||
String? search,
|
|
||||||
}) async {
|
|
||||||
final db = await _databaseHelper.database;
|
|
||||||
|
|
||||||
try {
|
|
||||||
String query = 'SELECT * FROM categories WHERE 1=1';
|
|
||||||
List<dynamic> whereArgs = [];
|
|
||||||
|
|
||||||
// Note: Assuming is_active will be added to database schema
|
|
||||||
if (isActive) {
|
|
||||||
query += ' AND is_active = ?';
|
|
||||||
whereArgs.add(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (search != null && search.isNotEmpty) {
|
|
||||||
query += ' AND (name LIKE ? OR description LIKE ?)';
|
|
||||||
whereArgs.add('%$search%');
|
|
||||||
whereArgs.add('%$search%');
|
|
||||||
}
|
|
||||||
|
|
||||||
// query += ' ORDER BY name ASC';
|
|
||||||
|
|
||||||
if (limit > 0) {
|
|
||||||
query += ' LIMIT ?';
|
|
||||||
whereArgs.add(limit);
|
|
||||||
|
|
||||||
if (page > 1) {
|
|
||||||
query += ' OFFSET ?';
|
|
||||||
whereArgs.add((page - 1) * limit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<Map<String, dynamic>> maps = await db.rawQuery(
|
|
||||||
query,
|
|
||||||
whereArgs,
|
|
||||||
);
|
|
||||||
|
|
||||||
final categories = maps.map((map) => CategoryDto.fromMap(map)).toList();
|
|
||||||
|
|
||||||
log(
|
|
||||||
'📊 Retrieved ${categories.length} categories from database',
|
|
||||||
name: _logName,
|
|
||||||
);
|
|
||||||
|
|
||||||
return DC.data(categories);
|
|
||||||
} catch (e, s) {
|
|
||||||
log(
|
|
||||||
'❌ Error getting categories',
|
|
||||||
name: _logName,
|
|
||||||
error: e,
|
|
||||||
stackTrace: s,
|
|
||||||
);
|
|
||||||
return DC.error(CategoryFailure.localStorageError(e.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<DC<CategoryFailure, CategoryDto>> getCategoryById(String id) async {
|
|
||||||
final db = await _databaseHelper.database;
|
|
||||||
|
|
||||||
try {
|
|
||||||
final List<Map<String, dynamic>> maps = await db.query(
|
|
||||||
'categories',
|
|
||||||
where: 'id = ?',
|
|
||||||
whereArgs: [id],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (maps.isEmpty) {
|
|
||||||
log('❌ Category not found: $id', name: _logName);
|
|
||||||
return DC.error(CategoryFailure.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
final category = CategoryDto.fromMap(maps.first);
|
|
||||||
log('✅ Category found: ${category.name}', name: _logName);
|
|
||||||
return DC.data(category);
|
|
||||||
} catch (e, s) {
|
|
||||||
log(
|
|
||||||
'❌ Error getting category by ID',
|
|
||||||
name: _logName,
|
|
||||||
error: e,
|
|
||||||
stackTrace: s,
|
|
||||||
);
|
|
||||||
return DC.error(CategoryFailure.localStorageError(e.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<int> getTotalCount({bool isActive = true, String? search}) async {
|
|
||||||
final db = await _databaseHelper.database;
|
|
||||||
|
|
||||||
try {
|
|
||||||
String query = 'SELECT COUNT(*) FROM categories WHERE 1=1';
|
|
||||||
List<dynamic> whereArgs = [];
|
|
||||||
|
|
||||||
if (isActive) {
|
|
||||||
query += ' AND is_active = ?';
|
|
||||||
whereArgs.add(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (search != null && search.isNotEmpty) {
|
|
||||||
query += ' AND (name LIKE ? OR description LIKE ?)';
|
|
||||||
whereArgs.add('%$search%');
|
|
||||||
whereArgs.add('%$search%');
|
|
||||||
}
|
|
||||||
|
|
||||||
final result = await db.rawQuery(query, whereArgs);
|
|
||||||
final count = Sqflite.firstIntValue(result) ?? 0;
|
|
||||||
log(
|
|
||||||
'📊 Category total count: $count (isActive: $isActive, search: $search)',
|
|
||||||
name: _logName,
|
|
||||||
);
|
|
||||||
return count;
|
|
||||||
} catch (e) {
|
|
||||||
log('❌ Error getting category total count: $e', name: _logName);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> hasCategories() async {
|
|
||||||
final count = await getTotalCount();
|
|
||||||
final hasData = count > 0;
|
|
||||||
log('🔍 Has categories: $hasData ($count categories)', name: _logName);
|
|
||||||
return hasData;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<DC<CategoryFailure, Map<String, dynamic>>> getDatabaseStats() async {
|
|
||||||
final db = await _databaseHelper.database;
|
|
||||||
|
|
||||||
try {
|
|
||||||
final categoryCount =
|
|
||||||
Sqflite.firstIntValue(
|
|
||||||
await db.rawQuery('SELECT COUNT(*) FROM categories'),
|
|
||||||
) ??
|
|
||||||
0;
|
|
||||||
|
|
||||||
final activeCount =
|
|
||||||
Sqflite.firstIntValue(
|
|
||||||
await db.rawQuery(
|
|
||||||
'SELECT COUNT(*) FROM categories WHERE is_active = 1',
|
|
||||||
),
|
|
||||||
) ??
|
|
||||||
0;
|
|
||||||
|
|
||||||
final stats = {
|
|
||||||
'total_categories': categoryCount,
|
|
||||||
'active_categories': activeCount,
|
|
||||||
'cache_entries': _queryCache.length,
|
|
||||||
'last_updated': DateTime.now().toIso8601String(),
|
|
||||||
};
|
|
||||||
|
|
||||||
log('📊 Category Database Stats: $stats', name: _logName);
|
|
||||||
return DC.data(stats);
|
|
||||||
} catch (e, s) {
|
|
||||||
log(
|
|
||||||
'❌ Error getting category database stats',
|
|
||||||
name: _logName,
|
|
||||||
error: e,
|
|
||||||
stackTrace: s,
|
|
||||||
);
|
|
||||||
return DC.error(
|
|
||||||
CategoryFailure.localStorageError(
|
|
||||||
'Gagal memuat statistik database: $e',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> clearAllCategories() async {
|
|
||||||
final db = await _databaseHelper.database;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await db.delete('categories');
|
|
||||||
clearCache();
|
|
||||||
log('🗑️ All categories cleared from local DB');
|
|
||||||
} catch (e) {
|
|
||||||
log('❌ Error clearing categories: $e');
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearCache() {
|
|
||||||
final count = _queryCache.length;
|
|
||||||
_queryCache.clear();
|
|
||||||
_cacheTimestamps.clear();
|
|
||||||
log('🧹 Category cache cleared: $count entries removed', name: _logName);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _generateCacheKey(int page, int limit, bool isActive, String? search) {
|
|
||||||
return 'categories_${page}_${limit}_${isActive}_${search ?? 'null'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearExpiredCache() {
|
|
||||||
final now = DateTime.now();
|
|
||||||
final expiredKeys = <String>[];
|
|
||||||
|
|
||||||
_cacheTimestamps.forEach((key, timestamp) {
|
|
||||||
if (now.difference(timestamp) > _cacheExpiry) {
|
|
||||||
expiredKeys.add(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for (final key in expiredKeys) {
|
|
||||||
_queryCache.remove(key);
|
|
||||||
_cacheTimestamps.remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expiredKeys.isNotEmpty) {
|
|
||||||
log(
|
|
||||||
'⏰ Expired category cache cleared: ${expiredKeys.length} entries',
|
|
||||||
name: _logName,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
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/function/app_function.dart';
|
|
||||||
import '../../../common/url/api_path.dart';
|
|
||||||
import '../../../domain/category/category.dart';
|
|
||||||
import '../category_dtos.dart';
|
|
||||||
|
|
||||||
@injectable
|
|
||||||
class CategoryRemoteDataProvider {
|
|
||||||
final ApiClient _apiClient;
|
|
||||||
final _logName = 'CategoryRemoteDataProvider';
|
|
||||||
|
|
||||||
CategoryRemoteDataProvider(this._apiClient);
|
|
||||||
|
|
||||||
Future<DC<CategoryFailure, ListCategoryDto>> fetchCategories({
|
|
||||||
int page = 1,
|
|
||||||
int limit = 10,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
final response = await _apiClient.get(
|
|
||||||
ApiPath.categories,
|
|
||||||
params: {'page': page, 'limit': limit},
|
|
||||||
headers: getAuthorizationHeader(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.data['data'] == null) {
|
|
||||||
return DC.error(CategoryFailure.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
final categories = ListCategoryDto.fromJson(
|
|
||||||
response.data['data'] as Map<String, dynamic>,
|
|
||||||
);
|
|
||||||
|
|
||||||
return DC.data(categories);
|
|
||||||
} on ApiFailure catch (e, s) {
|
|
||||||
log('fetchCategoryError', name: _logName, error: e, stackTrace: s);
|
|
||||||
return DC.error(CategoryFailure.serverError(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
part of '../category_dtos.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
class ListCategoryDto with _$ListCategoryDto {
|
|
||||||
const ListCategoryDto._();
|
|
||||||
|
|
||||||
const factory ListCategoryDto({
|
|
||||||
@JsonKey(name: "categories") List<CategoryDto>? categories,
|
|
||||||
@JsonKey(name: "total_count") int? totalCount,
|
|
||||||
@JsonKey(name: "page") int? page,
|
|
||||||
@JsonKey(name: "limit") int? limit,
|
|
||||||
@JsonKey(name: "total_pages") int? totalPages,
|
|
||||||
}) = _ListCategoryDto;
|
|
||||||
|
|
||||||
factory ListCategoryDto.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$ListCategoryDtoFromJson(json);
|
|
||||||
|
|
||||||
ListCategory toDomain() => ListCategory(
|
|
||||||
categories: categories?.map((dto) => dto.toDomain()).toList() ?? [],
|
|
||||||
totalCount: totalCount ?? 0,
|
|
||||||
page: page ?? 0,
|
|
||||||
limit: limit ?? 0,
|
|
||||||
totalPages: totalPages ?? 0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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: "order") int? order,
|
|
||||||
@JsonKey(name: "metadata") Map<String, dynamic>? metadata,
|
|
||||||
@JsonKey(name: "created_at") String? createdAt,
|
|
||||||
@JsonKey(name: "updated_at") String? updatedAt,
|
|
||||||
}) = _CategoryDto;
|
|
||||||
|
|
||||||
factory CategoryDto.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$CategoryDtoFromJson(json);
|
|
||||||
|
|
||||||
/// Mapping ke domain
|
|
||||||
Category toDomain() => Category(
|
|
||||||
id: id ?? '',
|
|
||||||
organizationId: organizationId ?? '',
|
|
||||||
name: name ?? '',
|
|
||||||
description: description ?? '',
|
|
||||||
businessType: businessType ?? '',
|
|
||||||
order: order ?? 0,
|
|
||||||
metadata: metadata ?? {},
|
|
||||||
createdAt: createdAt ?? '',
|
|
||||||
updatedAt: updatedAt ?? '',
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Mapping ke Map untuk SQLite
|
|
||||||
Map<String, dynamic> toMap() => {
|
|
||||||
'id': id,
|
|
||||||
'organization_id': organizationId,
|
|
||||||
'name': name,
|
|
||||||
'description': description,
|
|
||||||
'business_type': businessType,
|
|
||||||
'order': order,
|
|
||||||
'metadata': metadata != null ? jsonEncode(metadata) : null,
|
|
||||||
'created_at': createdAt,
|
|
||||||
'updated_at': updatedAt,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Mapping dari Map SQLite
|
|
||||||
factory CategoryDto.fromMap(Map<String, dynamic> map) => CategoryDto(
|
|
||||||
id: map['id'] as String?,
|
|
||||||
organizationId: map['organization_id'] as String?,
|
|
||||||
name: map['name'] as String?,
|
|
||||||
description: map['description'] as String?,
|
|
||||||
businessType: map['business_type'] as String?,
|
|
||||||
order: map['order'] as int?,
|
|
||||||
metadata: map['metadata'] != null
|
|
||||||
? jsonDecode(map['metadata'] as String) as Map<String, dynamic>
|
|
||||||
: null,
|
|
||||||
createdAt: map['created_at'] as String?,
|
|
||||||
updatedAt: map['updated_at'] as String?,
|
|
||||||
);
|
|
||||||
|
|
||||||
factory CategoryDto.fromDomain(Category category) => CategoryDto(
|
|
||||||
id: category.id,
|
|
||||||
organizationId: category.organizationId,
|
|
||||||
name: category.name,
|
|
||||||
description: category.description,
|
|
||||||
businessType: category.businessType,
|
|
||||||
order: category.order,
|
|
||||||
metadata: category.metadata,
|
|
||||||
createdAt: category.createdAt,
|
|
||||||
updatedAt: category.updatedAt,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,352 +0,0 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:dartz/dartz.dart';
|
|
||||||
import 'package:injectable/injectable.dart';
|
|
||||||
|
|
||||||
import '../../../domain/category/category.dart';
|
|
||||||
import '../category_dtos.dart';
|
|
||||||
import '../datasources/local_data_provider.dart';
|
|
||||||
import '../datasources/remote_data_provider.dart';
|
|
||||||
|
|
||||||
@Injectable(as: ICategoryRepository)
|
|
||||||
class CategoryRepository implements ICategoryRepository {
|
|
||||||
final CategoryRemoteDataProvider _remoteDataProvider;
|
|
||||||
final CategoryLocalDataProvider _localDataProvider;
|
|
||||||
|
|
||||||
final _logName = 'CategoryRepository';
|
|
||||||
|
|
||||||
CategoryRepository(this._remoteDataProvider, this._localDataProvider);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Either<CategoryFailure, ListCategory>> getCategories({
|
|
||||||
int page = 1,
|
|
||||||
int limit = 10,
|
|
||||||
bool isActive = true,
|
|
||||||
String? search,
|
|
||||||
bool forceRemote = false,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
log(
|
|
||||||
'📱 Getting categories - page: $page, isActive: $isActive, search: $search, forceRemote: $forceRemote',
|
|
||||||
name: _logName,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Clean expired cache
|
|
||||||
_localDataProvider.clearExpiredCache();
|
|
||||||
|
|
||||||
// Check if we should try remote first
|
|
||||||
if (forceRemote || !await _localDataProvider.hasCategories()) {
|
|
||||||
log('🌐 Attempting remote fetch first...', name: _logName);
|
|
||||||
|
|
||||||
final remoteResult = await _getRemoteCategories(
|
|
||||||
page: page,
|
|
||||||
limit: limit,
|
|
||||||
isActive: isActive,
|
|
||||||
);
|
|
||||||
|
|
||||||
return await remoteResult.fold(
|
|
||||||
(failure) async {
|
|
||||||
log('❌ Remote fetch failed: $failure', name: _logName);
|
|
||||||
log('📱 Falling back to local data...', name: _logName);
|
|
||||||
return await _getLocalCategories(
|
|
||||||
page: page,
|
|
||||||
limit: limit,
|
|
||||||
isActive: isActive,
|
|
||||||
search: search,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(data) async {
|
|
||||||
log(
|
|
||||||
'✅ Remote fetch successful, syncing to local...',
|
|
||||||
name: _logName,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Sync remote data to local
|
|
||||||
if (data.categories.isNotEmpty) {
|
|
||||||
await _syncToLocal(data.categories, clearFirst: page == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Right(data);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
log('📱 Using local data (cache available)...', name: _logName);
|
|
||||||
return await _getLocalCategories(
|
|
||||||
page: page,
|
|
||||||
limit: limit,
|
|
||||||
isActive: isActive,
|
|
||||||
search: search,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e, s) {
|
|
||||||
log('❌ Error in getCategories', name: _logName, error: e, stackTrace: s);
|
|
||||||
return Left(
|
|
||||||
CategoryFailure.dynamicErrorMessage('Gagal memuat kategori: $e'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Either<CategoryFailure, Category>> getCategoryById(String id) async {
|
|
||||||
try {
|
|
||||||
log('🔍 Getting category by ID: $id', name: _logName);
|
|
||||||
|
|
||||||
final result = await _localDataProvider.getCategoryById(id);
|
|
||||||
|
|
||||||
if (result.hasData) {
|
|
||||||
final category = result.data!.toDomain();
|
|
||||||
log('✅ Category found: ${category.name}', name: _logName);
|
|
||||||
return Right(category);
|
|
||||||
} else {
|
|
||||||
log('❌ Category not found or error: ${result.error}', name: _logName);
|
|
||||||
return Left(result.error!);
|
|
||||||
}
|
|
||||||
} catch (e, s) {
|
|
||||||
log(
|
|
||||||
'❌ Error getting category by ID',
|
|
||||||
name: _logName,
|
|
||||||
error: e,
|
|
||||||
stackTrace: s,
|
|
||||||
);
|
|
||||||
return Left(
|
|
||||||
CategoryFailure.localStorageError('Gagal memuat kategori: $e'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Either<CategoryFailure, Map<String, dynamic>>>
|
|
||||||
getDatabaseStats() async {
|
|
||||||
try {
|
|
||||||
log('📊 Getting database stats...', name: _logName);
|
|
||||||
|
|
||||||
final result = await _localDataProvider.getDatabaseStats();
|
|
||||||
|
|
||||||
if (result.hasData) {
|
|
||||||
final stats = result.data!;
|
|
||||||
log('📊 Category database stats: $stats', name: _logName);
|
|
||||||
return Right(stats);
|
|
||||||
} else {
|
|
||||||
log('❌ Error getting stats: ${result.error}', name: _logName);
|
|
||||||
return Left(result.error!);
|
|
||||||
}
|
|
||||||
} catch (e, s) {
|
|
||||||
log(
|
|
||||||
'❌ Error getting database stats',
|
|
||||||
name: _logName,
|
|
||||||
error: e,
|
|
||||||
stackTrace: s,
|
|
||||||
);
|
|
||||||
return Left(
|
|
||||||
CategoryFailure.localStorageError(
|
|
||||||
'Gagal memuat statistik database: $e',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> hasLocalCategories() async {
|
|
||||||
final hasCategories = await _localDataProvider.hasCategories();
|
|
||||||
log('📊 Has local categories: $hasCategories', name: _logName);
|
|
||||||
return hasCategories;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Either<CategoryFailure, ListCategory>> refreshCategories({
|
|
||||||
bool isActive = true,
|
|
||||||
String? search,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
log('🔄 Refreshing categories...', name: _logName);
|
|
||||||
|
|
||||||
// Clear cache before refresh
|
|
||||||
_localDataProvider.clearCache();
|
|
||||||
|
|
||||||
return await getCategories(
|
|
||||||
page: 1,
|
|
||||||
limit: 10,
|
|
||||||
isActive: isActive,
|
|
||||||
search: search,
|
|
||||||
forceRemote: true, // Force remote refresh
|
|
||||||
);
|
|
||||||
} catch (e, s) {
|
|
||||||
log(
|
|
||||||
'❌ Error refreshing categories',
|
|
||||||
name: _logName,
|
|
||||||
error: e,
|
|
||||||
stackTrace: s,
|
|
||||||
);
|
|
||||||
return Left(
|
|
||||||
CategoryFailure.localStorageError('Gagal memperbarui kategori: $e'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Either<CategoryFailure, String>> syncAllCategories() async {
|
|
||||||
try {
|
|
||||||
log('🔄 Starting manual sync of all categories...', name: _logName);
|
|
||||||
|
|
||||||
int page = 1;
|
|
||||||
const limit = 50; // Higher limit for bulk sync
|
|
||||||
bool hasMore = true;
|
|
||||||
int totalSynced = 0;
|
|
||||||
|
|
||||||
// Clear local data first for fresh sync
|
|
||||||
await _localDataProvider.clearAllCategories();
|
|
||||||
|
|
||||||
while (hasMore) {
|
|
||||||
log('📄 Syncing page $page...');
|
|
||||||
|
|
||||||
final result = await _remoteDataProvider.fetchCategories(
|
|
||||||
page: page,
|
|
||||||
limit: limit,
|
|
||||||
// isActive: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// If fetchCategories returns DC directly, handle it directly
|
|
||||||
final data = result.data!.toDomain();
|
|
||||||
|
|
||||||
if (data.categories.isNotEmpty) {
|
|
||||||
await _localDataProvider.saveCategoriesBatch(
|
|
||||||
data.categories
|
|
||||||
.map((category) => CategoryDto.fromDomain(category))
|
|
||||||
.toList(),
|
|
||||||
clearFirst: false, // Don't clear on subsequent pages
|
|
||||||
);
|
|
||||||
totalSynced += data.categories.length;
|
|
||||||
|
|
||||||
// Check if we have more pages
|
|
||||||
hasMore = page < data.totalPages;
|
|
||||||
page++;
|
|
||||||
|
|
||||||
log(
|
|
||||||
'📦 Page ${page - 1} synced: ${data.categories.length} categories',
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
hasMore = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final message = 'Berhasil sinkronisasi $totalSynced kategori';
|
|
||||||
log('✅ $message');
|
|
||||||
return Right(message);
|
|
||||||
} catch (e) {
|
|
||||||
final error = 'Gagal sinkronisasi kategori: $e';
|
|
||||||
log('❌ $error');
|
|
||||||
return Left(CategoryFailure.localStorageError(error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Either<CategoryFailure, ListCategory>> _getRemoteCategories({
|
|
||||||
int page = 1,
|
|
||||||
int limit = 10,
|
|
||||||
bool isActive = true,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
log('🌐 Fetching categories from remote...', name: _logName);
|
|
||||||
|
|
||||||
final result = await _remoteDataProvider.fetchCategories(
|
|
||||||
page: page,
|
|
||||||
limit: limit,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Convert DC to Either and DTO to Domain
|
|
||||||
if (result.hasData) {
|
|
||||||
final categories = result.data!.toDomain();
|
|
||||||
return Right(categories);
|
|
||||||
} else {
|
|
||||||
return Left(result.error!);
|
|
||||||
}
|
|
||||||
} catch (e, s) {
|
|
||||||
log('❌ Remote fetch error', name: _logName, error: e, stackTrace: s);
|
|
||||||
return Left(
|
|
||||||
CategoryFailure.dynamicErrorMessage(
|
|
||||||
'Gagal mengambil data dari server: $e',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Either<CategoryFailure, ListCategory>> _getLocalCategories({
|
|
||||||
int page = 1,
|
|
||||||
int limit = 10,
|
|
||||||
bool isActive = true,
|
|
||||||
String? search,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
log('💾 Fetching categories from local...', name: _logName);
|
|
||||||
|
|
||||||
final result = await _localDataProvider.getCachedCategories(
|
|
||||||
page: page,
|
|
||||||
limit: limit,
|
|
||||||
isActive: isActive,
|
|
||||||
search: search,
|
|
||||||
);
|
|
||||||
|
|
||||||
final totalCount = await _localDataProvider.getTotalCount(
|
|
||||||
isActive: isActive,
|
|
||||||
search: search,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Convert DC to Either and DTO to Domain
|
|
||||||
if (result.hasData) {
|
|
||||||
final categories = result.data!.map((dto) => dto.toDomain()).toList();
|
|
||||||
final categoryData = ListCategory(
|
|
||||||
categories: categories,
|
|
||||||
totalCount: totalCount,
|
|
||||||
page: page,
|
|
||||||
limit: limit,
|
|
||||||
totalPages: totalCount > 0 ? (totalCount / limit).ceil() : 0,
|
|
||||||
);
|
|
||||||
log('✅ Returned ${categories.length} local categories', name: _logName);
|
|
||||||
return Right(categoryData);
|
|
||||||
} else {
|
|
||||||
log(
|
|
||||||
'❌ Error getting local categories: ${result.error}',
|
|
||||||
name: _logName,
|
|
||||||
);
|
|
||||||
return Left(result.error!);
|
|
||||||
}
|
|
||||||
} catch (e, s) {
|
|
||||||
log(
|
|
||||||
'❌ Error getting local categories',
|
|
||||||
name: _logName,
|
|
||||||
error: e,
|
|
||||||
stackTrace: s,
|
|
||||||
);
|
|
||||||
return Left(
|
|
||||||
CategoryFailure.localStorageError(
|
|
||||||
'Gagal memuat kategori dari database lokal: $e',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _syncToLocal(
|
|
||||||
List<Category> categories, {
|
|
||||||
bool clearFirst = false,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
log(
|
|
||||||
'💾 Syncing ${categories.length} categories to local database...',
|
|
||||||
name: _logName,
|
|
||||||
);
|
|
||||||
await _localDataProvider.saveCategoriesBatch(
|
|
||||||
categories.map((category) => CategoryDto.fromDomain(category)).toList(),
|
|
||||||
clearFirst: clearFirst,
|
|
||||||
);
|
|
||||||
log('✅ Categories synced to local successfully', name: _logName);
|
|
||||||
} catch (e) {
|
|
||||||
log('❌ Error syncing categories to local: $e', name: _logName);
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void clearCache() {
|
|
||||||
log('🧹 Clearing category cache', name: _logName);
|
|
||||||
_localDataProvider.clearCache();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -32,7 +32,7 @@ class OutletRemoteDataProvider {
|
|||||||
return DC.error(OutletFailure.empty());
|
return DC.error(OutletFailure.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
final outlets = (response.data['data']['outlets'] as List)
|
final outlets = (response.data['data'] as List)
|
||||||
.map((e) => OutletDto.fromJson(e as Map<String, dynamic>))
|
.map((e) => OutletDto.fromJson(e as Map<String, dynamic>))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
|||||||
@ -12,21 +12,15 @@
|
|||||||
import 'package:apskel_pos_flutter_v2/application/auth/auth_bloc.dart' as _i343;
|
import 'package:apskel_pos_flutter_v2/application/auth/auth_bloc.dart' as _i343;
|
||||||
import 'package:apskel_pos_flutter_v2/application/auth/login_form/login_form_bloc.dart'
|
import 'package:apskel_pos_flutter_v2/application/auth/login_form/login_form_bloc.dart'
|
||||||
as _i46;
|
as _i46;
|
||||||
import 'package:apskel_pos_flutter_v2/application/outlet/outlet_loader/outlet_loader_bloc.dart'
|
|
||||||
as _i76;
|
|
||||||
import 'package:apskel_pos_flutter_v2/common/api/api_client.dart' as _i457;
|
import 'package:apskel_pos_flutter_v2/common/api/api_client.dart' as _i457;
|
||||||
import 'package:apskel_pos_flutter_v2/common/database/database_helper.dart'
|
|
||||||
as _i487;
|
|
||||||
import 'package:apskel_pos_flutter_v2/common/di/di_auto_route.dart' as _i729;
|
import 'package:apskel_pos_flutter_v2/common/di/di_auto_route.dart' as _i729;
|
||||||
import 'package:apskel_pos_flutter_v2/common/di/di_connectivity.dart' as _i807;
|
import 'package:apskel_pos_flutter_v2/common/di/di_connectivity.dart' as _i807;
|
||||||
import 'package:apskel_pos_flutter_v2/common/di/di_database.dart' as _i209;
|
|
||||||
import 'package:apskel_pos_flutter_v2/common/di/di_dio.dart' as _i86;
|
import 'package:apskel_pos_flutter_v2/common/di/di_dio.dart' as _i86;
|
||||||
import 'package:apskel_pos_flutter_v2/common/di/di_shared_preferences.dart'
|
import 'package:apskel_pos_flutter_v2/common/di/di_shared_preferences.dart'
|
||||||
as _i135;
|
as _i135;
|
||||||
import 'package:apskel_pos_flutter_v2/common/network/network_client.dart'
|
import 'package:apskel_pos_flutter_v2/common/network/network_client.dart'
|
||||||
as _i171;
|
as _i171;
|
||||||
import 'package:apskel_pos_flutter_v2/domain/auth/auth.dart' as _i776;
|
import 'package:apskel_pos_flutter_v2/domain/auth/auth.dart' as _i776;
|
||||||
import 'package:apskel_pos_flutter_v2/domain/category/category.dart' as _i502;
|
|
||||||
import 'package:apskel_pos_flutter_v2/domain/outlet/outlet.dart' as _i552;
|
import 'package:apskel_pos_flutter_v2/domain/outlet/outlet.dart' as _i552;
|
||||||
import 'package:apskel_pos_flutter_v2/env.dart' as _i923;
|
import 'package:apskel_pos_flutter_v2/env.dart' as _i923;
|
||||||
import 'package:apskel_pos_flutter_v2/infrastructure/auth/datasources/local_data_provider.dart'
|
import 'package:apskel_pos_flutter_v2/infrastructure/auth/datasources/local_data_provider.dart'
|
||||||
@ -35,12 +29,6 @@ import 'package:apskel_pos_flutter_v2/infrastructure/auth/datasources/remote_dat
|
|||||||
as _i370;
|
as _i370;
|
||||||
import 'package:apskel_pos_flutter_v2/infrastructure/auth/repositories/auth_repository.dart'
|
import 'package:apskel_pos_flutter_v2/infrastructure/auth/repositories/auth_repository.dart'
|
||||||
as _i941;
|
as _i941;
|
||||||
import 'package:apskel_pos_flutter_v2/infrastructure/category/datasources/local_data_provider.dart'
|
|
||||||
as _i708;
|
|
||||||
import 'package:apskel_pos_flutter_v2/infrastructure/category/datasources/remote_data_provider.dart'
|
|
||||||
as _i856;
|
|
||||||
import 'package:apskel_pos_flutter_v2/infrastructure/category/repositories/category_repository.dart'
|
|
||||||
as _i604;
|
|
||||||
import 'package:apskel_pos_flutter_v2/infrastructure/outlet/datasources/local_data_provider.dart'
|
import 'package:apskel_pos_flutter_v2/infrastructure/outlet/datasources/local_data_provider.dart'
|
||||||
as _i693;
|
as _i693;
|
||||||
import 'package:apskel_pos_flutter_v2/infrastructure/outlet/datasources/remote_data_provider.dart'
|
import 'package:apskel_pos_flutter_v2/infrastructure/outlet/datasources/remote_data_provider.dart'
|
||||||
@ -66,7 +54,6 @@ extension GetItInjectableX on _i174.GetIt {
|
|||||||
}) async {
|
}) async {
|
||||||
final gh = _i526.GetItHelper(this, environment, environmentFilter);
|
final gh = _i526.GetItHelper(this, environment, environmentFilter);
|
||||||
final sharedPreferencesDi = _$SharedPreferencesDi();
|
final sharedPreferencesDi = _$SharedPreferencesDi();
|
||||||
final databaseDi = _$DatabaseDi();
|
|
||||||
final dioDi = _$DioDi();
|
final dioDi = _$DioDi();
|
||||||
final autoRouteDi = _$AutoRouteDi();
|
final autoRouteDi = _$AutoRouteDi();
|
||||||
final connectivityDi = _$ConnectivityDi();
|
final connectivityDi = _$ConnectivityDi();
|
||||||
@ -74,7 +61,6 @@ extension GetItInjectableX on _i174.GetIt {
|
|||||||
() => sharedPreferencesDi.prefs,
|
() => sharedPreferencesDi.prefs,
|
||||||
preResolve: true,
|
preResolve: true,
|
||||||
);
|
);
|
||||||
gh.singleton<_i487.DatabaseHelper>(() => databaseDi.databaseHelper);
|
|
||||||
gh.lazySingleton<_i361.Dio>(() => dioDi.dio);
|
gh.lazySingleton<_i361.Dio>(() => dioDi.dio);
|
||||||
gh.lazySingleton<_i800.AppRouter>(() => autoRouteDi.appRouter);
|
gh.lazySingleton<_i800.AppRouter>(() => autoRouteDi.appRouter);
|
||||||
gh.lazySingleton<_i895.Connectivity>(() => connectivityDi.connectivity);
|
gh.lazySingleton<_i895.Connectivity>(() => connectivityDi.connectivity);
|
||||||
@ -82,9 +68,6 @@ extension GetItInjectableX on _i174.GetIt {
|
|||||||
() => _i171.NetworkClient(gh<_i895.Connectivity>()),
|
() => _i171.NetworkClient(gh<_i895.Connectivity>()),
|
||||||
);
|
);
|
||||||
gh.factory<_i923.Env>(() => _i923.DevEnv(), registerFor: {_dev});
|
gh.factory<_i923.Env>(() => _i923.DevEnv(), registerFor: {_dev});
|
||||||
gh.factory<_i708.CategoryLocalDataProvider>(
|
|
||||||
() => _i708.CategoryLocalDataProvider(gh<_i487.DatabaseHelper>()),
|
|
||||||
);
|
|
||||||
gh.factory<_i204.AuthLocalDataProvider>(
|
gh.factory<_i204.AuthLocalDataProvider>(
|
||||||
() => _i204.AuthLocalDataProvider(gh<_i460.SharedPreferences>()),
|
() => _i204.AuthLocalDataProvider(gh<_i460.SharedPreferences>()),
|
||||||
);
|
);
|
||||||
@ -95,9 +78,6 @@ extension GetItInjectableX on _i174.GetIt {
|
|||||||
() => _i457.ApiClient(gh<_i361.Dio>(), gh<_i923.Env>()),
|
() => _i457.ApiClient(gh<_i361.Dio>(), gh<_i923.Env>()),
|
||||||
);
|
);
|
||||||
gh.factory<_i923.Env>(() => _i923.ProdEnv(), registerFor: {_prod});
|
gh.factory<_i923.Env>(() => _i923.ProdEnv(), registerFor: {_prod});
|
||||||
gh.factory<_i856.CategoryRemoteDataProvider>(
|
|
||||||
() => _i856.CategoryRemoteDataProvider(gh<_i457.ApiClient>()),
|
|
||||||
);
|
|
||||||
gh.factory<_i370.AuthRemoteDataProvider>(
|
gh.factory<_i370.AuthRemoteDataProvider>(
|
||||||
() => _i370.AuthRemoteDataProvider(gh<_i457.ApiClient>()),
|
() => _i370.AuthRemoteDataProvider(gh<_i457.ApiClient>()),
|
||||||
);
|
);
|
||||||
@ -110,12 +90,6 @@ extension GetItInjectableX on _i174.GetIt {
|
|||||||
gh<_i204.AuthLocalDataProvider>(),
|
gh<_i204.AuthLocalDataProvider>(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
gh.factory<_i502.ICategoryRepository>(
|
|
||||||
() => _i604.CategoryRepository(
|
|
||||||
gh<_i856.CategoryRemoteDataProvider>(),
|
|
||||||
gh<_i708.CategoryLocalDataProvider>(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
gh.factory<_i552.IOutletRepository>(
|
gh.factory<_i552.IOutletRepository>(
|
||||||
() => _i845.OutletRepository(
|
() => _i845.OutletRepository(
|
||||||
gh<_i132.OutletRemoteDataProvider>(),
|
gh<_i132.OutletRemoteDataProvider>(),
|
||||||
@ -131,17 +105,12 @@ extension GetItInjectableX on _i174.GetIt {
|
|||||||
gh<_i552.IOutletRepository>(),
|
gh<_i552.IOutletRepository>(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
gh.factory<_i76.OutletLoaderBloc>(
|
|
||||||
() => _i76.OutletLoaderBloc(gh<_i552.IOutletRepository>()),
|
|
||||||
);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _$SharedPreferencesDi extends _i135.SharedPreferencesDi {}
|
class _$SharedPreferencesDi extends _i135.SharedPreferencesDi {}
|
||||||
|
|
||||||
class _$DatabaseDi extends _i209.DatabaseDi {}
|
|
||||||
|
|
||||||
class _$DioDi extends _i86.DioDi {}
|
class _$DioDi extends _i86.DioDi {}
|
||||||
|
|
||||||
class _$AutoRouteDi extends _i729.AutoRouteDi {}
|
class _$AutoRouteDi extends _i729.AutoRouteDi {}
|
||||||
|
|||||||
@ -2,8 +2,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
import '../application/auth/auth_bloc.dart';
|
import '../application/auth/auth_bloc.dart';
|
||||||
import '../application/category/category_loader/category_loader_bloc.dart';
|
|
||||||
import '../application/outlet/outlet_loader/outlet_loader_bloc.dart';
|
|
||||||
import '../common/theme/theme.dart';
|
import '../common/theme/theme.dart';
|
||||||
import '../common/constant/app_constant.dart';
|
import '../common/constant/app_constant.dart';
|
||||||
import '../injection.dart';
|
import '../injection.dart';
|
||||||
@ -22,12 +20,8 @@ class _AppWidgetState extends State<AppWidget> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiBlocProvider(
|
return BlocProvider(
|
||||||
providers: [
|
create: (context) => getIt<AuthBloc>(),
|
||||||
BlocProvider(create: (context) => getIt<AuthBloc>()),
|
|
||||||
BlocProvider(create: (context) => getIt<OutletLoaderBloc>()),
|
|
||||||
BlocProvider(create: (context) => getIt<CategoryLoaderBloc>()),
|
|
||||||
],
|
|
||||||
child: MaterialApp.router(
|
child: MaterialApp.router(
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
title: AppConstant.appName,
|
title: AppConstant.appName,
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import '../../../common/theme/theme.dart';
|
|
||||||
import '../../../domain/outlet/outlet.dart';
|
|
||||||
import '../spaces/space.dart';
|
|
||||||
|
|
||||||
class OutletCard extends StatelessWidget {
|
|
||||||
final Outlet outlet;
|
|
||||||
final bool isSelected;
|
|
||||||
const OutletCard({super.key, required this.outlet, required this.isSelected});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
|
|
||||||
margin: EdgeInsets.only(bottom: 12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: isSelected ? AppColor.primary.withOpacity(0.1) : AppColor.white,
|
|
||||||
border: Border.all(color: AppColor.primary),
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(Icons.store, color: AppColor.primary),
|
|
||||||
SpaceWidth(12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
outlet.name,
|
|
||||||
style: AppStyle.lg.copyWith(fontWeight: FontWeight.w600),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
outlet.address,
|
|
||||||
style: AppStyle.sm,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 1,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
part of 'dialog.dart';
|
|
||||||
|
|
||||||
class CustomModalDialog extends StatelessWidget {
|
|
||||||
final String title;
|
|
||||||
final String? subtitle;
|
|
||||||
final Widget child;
|
|
||||||
final VoidCallback? onClose;
|
|
||||||
final double? minWidth;
|
|
||||||
final double? maxWidth;
|
|
||||||
final double? minHeight;
|
|
||||||
final double? maxHeight;
|
|
||||||
final EdgeInsets? contentPadding;
|
|
||||||
|
|
||||||
const CustomModalDialog({
|
|
||||||
super.key,
|
|
||||||
required this.title,
|
|
||||||
this.subtitle,
|
|
||||||
required this.child,
|
|
||||||
this.onClose,
|
|
||||||
this.minWidth,
|
|
||||||
this.maxWidth,
|
|
||||||
this.minHeight,
|
|
||||||
this.maxHeight,
|
|
||||||
this.contentPadding,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Dialog(
|
|
||||||
backgroundColor: AppColor.white,
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
minWidth: minWidth ?? context.deviceWidth * 0.3,
|
|
||||||
maxWidth: maxWidth ?? context.deviceWidth * 0.8,
|
|
||||||
minHeight: minHeight ?? context.deviceHeight * 0.3,
|
|
||||||
maxHeight: maxHeight ?? context.deviceHeight * 0.8,
|
|
||||||
),
|
|
||||||
child: IntrinsicWidth(
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
width: double.infinity,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: AppColor.primaryGradient,
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: AppStyle.xxl.copyWith(
|
|
||||||
color: AppColor.white,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (subtitle != null)
|
|
||||||
Text(
|
|
||||||
subtitle ?? '',
|
|
||||||
style: AppStyle.lg.copyWith(
|
|
||||||
color: AppColor.textSecondary,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SpaceWidth(12),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.close, color: AppColor.white),
|
|
||||||
onPressed: () {
|
|
||||||
if (onClose != null) {
|
|
||||||
onClose!();
|
|
||||||
} else {
|
|
||||||
context.maybePop();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Flexible(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
padding: contentPadding ?? EdgeInsets.zero,
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
|
|
||||||
import '../../../application/outlet/outlet_loader/outlet_loader_bloc.dart';
|
|
||||||
import '../../../common/extension/extension.dart';
|
|
||||||
import '../../../common/theme/theme.dart';
|
|
||||||
import '../button/button.dart';
|
|
||||||
import '../card/outlet_card.dart';
|
|
||||||
import '../loader/loader_with_text.dart';
|
|
||||||
import '../spaces/space.dart';
|
|
||||||
|
|
||||||
part 'custom_modal_dialog.dart';
|
|
||||||
part 'outlet_dialog.dart';
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
part of 'dialog.dart';
|
|
||||||
|
|
||||||
class OutletDialog extends StatefulWidget {
|
|
||||||
const OutletDialog({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<OutletDialog> createState() => _OutletDialogState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _OutletDialogState extends State<OutletDialog> {
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
context.read<OutletLoaderBloc>().add(
|
|
||||||
OutletLoaderEvent.fetched(isRefresh: true),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return CustomModalDialog(
|
|
||||||
title: 'Outlet',
|
|
||||||
subtitle: 'Silahkan pilih outlet',
|
|
||||||
minWidth: context.deviceWidth * 0.4,
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16.0,
|
|
||||||
vertical: 24.0,
|
|
||||||
),
|
|
||||||
child: BlocBuilder<OutletLoaderBloc, OutletLoaderState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
if (state.isFetching) {
|
|
||||||
return LoaderWithText();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
...List.generate(
|
|
||||||
state.outlets.length,
|
|
||||||
(index) => GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
// selectOutlet(outlets[index]);
|
|
||||||
},
|
|
||||||
child: OutletCard(
|
|
||||||
outlet: state.outlets[index],
|
|
||||||
isSelected: false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SpaceHeight(24),
|
|
||||||
AppElevatedButton.filled(onPressed: null, label: 'Terapkan'),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,7 +12,7 @@ class LoaderWithText extends StatelessWidget {
|
|||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
SpinKitFadingCircle(color: AppColor.primary, size: 24),
|
SpinKitFadingCircle(color: AppColor.primary),
|
||||||
SpaceWidth(10),
|
SpaceWidth(10),
|
||||||
Text(
|
Text(
|
||||||
'Loading...',
|
'Loading...',
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import '../../../../../../application/auth/auth_bloc.dart';
|
import '../../../../../../application/auth/auth_bloc.dart';
|
||||||
import '../../../../../../common/extension/extension.dart';
|
import '../../../../../../common/extension/extension.dart';
|
||||||
import '../../../../../../common/theme/theme.dart';
|
import '../../../../../../common/theme/theme.dart';
|
||||||
import '../../../../../components/dialog/dialog.dart';
|
|
||||||
import '../../../../../components/field/field.dart';
|
import '../../../../../components/field/field.dart';
|
||||||
import '../../../../../components/spaces/space.dart';
|
import '../../../../../components/spaces/space.dart';
|
||||||
|
|
||||||
@ -24,12 +23,7 @@ class HomeTitle extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
BlocBuilder<AuthBloc, AuthState>(
|
BlocBuilder<AuthBloc, AuthState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return GestureDetector(
|
return Row(
|
||||||
onTap: () => showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => OutletDialog(),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
@ -48,7 +42,6 @@ class HomeTitle extends StatelessWidget {
|
|||||||
size: 18,
|
size: 18,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@ -35,7 +35,7 @@ class _SplashPageState extends State<SplashPage> {
|
|||||||
listenWhen: (previous, current) => previous.status != current.status,
|
listenWhen: (previous, current) => previous.status != current.status,
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
if (state.isAuthenticated) {
|
if (state.isAuthenticated) {
|
||||||
context.router.replace(const SyncRoute());
|
context.router.replace(const MainRoute());
|
||||||
} else {
|
} else {
|
||||||
context.router.replace(const LoginRoute());
|
context.router.replace(const LoginRoute());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,71 +0,0 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import '../../../common/extension/extension.dart';
|
|
||||||
import '../../../common/theme/theme.dart';
|
|
||||||
import '../../components/spaces/space.dart';
|
|
||||||
|
|
||||||
@RoutePage()
|
|
||||||
class SyncPage extends StatelessWidget {
|
|
||||||
const SyncPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: AppColor.background,
|
|
||||||
body: SafeArea(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [_buildHeader()],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SpaceWidth(40),
|
|
||||||
SizedBox(width: 40),
|
|
||||||
Expanded(
|
|
||||||
flex: 3,
|
|
||||||
child: Container(height: context.deviceHeight * 0.8),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildHeader() {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 60,
|
|
||||||
height: 60,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: AppColor.primary.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
),
|
|
||||||
child: Icon(Icons.sync, size: 30, color: AppColor.primary),
|
|
||||||
),
|
|
||||||
SizedBox(height: 12),
|
|
||||||
Text(
|
|
||||||
'Sinkronisasi Data',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.grey.shade800,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
'Mengunduh kategori dan produk terbaru',
|
|
||||||
style: TextStyle(fontSize: 16, color: Colors.grey.shade600),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -22,8 +22,5 @@ class AppRouter extends RootStackRouter {
|
|||||||
AutoRoute(page: SettingRoute.page),
|
AutoRoute(page: SettingRoute.page),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
// Sync
|
|
||||||
AutoRoute(page: SyncRoute.page),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,22 +22,20 @@ import 'package:apskel_pos_flutter_v2/presentation/pages/main/pages/report/repor
|
|||||||
import 'package:apskel_pos_flutter_v2/presentation/pages/main/pages/setting/setting_page.dart'
|
import 'package:apskel_pos_flutter_v2/presentation/pages/main/pages/setting/setting_page.dart'
|
||||||
as _i6;
|
as _i6;
|
||||||
import 'package:apskel_pos_flutter_v2/presentation/pages/main/pages/table/table_page.dart'
|
import 'package:apskel_pos_flutter_v2/presentation/pages/main/pages/table/table_page.dart'
|
||||||
as _i9;
|
as _i8;
|
||||||
import 'package:apskel_pos_flutter_v2/presentation/pages/splash/splash_page.dart'
|
import 'package:apskel_pos_flutter_v2/presentation/pages/splash/splash_page.dart'
|
||||||
as _i7;
|
as _i7;
|
||||||
import 'package:apskel_pos_flutter_v2/presentation/pages/sync/sync_page.dart'
|
import 'package:auto_route/auto_route.dart' as _i9;
|
||||||
as _i8;
|
|
||||||
import 'package:auto_route/auto_route.dart' as _i10;
|
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i1.CustomerPage]
|
/// [_i1.CustomerPage]
|
||||||
class CustomerRoute extends _i10.PageRouteInfo<void> {
|
class CustomerRoute extends _i9.PageRouteInfo<void> {
|
||||||
const CustomerRoute({List<_i10.PageRouteInfo>? children})
|
const CustomerRoute({List<_i9.PageRouteInfo>? children})
|
||||||
: super(CustomerRoute.name, initialChildren: children);
|
: super(CustomerRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'CustomerRoute';
|
static const String name = 'CustomerRoute';
|
||||||
|
|
||||||
static _i10.PageInfo page = _i10.PageInfo(
|
static _i9.PageInfo page = _i9.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i1.CustomerPage();
|
return const _i1.CustomerPage();
|
||||||
@ -47,13 +45,13 @@ class CustomerRoute extends _i10.PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i2.HomePage]
|
/// [_i2.HomePage]
|
||||||
class HomeRoute extends _i10.PageRouteInfo<void> {
|
class HomeRoute extends _i9.PageRouteInfo<void> {
|
||||||
const HomeRoute({List<_i10.PageRouteInfo>? children})
|
const HomeRoute({List<_i9.PageRouteInfo>? children})
|
||||||
: super(HomeRoute.name, initialChildren: children);
|
: super(HomeRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'HomeRoute';
|
static const String name = 'HomeRoute';
|
||||||
|
|
||||||
static _i10.PageInfo page = _i10.PageInfo(
|
static _i9.PageInfo page = _i9.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i2.HomePage();
|
return const _i2.HomePage();
|
||||||
@ -63,29 +61,29 @@ class HomeRoute extends _i10.PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i3.LoginPage]
|
/// [_i3.LoginPage]
|
||||||
class LoginRoute extends _i10.PageRouteInfo<void> {
|
class LoginRoute extends _i9.PageRouteInfo<void> {
|
||||||
const LoginRoute({List<_i10.PageRouteInfo>? children})
|
const LoginRoute({List<_i9.PageRouteInfo>? children})
|
||||||
: super(LoginRoute.name, initialChildren: children);
|
: super(LoginRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'LoginRoute';
|
static const String name = 'LoginRoute';
|
||||||
|
|
||||||
static _i10.PageInfo page = _i10.PageInfo(
|
static _i9.PageInfo page = _i9.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return _i10.WrappedRoute(child: const _i3.LoginPage());
|
return _i9.WrappedRoute(child: const _i3.LoginPage());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i4.MainPage]
|
/// [_i4.MainPage]
|
||||||
class MainRoute extends _i10.PageRouteInfo<void> {
|
class MainRoute extends _i9.PageRouteInfo<void> {
|
||||||
const MainRoute({List<_i10.PageRouteInfo>? children})
|
const MainRoute({List<_i9.PageRouteInfo>? children})
|
||||||
: super(MainRoute.name, initialChildren: children);
|
: super(MainRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'MainRoute';
|
static const String name = 'MainRoute';
|
||||||
|
|
||||||
static _i10.PageInfo page = _i10.PageInfo(
|
static _i9.PageInfo page = _i9.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i4.MainPage();
|
return const _i4.MainPage();
|
||||||
@ -95,13 +93,13 @@ class MainRoute extends _i10.PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i5.ReportPage]
|
/// [_i5.ReportPage]
|
||||||
class ReportRoute extends _i10.PageRouteInfo<void> {
|
class ReportRoute extends _i9.PageRouteInfo<void> {
|
||||||
const ReportRoute({List<_i10.PageRouteInfo>? children})
|
const ReportRoute({List<_i9.PageRouteInfo>? children})
|
||||||
: super(ReportRoute.name, initialChildren: children);
|
: super(ReportRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'ReportRoute';
|
static const String name = 'ReportRoute';
|
||||||
|
|
||||||
static _i10.PageInfo page = _i10.PageInfo(
|
static _i9.PageInfo page = _i9.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i5.ReportPage();
|
return const _i5.ReportPage();
|
||||||
@ -111,13 +109,13 @@ class ReportRoute extends _i10.PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i6.SettingPage]
|
/// [_i6.SettingPage]
|
||||||
class SettingRoute extends _i10.PageRouteInfo<void> {
|
class SettingRoute extends _i9.PageRouteInfo<void> {
|
||||||
const SettingRoute({List<_i10.PageRouteInfo>? children})
|
const SettingRoute({List<_i9.PageRouteInfo>? children})
|
||||||
: super(SettingRoute.name, initialChildren: children);
|
: super(SettingRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'SettingRoute';
|
static const String name = 'SettingRoute';
|
||||||
|
|
||||||
static _i10.PageInfo page = _i10.PageInfo(
|
static _i9.PageInfo page = _i9.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i6.SettingPage();
|
return const _i6.SettingPage();
|
||||||
@ -127,13 +125,13 @@ class SettingRoute extends _i10.PageRouteInfo<void> {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i7.SplashPage]
|
/// [_i7.SplashPage]
|
||||||
class SplashRoute extends _i10.PageRouteInfo<void> {
|
class SplashRoute extends _i9.PageRouteInfo<void> {
|
||||||
const SplashRoute({List<_i10.PageRouteInfo>? children})
|
const SplashRoute({List<_i9.PageRouteInfo>? children})
|
||||||
: super(SplashRoute.name, initialChildren: children);
|
: super(SplashRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'SplashRoute';
|
static const String name = 'SplashRoute';
|
||||||
|
|
||||||
static _i10.PageInfo page = _i10.PageInfo(
|
static _i9.PageInfo page = _i9.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i7.SplashPage();
|
return const _i7.SplashPage();
|
||||||
@ -142,33 +140,17 @@ class SplashRoute extends _i10.PageRouteInfo<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [_i8.SyncPage]
|
/// [_i8.TablePage]
|
||||||
class SyncRoute extends _i10.PageRouteInfo<void> {
|
class TableRoute extends _i9.PageRouteInfo<void> {
|
||||||
const SyncRoute({List<_i10.PageRouteInfo>? children})
|
const TableRoute({List<_i9.PageRouteInfo>? children})
|
||||||
: super(SyncRoute.name, initialChildren: children);
|
|
||||||
|
|
||||||
static const String name = 'SyncRoute';
|
|
||||||
|
|
||||||
static _i10.PageInfo page = _i10.PageInfo(
|
|
||||||
name,
|
|
||||||
builder: (data) {
|
|
||||||
return const _i8.SyncPage();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// generated route for
|
|
||||||
/// [_i9.TablePage]
|
|
||||||
class TableRoute extends _i10.PageRouteInfo<void> {
|
|
||||||
const TableRoute({List<_i10.PageRouteInfo>? children})
|
|
||||||
: super(TableRoute.name, initialChildren: children);
|
: super(TableRoute.name, initialChildren: children);
|
||||||
|
|
||||||
static const String name = 'TableRoute';
|
static const String name = 'TableRoute';
|
||||||
|
|
||||||
static _i10.PageInfo page = _i10.PageInfo(
|
static _i9.PageInfo page = _i9.PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const _i9.TablePage();
|
return const _i8.TablePage();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import firebase_core
|
|||||||
import firebase_crashlytics
|
import firebase_crashlytics
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import sqflite_darwin
|
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||||
@ -18,5 +17,4 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin"))
|
FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
|
||||||
}
|
}
|
||||||
|
|||||||
48
pubspec.lock
48
pubspec.lock
@ -949,46 +949,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.1"
|
version: "1.10.1"
|
||||||
sqflite:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: sqflite
|
|
||||||
sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.2"
|
|
||||||
sqflite_android:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sqflite_android
|
|
||||||
sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.2+2"
|
|
||||||
sqflite_common:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sqflite_common
|
|
||||||
sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.5.6"
|
|
||||||
sqflite_darwin:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sqflite_darwin
|
|
||||||
sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.2"
|
|
||||||
sqflite_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sqflite_platform_interface
|
|
||||||
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.0"
|
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1021,14 +981,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.1"
|
version: "1.4.1"
|
||||||
synchronized:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: synchronized
|
|
||||||
sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.4.0"
|
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -33,7 +33,6 @@ dependencies:
|
|||||||
flutter_spinkit: ^5.2.2
|
flutter_spinkit: ^5.2.2
|
||||||
bloc: ^9.1.0
|
bloc: ^9.1.0
|
||||||
flutter_bloc: ^9.1.1
|
flutter_bloc: ^9.1.1
|
||||||
sqflite: ^2.4.2
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user