feat: Refactor code structure and optimize performance across multiple modules

This commit is contained in:
Achintha Isuru
2025-11-17 23:29:28 -05:00
parent 831570f2e0
commit a64cbd9edf
1508 changed files with 105319 additions and 0 deletions

View File

@@ -0,0 +1,337 @@
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/common/date_time_extension.dart';
import 'package:krow/core/application/di/injectable.dart';
import 'package:krow/core/data/enums/state_status.dart';
import 'package:krow/features/profile/schedule/domain/entities/day_shedule.dart';
import 'package:krow/features/profile/schedule/domain/entities/schedule_slot.dart';
import 'package:krow/features/profile/schedule/domain/staff_schedule_repository.dart';
part 'schedule_event.dart';
part 'schedule_state.dart';
class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
ScheduleBloc() : super(const ScheduleState()) {
on<ScheduleInitEvent>(_onBlocInit);
on<ScheduleEventChangeMode>(_onChangeMode);
on<ScheduleEventSelectDates>(_onSelectDates);
on<ScheduleEventAddSlot>(_onAddSlot);
on<ScheduleEventRemoveSlot>(_onRemoveSlot);
on<ScheduleEventEditSlot>(_onEditSlot);
on<ScheduleEventSave>(_onConfirmChanges);
on<ScheduleEventCancel>(_onCancelChanges);
on<ScheduleEventDeleteSchedule>(_onDeleteSchedule);
}
final StaffScheduleRepository staffRepository =
getIt<StaffScheduleRepository>();
Future<void> _onBlocInit(
ScheduleInitEvent event,
Emitter<ScheduleState> emit,
) async {
emit(state.copyWith(status: StateStatus.loading));
try {
await for (final scheduleData in staffRepository.getStaffSchedule()) {
emit(
state.copyWith(
schedules: scheduleData,
status: StateStatus.idle,
),
);
}
} catch (except) {
log(except.toString());
}
if (state.status == StateStatus.loading) {
emit(state.copyWith(status: StateStatus.idle));
}
}
Future<void> _onSelectDates(
ScheduleEventSelectDates event,
Emitter<ScheduleState> emit,
) async {
if (event.dates.length == 1) {
emit(
state.copyWith(
scheduleErrorText: '',
selectedDates: [event.dates.first],
prevSelectedDates: [],
isInEditMode: false,
),
);
emit(
state.copyWith(
selectedSchedules: await state.findSchedulesForSelection(),
),
);
return;
}
var startDate = event.dates.first;
final endDate = event.dates.last;
emit(
state.copyWith(
scheduleErrorText: '',
selectedDates: [
for (var i = 0; i < endDate.difference(startDate).inDays; i++)
startDate.add(Duration(days: i)),
endDate,
],
prevSelectedDates: [],
isInEditMode: false,
),
);
emit(
state.copyWith(
selectedSchedules: await state.findSchedulesForSelection(),
),
);
}
void _onChangeMode(
ScheduleEventChangeMode event,
Emitter<ScheduleState> emit,
) {
if (event.isInScheduleAddMode != null && state.selectedDates.length == 1) {
emit(state.copyWith(
scheduleErrorText: '',
isScheduleAddMode: event.isInScheduleAddMode,
));
return;
}
if (event.editedDate != null) {
emit(
state.copyWith(
selectedDates: [event.editedDate!],
prevSelectedDates: List.from(state.selectedDates),
),
);
}
emit(
state.copyWith(
isInEditMode: event.isInEditScheduleMode,
scheduleErrorText: '',
isScheduleAddMode: false,
tempSchedules: state.selectedDates.length == 1
? [
state.getScheduleForDate(
date: state.selectedDates.first,
isWeekly: event.isWeekly,
),
]
: [
for (int i = 0; i < state.selectedDates.length; i++)
DaySchedule(
date: state.selectedDates[i],
slots: const [],
),
],
),
);
}
void _onAddSlot(
ScheduleEventAddSlot event,
Emitter<ScheduleState> emit,
) {
emit(
state.copyWith(
tempSchedules: state.tempSchedules.map(
(daySchedule) {
final startTime = daySchedule.getLatestSlot();
return daySchedule.copyWith(
slots: [
...daySchedule.slots,
ScheduleSlot.minFromStartTime(
start: startTime,
),
],
);
},
).toList(),
),
);
}
void _onRemoveSlot(
ScheduleEventRemoveSlot event,
Emitter<ScheduleState> emit,
) {
emit(
state.copyWith(
scheduleErrorText: '',
tempSchedules: state.tempSchedules.map(
(daySchedule) {
return daySchedule.removeSlotAtIndex(event.slotIndex);
},
).toList(),
),
);
}
void _onEditSlot(
ScheduleEventEditSlot event,
Emitter<ScheduleState> emit,
) {
emit(
state.copyWith(
scheduleErrorText: '',
tempSchedules: state.tempSchedules.map(
(daySchedule) {
final daySlots = List<ScheduleSlot>.from(daySchedule.slots);
daySlots[event.slotIndex] = daySlots[event.slotIndex].editTime(
startTime: event.start?.copyWith(
day: daySchedule.date.day,
month: daySchedule.date.month,
),
endTime: event.end?.copyWith(
day: daySchedule.date.day,
month: daySchedule.date.month,
),
);
return daySchedule.copyWith(
slots: daySlots,
);
},
).toList(),
),
);
}
Future<Map<String, DaySchedule>> _handleTemporaryScheduleSave() async {
final previousSchedules = state.getDailySchedulesForTemporary();
if (state.tempSchedules.length == 1) {
return previousSchedules.isNotEmpty
? staffRepository.updateStaffSchedule(
schedules: state.tempSchedules,
)
: staffRepository.createStaffSchedule(
schedules: state.tempSchedules,
);
}
if (previousSchedules.isNotEmpty) {
await staffRepository.deleteStaffSchedule(schedules: previousSchedules);
}
return staffRepository.createStaffSchedule(
schedules: state.tempSchedules,
);
}
Future<void> _onConfirmChanges(
ScheduleEventSave event,
Emitter<ScheduleState> emit,
) async {
emit(
state.copyWith(
status: StateStatus.loading,
selectedDates:
state.prevSelectedDates.isNotEmpty ? state.prevSelectedDates : null,
prevSelectedDates: [],
),
);
final isTempScheduleValid = await state.isValid;
if (!isTempScheduleValid) {
emit(
state.copyWith(
status: StateStatus.error,
scheduleErrorText: 'overlap_error'.tr(),
),
);
return;
}
Map<String, DaySchedule>? schedules;
try {
schedules = await _handleTemporaryScheduleSave();
} catch (except) {
log(except.toString());
}
emit(
state.copyWith(
status: StateStatus.idle,
isInEditMode: false,
schedules: schedules,
tempSchedules: [],
),
);
emit(
state.copyWith(
selectedSchedules: await state.findSchedulesForSelection(),
),
);
}
void _onCancelChanges(
ScheduleEventCancel event,
Emitter<ScheduleState> emit,
) {
emit(
state.copyWith(
isInEditMode: false,
scheduleErrorText: '',
tempSchedules: [],
selectedDates:
state.prevSelectedDates.isNotEmpty ? state.prevSelectedDates : null,
prevSelectedDates: [],
),
);
}
Future<void> _onDeleteSchedule(
ScheduleEventDeleteSchedule event,
Emitter<ScheduleState> emit,
) async {
emit(
state.copyWith(
isInEditMode: false,
status: StateStatus.loading,
),
);
Map<String, DaySchedule>? schedules;
try {
final result = staffRepository.deleteStaffSchedule(
schedules: [event.schedule],
);
schedules = Map<String, DaySchedule>.from(state.schedules);
await result;
schedules.remove(event.schedule.getIdKey());
} catch (except) {
log(except.toString());
}
emit(
state.copyWith(
status: StateStatus.idle,
schedules: schedules,
),
);
emit(
state.copyWith(
selectedSchedules: await state.findSchedulesForSelection(),
),
);
}
}

