feat: legacy mobile apps created
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow/core/application/di/injectable.dart';
|
||||
import 'package:krow/core/data/enums/pagination_status.dart';
|
||||
import 'package:krow/core/data/enums/state_status.dart';
|
||||
import 'package:krow/features/earning/domain/entities/earning_shift_entity.dart';
|
||||
import 'package:krow/features/earning/domain/entities/earnings_summary_entity.dart';
|
||||
import 'package:krow/features/earning/domain/staff_earning_repository.dart';
|
||||
|
||||
part 'earnings_data.dart';
|
||||
part 'earnings_event.dart';
|
||||
part 'earnings_state.dart';
|
||||
|
||||
class EarningsBloc extends Bloc<EarningsEvent, EarningsState> {
|
||||
EarningsBloc()
|
||||
: super(const EarningsState.initial()
|
||||
..copyWith(
|
||||
tabs: defaultEarningTabs,
|
||||
)) {
|
||||
on<EarningsInitEvent>(_onInit);
|
||||
on<EarningsSwitchBalancePeriodEvent>(_onSwitchBalancePeriod);
|
||||
on<EarningsTabChangedEvent>(_onTabChanged);
|
||||
on<LoadAdditionalEarnings>(_onLoadAdditionalEarnings);
|
||||
on<ReloadCurrentEarnings>(_onReloadCurrentEarnings);
|
||||
on<ConfirmStaffEarning>(_onConfirmStaffEarning);
|
||||
on<DisputeStaffEarning>(_onDisputeStaffEarning);
|
||||
}
|
||||
|
||||
final _earningRepository = getIt<StaffEarningRepository>();
|
||||
|
||||
Future<void> _onInit(
|
||||
EarningsInitEvent event,
|
||||
Emitter<EarningsState> emit,
|
||||
) async {
|
||||
add(const LoadAdditionalEarnings());
|
||||
|
||||
emit(state.copyWith(status: StateStatus.loading));
|
||||
EarningsSummaryEntity? earningsData;
|
||||
try {
|
||||
earningsData = await _earningRepository.getStaffEarningsData();
|
||||
} catch (except) {
|
||||
log('Error in EarningsBloc, on EarningsInitEvent', error: except);
|
||||
}
|
||||
emit(state.copyWith(earnings: earningsData, status: StateStatus.idle));
|
||||
}
|
||||
|
||||
void _onSwitchBalancePeriod(
|
||||
EarningsSwitchBalancePeriodEvent event,
|
||||
Emitter<EarningsState> emit,
|
||||
) {
|
||||
emit(state.copyWith(balancePeriod: event.period));
|
||||
}
|
||||
|
||||
void _onTabChanged(
|
||||
EarningsTabChangedEvent event,
|
||||
Emitter<EarningsState> emit,
|
||||
) {
|
||||
emit(state.copyWith(tabIndex: event.tabIndex));
|
||||
|
||||
if (state.currentTab.status == PaginationStatus.initial) {
|
||||
add(const LoadAdditionalEarnings());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onLoadAdditionalEarnings(
|
||||
LoadAdditionalEarnings event,
|
||||
Emitter<EarningsState> emit,
|
||||
) async {
|
||||
if (!state.currentTab.status.allowLoad) return;
|
||||
emit(state.updateCurrentTab(status: PaginationStatus.loading));
|
||||
|
||||
final tabIndex = state.tabIndex;
|
||||
try {
|
||||
final earningsBatch = await _earningRepository.getEarningsBatch(
|
||||
status: state.currentTab.loadingKey,
|
||||
lastEntryCursor: state.currentTab.paginationCursor,
|
||||
);
|
||||
|
||||
emit(
|
||||
state.copyWithTab(
|
||||
tabIndex: tabIndex,
|
||||
tab: state.tabs[tabIndex].copyWith(
|
||||
status: earningsBatch.hasNextBatch
|
||||
? PaginationStatus.idle
|
||||
: PaginationStatus.end,
|
||||
items: [...state.tabs[tabIndex].items, ...earningsBatch.earnings],
|
||||
hasMoreItems: earningsBatch.hasNextBatch,
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (except) {
|
||||
log(
|
||||
'Error in EarningsBloc, on LoadAdditionalEarnings',
|
||||
error: except,
|
||||
);
|
||||
emit(
|
||||
state.copyWithTab(
|
||||
tabIndex: tabIndex,
|
||||
tab: state.tabs[tabIndex].copyWith(
|
||||
status: PaginationStatus.error,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (state.tabs[tabIndex].status == PaginationStatus.loading) {
|
||||
emit(
|
||||
state.copyWithTab(
|
||||
tabIndex: tabIndex,
|
||||
tab: state.tabs[tabIndex].copyWith(
|
||||
status: PaginationStatus.idle,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _onReloadCurrentEarnings(
|
||||
ReloadCurrentEarnings event,
|
||||
Emitter<EarningsState> emit,
|
||||
) {
|
||||
emit(
|
||||
state.copyWithTab(
|
||||
tab: EarningsTabState.initial(
|
||||
label: state.currentTab.label,
|
||||
loadingKey: state.currentTab.loadingKey,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
add(const LoadAdditionalEarnings());
|
||||
}
|
||||
|
||||
Future<void> _onEarningAction(
|
||||
Future<EarningShiftEntity?> earningFuture,
|
||||
String eventName,
|
||||
Emitter<EarningsState> emit,
|
||||
) async {
|
||||
emit(state.copyWith(status: StateStatus.loading));
|
||||
|
||||
EarningShiftEntity? earning;
|
||||
try {
|
||||
earning = await earningFuture;
|
||||
} catch (except) {
|
||||
log(
|
||||
'Error in EarningsBloc, on $eventName',
|
||||
error: except,
|
||||
);
|
||||
}
|
||||
|
||||
var tabData = state.currentTab;
|
||||
if (earning != null) {
|
||||
final earningIndex =
|
||||
state.currentTab.items.indexWhere((item) => item.id == earning?.id);
|
||||
|
||||
if (earningIndex >= 0) {
|
||||
tabData = state.currentTab.copyWith(
|
||||
items: List.from(state.currentTab.items)..[earningIndex] = earning,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
emit(
|
||||
state.copyWithTab(
|
||||
status: StateStatus.idle,
|
||||
tab: tabData,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onConfirmStaffEarning(
|
||||
ConfirmStaffEarning event,
|
||||
Emitter<EarningsState> emit,
|
||||
) {
|
||||
return _onEarningAction(
|
||||
_earningRepository.confirmStaffEarning(
|
||||
earningId: event.earningId,
|
||||
),
|
||||
'ConfirmStaffEarning',
|
||||
emit,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onDisputeStaffEarning(
|
||||
DisputeStaffEarning event,
|
||||
Emitter<EarningsState> emit,
|
||||
) async {
|
||||
return _onEarningAction(
|
||||
_earningRepository.disputeStaffEarning(
|
||||
id: event.earningId,
|
||||
reason: event.reason,
|
||||
details: event.details,
|
||||
),
|
||||
'DeclineStaffEarning',
|
||||
emit,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
part of 'earnings_bloc.dart';
|
||||
|
||||
enum BalancePeriod {
|
||||
week,
|
||||
month,
|
||||
}
|
||||
|
||||
class EarningsTabState {
|
||||
const EarningsTabState({
|
||||
required this.label,
|
||||
required this.loadingKey,
|
||||
required this.items,
|
||||
this.status = PaginationStatus.idle,
|
||||
this.hasMoreItems = true,
|
||||
this.paginationCursor,
|
||||
});
|
||||
|
||||
const EarningsTabState.initial({
|
||||
required this.label,
|
||||
required this.loadingKey,
|
||||
this.items = const [],
|
||||
this.status = PaginationStatus.initial,
|
||||
this.hasMoreItems = true,
|
||||
this.paginationCursor,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final String loadingKey;
|
||||
final List<EarningShiftEntity> items;
|
||||
final PaginationStatus status;
|
||||
final bool hasMoreItems;
|
||||
final String? paginationCursor;
|
||||
|
||||
EarningsTabState copyWith({
|
||||
List<EarningShiftEntity>? items,
|
||||
PaginationStatus? status,
|
||||
bool? hasMoreItems,
|
||||
String? label,
|
||||
String? loadingKey,
|
||||
String? paginationCursor,
|
||||
}) {
|
||||
return EarningsTabState(
|
||||
loadingKey: loadingKey ?? this.loadingKey,
|
||||
items: items ?? this.items,
|
||||
status: status ?? this.status,
|
||||
hasMoreItems: hasMoreItems ?? this.hasMoreItems,
|
||||
label: label ?? this.label,
|
||||
paginationCursor: paginationCursor ?? this.paginationCursor,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const defaultEarningTabs = [
|
||||
EarningsTabState.initial(
|
||||
label: 'new_earning',
|
||||
loadingKey: 'new' // Confirmed by admin
|
||||
),
|
||||
EarningsTabState.initial(
|
||||
label: 'confirmed',
|
||||
loadingKey: 'confirmed', // Confirmed by staff
|
||||
),
|
||||
EarningsTabState.initial(
|
||||
label: 'disputed',
|
||||
loadingKey: 'disputed', // Declined by staff
|
||||
),
|
||||
EarningsTabState.initial(
|
||||
label: 'sent',
|
||||
loadingKey: 'sent', // Should remain sent
|
||||
),
|
||||
EarningsTabState.initial(
|
||||
label: 'received',
|
||||
loadingKey: 'paid', // Should remain paid
|
||||
),
|
||||
];
|
||||
|
||||
// $of = match ($status) {
|
||||
// 'new' => fn(Builder $q) => $q->whereIn('status', [StaffPaymentStatus::new]),
|
||||
// 'confirmed' => fn(Builder $q) => $q->whereIn('status', [StaffPaymentStatus::confirmed_by_admin]),
|
||||
// 'pending' => fn(Builder $q) => $q->whereIn('status', [
|
||||
// StaffPaymentStatus::decline_by_staff,
|
||||
// StaffPaymentStatus::confirmed_by_staff,
|
||||
// ]),
|
||||
// 'sent' => fn(Builder $q) => $q->whereIn('status', [StaffPaymentStatus::sent]),
|
||||
// 'paid' => fn(Builder $q) => $q->whereIn('status', [StaffPaymentStatus::paid]),
|
||||
// };
|
||||
@@ -0,0 +1,48 @@
|
||||
part of 'earnings_bloc.dart';
|
||||
|
||||
@immutable
|
||||
sealed class EarningsEvent {
|
||||
const EarningsEvent();
|
||||
}
|
||||
|
||||
class EarningsInitEvent extends EarningsEvent {
|
||||
const EarningsInitEvent();
|
||||
}
|
||||
|
||||
class EarningsSwitchBalancePeriodEvent extends EarningsEvent {
|
||||
final BalancePeriod period;
|
||||
|
||||
const EarningsSwitchBalancePeriodEvent({required this.period});
|
||||
}
|
||||
|
||||
class EarningsTabChangedEvent extends EarningsEvent {
|
||||
final int tabIndex;
|
||||
|
||||
const EarningsTabChangedEvent({required this.tabIndex});
|
||||
}
|
||||
|
||||
class LoadAdditionalEarnings extends EarningsEvent {
|
||||
const LoadAdditionalEarnings();
|
||||
}
|
||||
|
||||
class ReloadCurrentEarnings extends EarningsEvent {
|
||||
const ReloadCurrentEarnings();
|
||||
}
|
||||
|
||||
class ConfirmStaffEarning extends EarningsEvent {
|
||||
const ConfirmStaffEarning(this.earningId);
|
||||
|
||||
final String earningId;
|
||||
}
|
||||
|
||||
class DisputeStaffEarning extends EarningsEvent {
|
||||
const DisputeStaffEarning({
|
||||
required this.earningId,
|
||||
required this.reason,
|
||||
required this.details,
|
||||
});
|
||||
|
||||
final String earningId;
|
||||
final String reason;
|
||||
final String details;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
part of 'earnings_bloc.dart';
|
||||
|
||||
@immutable
|
||||
class EarningsState {
|
||||
const EarningsState({
|
||||
required this.status,
|
||||
required this.tabIndex,
|
||||
required this.balancePeriod,
|
||||
required this.earnings,
|
||||
required this.tabs,
|
||||
});
|
||||
|
||||
const EarningsState.initial({
|
||||
this.status = StateStatus.idle,
|
||||
this.tabIndex = 0,
|
||||
this.balancePeriod = BalancePeriod.week,
|
||||
this.earnings = const EarningsSummaryEntity.empty(),
|
||||
this.tabs = defaultEarningTabs,
|
||||
});
|
||||
|
||||
final StateStatus status;
|
||||
final int tabIndex;
|
||||
final BalancePeriod balancePeriod;
|
||||
final EarningsSummaryEntity earnings;
|
||||
final List<EarningsTabState> tabs;
|
||||
|
||||
EarningsTabState get currentTab => tabs[tabIndex];
|
||||
|
||||
double get totalEarnings {
|
||||
return balancePeriod == BalancePeriod.week
|
||||
? earnings.totalEarningsByWeek
|
||||
: earnings.totalEarningsByMonth;
|
||||
}
|
||||
|
||||
double get totalHours {
|
||||
return balancePeriod == BalancePeriod.week
|
||||
? earnings.totalWorkedHoursByWeek
|
||||
: earnings.totalWorkedHoursByMonth;
|
||||
}
|
||||
|
||||
double get totalPayout {
|
||||
return balancePeriod == BalancePeriod.week
|
||||
? earnings.payoutByWeek
|
||||
: earnings.payoutByMonth;
|
||||
}
|
||||
|
||||
EarningsState copyWith({
|
||||
StateStatus? status,
|
||||
int? tabIndex,
|
||||
BalancePeriod? balancePeriod,
|
||||
EarningsSummaryEntity? earnings,
|
||||
List<EarningsTabState>? tabs,
|
||||
}) {
|
||||
return EarningsState(
|
||||
status: status ?? this.status,
|
||||
tabIndex: tabIndex ?? this.tabIndex,
|
||||
balancePeriod: balancePeriod ?? this.balancePeriod,
|
||||
earnings: earnings ?? this.earnings,
|
||||
tabs: tabs ?? this.tabs,
|
||||
);
|
||||
}
|
||||
|
||||
EarningsState copyWithTab({
|
||||
required EarningsTabState tab,
|
||||
int? tabIndex,
|
||||
StateStatus? status,
|
||||
}) {
|
||||
return copyWith(
|
||||
tabs: List.from(tabs)..[tabIndex ?? this.tabIndex] = tab,
|
||||
status: status,
|
||||
);
|
||||
}
|
||||
|
||||
EarningsState updateCurrentTab({
|
||||
List<EarningShiftEntity>? items,
|
||||
PaginationStatus? status,
|
||||
bool? hasMoreItems,
|
||||
String? label,
|
||||
String? paginationCursor,
|
||||
}) {
|
||||
return copyWith(
|
||||
tabs: List.from(tabs)
|
||||
..[tabIndex] = currentTab.copyWith(
|
||||
items: items,
|
||||
status: status,
|
||||
hasMoreItems: hasMoreItems,
|
||||
label: label,
|
||||
paginationCursor: paginationCursor,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user