View File

@@ -0,0 +1,74 @@
part of 'schedule_bloc.dart';
@immutable
sealed class ScheduleEvent {
const ScheduleEvent();
}
class ScheduleInitEvent extends ScheduleEvent {
const ScheduleInitEvent();
}
class ScheduleEventChangeMode extends ScheduleEvent {
const ScheduleEventChangeMode({
this.isInEditScheduleMode,
this.isInScheduleAddMode,
this.editedDate,
this.isWeekly = false,
});
final bool? isInEditScheduleMode;
final bool? isInScheduleAddMode;
final DateTime? editedDate;
final bool isWeekly;
}
class ScheduleEventSelectDates extends ScheduleEvent {
const ScheduleEventSelectDates({required this.dates});
final List<DateTime> dates;
}
class ScheduleEventAddSlot extends ScheduleEvent {
const ScheduleEventAddSlot();
}
class ScheduleEventRemoveSlot extends ScheduleEvent {
const ScheduleEventRemoveSlot({
required this.slot,
required this.slotIndex,
});
final ScheduleSlot slot;
final int slotIndex;
}
class ScheduleEventDeleteSchedule extends ScheduleEvent {
const ScheduleEventDeleteSchedule({
required this.schedule,
});
final DaySchedule schedule;
}
class ScheduleEventEditSlot extends ScheduleEvent {
const ScheduleEventEditSlot({
required this.slot,
required this.slotIndex,
this.start,
this.end,
});
final ScheduleSlot slot;
final int slotIndex;
final DateTime? start;
final DateTime? end;
}
class ScheduleEventSave extends ScheduleEvent {
const ScheduleEventSave();
}
class ScheduleEventCancel extends ScheduleEvent {
const ScheduleEventCancel();
}

View File

@@ -0,0 +1,122 @@
part of 'schedule_bloc.dart';
@immutable
class ScheduleState {
const ScheduleState({
this.schedules = const {},
this.tempSchedules = const [],
this.selectedDates = const [],
this.prevSelectedDates = const [],
this.selectedSchedules = const [],
this.status = StateStatus.idle,
this.isInEditMode = false,
this.isScheduleAddMode = false,
this.scheduleErrorText = '',
});
final Map<String, DaySchedule> schedules;
final List<DaySchedule> tempSchedules;
final List<DateTime> selectedDates;
final List<DateTime> prevSelectedDates;
final List<DaySchedule> selectedSchedules;
final StateStatus status;
final bool isInEditMode;
final bool isScheduleAddMode;
final String scheduleErrorText;
Future<bool> get isValid async {
if (tempSchedules.isEmpty) return false;
bool isValid = true;
for (int i = 0; i < tempSchedules.length; i++) {
isValid = tempSchedules[i].isValid;
if (!isValid) return isValid;
}
return isValid;
}
ScheduleState copyWith({
Map<String, DaySchedule>? schedules,
List<DaySchedule>? tempSchedules,
List<DateTime>? selectedDates,
List<DateTime>? prevSelectedDates,
List<DaySchedule>? selectedSchedules,
StateStatus? status,
bool? isInEditMode,
bool? isScheduleAddMode,
String? scheduleErrorText,
}) {
return ScheduleState(
schedules: schedules ?? this.schedules,
tempSchedules: tempSchedules ?? this.tempSchedules,
selectedDates: selectedDates ?? this.selectedDates,
prevSelectedDates: prevSelectedDates ?? this.prevSelectedDates,
selectedSchedules: selectedSchedules ?? this.selectedSchedules,
status: status ?? this.status,
isInEditMode: isInEditMode ?? this.isInEditMode,
isScheduleAddMode: isScheduleAddMode ?? this.isScheduleAddMode,
scheduleErrorText: scheduleErrorText ?? this.scheduleErrorText,
);
}
DaySchedule? findScheduleForDate(DateTime date) {
return schedules[date.getDayDateId()] ?? schedules[date.getWeekdayId()];
}
bool containsScheduleForDate(DateTime date) {
return schedules.containsKey(date.getDayDateId()) ||
schedules.containsKey(date.getWeekdayId());
}
Future<List<DaySchedule>> findSchedulesForSelection() async {
return [
for (final date in selectedDates)
if (schedules.containsKey(date.getDayDateId()))
schedules[date.getDayDateId()]!
else if (schedules.containsKey(date.getWeekdayId()))
schedules[date.getWeekdayId()]!.copyWith(
date: date,
),
];
}
List<DaySchedule> getDailySchedulesForTemporary() {
return [
for (final day in tempSchedules)
if (schedules.containsKey(day.getIdKey())) schedules[day.getIdKey()]!
];
}
bool isRangeContainsSchedules(List<DateTime> dates) {
return dates.any(
(date) => schedules.containsKey(date.getDayDateId()),
);
}
DaySchedule getScheduleForDate({
required DateTime date,
required bool isWeekly,
}) {
final daySchedule =
schedules[isWeekly ? date.getWeekdayId() : date.getDayDateId()];
return daySchedule ??
DaySchedule(
date: date,
slots: const [],
isWeekly: isWeekly,
);
}
Map<String, DaySchedule> mergeSchedules() {
final schedules = Map<String, DaySchedule>.from(this.schedules);
for (final schedule in tempSchedules) {
schedules[schedule.getIdKey()] = schedule;
}
return schedules;
}
}