feat(plans): plan creating has been completed

This commit is contained in:
Username1548
2025-04-29 14:44:33 +07:00
parent c8b828bfe1
commit 0fcb2c1a0d
43 changed files with 1602 additions and 635 deletions

View File

@@ -1,56 +1,65 @@
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
import 'package:eatvio_app/features/create_plan/repositories/create_plan_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
class CreatePlanCubit extends Cubit<CreatePlanState> {
CreatePlanCubit() : super(CreatePlanState.initial());
final CreatePlanRepository createPlanRepository;
CreatePlanCubit({required this.createPlanRepository}) : super(CreatePlanState.initial());
void setDetails(String text) {
emit(state.copyWith(planData: state.planData.copyWith(details: text)));
validateDetails();
}
void setName(String name) {
emit(state.copyWith(planData: state.planData.copyWith(name: name)));
validateName();
}
void setOptionTitle(String title) {
final options = [...state.planData.options];
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(short: title);
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(short: title);
emit(state.copyWith(planData: state.planData.copyWith(options: options)));
validateSelectedOptionTitle();
}
void setPrice(double price){
void setPrice(double price) {
final selectedOptionIndex = state.selectedOptionIndex ?? 0;
final options = [...state.planData.options];
options[selectedOptionIndex] = options[selectedOptionIndex].copyWith(price: price);
final options = [...state.planData.options];
options[selectedOptionIndex] = options[selectedOptionIndex].copyWith(price: price);
emit(state.copyWith(planData: state.planData.copyWith(options: options)));
validateSelectedOptionPrice();
}
void setOptionDescription(String description) {
final options = [...state.planData.options];
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(description: description);
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(description: description);
emit(state.copyWith(planData: state.planData.copyWith(options: options)));
validateSelectedOptionDescription();
}
void setOptionContent(String content, index) {
final options = [...state.planData.options];
void setOptionContent(String content, index) {
final options = [...state.planData.options];
final items = [...options[state.selectedOptionIndex ?? 0].items];
if (items.length <= index) {
items.add(content);
}else {
} else {
items[index] = content;
}
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(items: items);
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(items: items);
emit(state.copyWith(planData: state.planData.copyWith(options: options)));
validateSelectedOptionItems();
}
void removeOptionContentItem(int index) {
final options = [...state.planData.options];
final updatedItems = [...options[state.selectedOptionIndex ?? 0].items];
updatedItems.removeAt(index);
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(items: updatedItems);
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(items: updatedItems);
emit(state.copyWith(planData: state.planData.copyWith(options: options)));
validateSelectedOptionItems();
}
void setDuration(int index) {
@@ -62,10 +71,12 @@ class CreatePlanCubit extends Cubit<CreatePlanState> {
),
),
);
validateDuration();
}
void setImage(XFile? image) {
emit(state.copyWith(planData: state.planData.copyWith(image: image)));
validateImage();
}
void setPlanState(String planState) {
@@ -74,9 +85,319 @@ class CreatePlanCubit extends Cubit<CreatePlanState> {
void setPlanType(List<bool> type) {
emit(state.copyWith(planData: state.planData.copyWith(type: type)));
if (type.any((elem) => elem)) {
validateType();
}
}
void setSelectedOptionIndex(int? index) {
emit(state.copyWith(selectedOptionIndex: index));
}
bool validateName() {
final name = state.planData.name;
if (name == null || name.length < 3) {
emit(state.copyWith.validationState(titleHasError: true));
return false;
}
emit(state.copyWith.validationState(titleHasError: false));
return true;
}
bool validateDetails() {
final details = state.planData.details;
if (details == null || details.length < 3) {
emit(state.copyWith.validationState(descriptionHasError: true));
return false;
}
emit(state.copyWith.validationState(descriptionHasError: false));
return true;
}
bool validateDuration() {
if (state.planData.duration == null) {
emit(state.copyWith.validationState(durationHasError: true));
return false;
}
emit(state.copyWith.validationState(durationHasError: false));
return true;
}
bool validateType() {
final type = state.planData.type;
if (type.isEmpty || type.every((elem) => elem)) {
emit(state.copyWith.validationState(typeHasError: true));
return false;
}
emit(state.copyWith.validationState(typeHasError: false));
return true;
}
bool validateImage() {
if (state.planData.image == null) {
emit(state.copyWith.validationState(imageHasError: true));
return false;
}
emit(state.copyWith.validationState(imageHasError: false));
return true;
}
bool validateSelectedOptionPrice() {
final option = state.planData.options[state.selectedOptionIndex ?? 0];
final optionsValidationsStates = [...state.validationState.optionsValidationState];
if (option.price < 1200) {
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(priceHasError: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
return false;
}
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(priceHasError: false);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
return true;
}
bool validatePriceOfAllFilledOptions() {
final options = state.planData.options;
final optionsValidationsStates = [...state.validationState.optionsValidationState];
bool result = true;
if (options[0].price < 1200) {
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(priceHasError: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
result = false;
} else {
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(priceHasError: false);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
}
if (options[1].price < 1200) {
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(priceHasError: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
result = false;
} else {
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(priceHasError: false);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
}
if (options[2].price < 1200) {
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(priceHasError: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
result = false;
} else {
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(priceHasError: false);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
}
return result;
}
bool validateSelectedOptionItems() {
final option = state.planData.options[state.selectedOptionIndex ?? 0];
final optionsValidationsStates = [...state.validationState.optionsValidationState];
bool result = true;
if (option.items.isEmpty) {
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(isItemsEmpty: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
return false;
}
final itemsErrors = Map<int, bool>.from(optionsValidationsStates[state.selectedOptionIndex ?? 0].itemsErrors);
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(isItemsEmpty: false);
for (final (index, item) in option.items.indexed) {
if (item.length < 3) {
itemsErrors[index] = true;
result = false;
} else {
itemsErrors[index] = false;
}
}
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(itemsErrors: itemsErrors);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
return result;
}
bool validateItemsOfAllFilledOptions() {
final options = state.planData.options;
final optionsValidationsStates = [...state.validationState.optionsValidationState];
bool result = true;
if (options[0].items.isEmpty) {
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(isItemsEmpty: true);
result = false;
} else {
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(isItemsEmpty: false);
}
final option1ItemsErrors = Map<int, bool>.from(optionsValidationsStates[0].itemsErrors);
for (final (index, item) in options[0].items.indexed) {
if (item.length < 3) {
option1ItemsErrors[index] = true;
result = false;
} else {
option1ItemsErrors[index] = false;
}
}
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(itemsErrors: option1ItemsErrors);
if (state.selectedOptionIndex == null) {
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
return result;
}
if (options[1].items.isEmpty) {
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(isItemsEmpty: true);
result = false;
} else {
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(isItemsEmpty: false);
}
final option2ItemsErrors = Map<int, bool>.from(optionsValidationsStates[1].itemsErrors);
for (final (index, item) in options[1].items.indexed) {
if (item.length < 3) {
option2ItemsErrors[index] = true;
result = false;
} else {
option2ItemsErrors[index] = false;
}
}
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(itemsErrors: option2ItemsErrors);
if (options[2].items.isEmpty) {
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(isItemsEmpty: true);
result = false;
} else {
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(isItemsEmpty: false);
}
final option3ItemsErrors = Map<int, bool>.from(optionsValidationsStates[2].itemsErrors);
for (final (index, item) in options[2].items.indexed) {
if (item.length < 3) {
option3ItemsErrors[index] = true;
result = false;
} else {
option3ItemsErrors[index] = false;
}
}
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(itemsErrors: option3ItemsErrors);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
return result;
}
bool validateSelectedOptionTitle() {
final option = state.planData.options[state.selectedOptionIndex ?? 0];
final optionsValidationsStates = [...state.validationState.optionsValidationState];
if (option.short.length < 3) {
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(titleHasError: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
return false;
}
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(titleHasError: false);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
return true;
}
bool validateTitlesOfAllFilledOptions() {
final options = state.planData.options;
final optionsValidationsStates = [...state.validationState.optionsValidationState];
bool result = true;
if (options[0].short.length < 3) {
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(titleHasError: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
result = false;
} else {
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(titleHasError: false);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
}
if (state.selectedOptionIndex == null) return result;
if (options[1].short.length < 3) {
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(titleHasError: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
result = false;
} else {
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(titleHasError: false);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
}
if (options[2].short.length < 3) {
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(titleHasError: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
result = false;
} else {
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(titleHasError: false);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
}
return result;
}
bool validateSelectedOptionDescription() {
final option = state.planData.options[state.selectedOptionIndex ?? 0];
final optionsValidationsStates = [...state.validationState.optionsValidationState];
if (option.description.length < 3) {
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(descriptionHasError: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
return false;
}
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(descriptionHasError: false);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
return true;
}
bool validateDescriptionsOfAllOptions() {
final options = state.planData.options;
final optionsValidationsStates = [...state.validationState.optionsValidationState];
bool result = true;
if (options[0].description.length < 3) {
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(descriptionHasError: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
result = false;
} else {
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(descriptionHasError: false);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
}
if (state.selectedOptionIndex == null) return result;
if (options[1].description.length < 3) {
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(descriptionHasError: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
result = false;
} else {
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(descriptionHasError: false);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
}
if (options[2].description.length < 3) {
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(descriptionHasError: true);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
result = false;
} else {
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(descriptionHasError: false);
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
}
return result;
}
bool validateAllFields() {
final isNameValid = validateName();
final isDetailsValid = validateDetails();
final isImageValid = validateImage();
final isDurationValid = validateDuration();
final areTitlesValid = validateTitlesOfAllFilledOptions();
final arePricesValid = validatePriceOfAllFilledOptions();
final areDescriptionsValid = validateDescriptionsOfAllOptions();
final areItemsValid = validateItemsOfAllFilledOptions();
return isNameValid && isDetailsValid && isImageValid && isDurationValid && areTitlesValid && arePricesValid && areDescriptionsValid && areItemsValid;
}
Future<void> publishPlan() async {
if (validateAllFields()) {
emit(state.copyWith(isLoading: true));
final planModel = await state.planData.toCreationJson();
final plan = await createPlanRepository.createPlan(model: planModel);
if (state.planData.options[1].short.isNotEmpty) {
final options = state.planData.toOptionsCreationJson();
await createPlanRepository.createPacketOptions(slug: plan.slug, model: options);
} else {
final option = state.planData.toOptionCreationJson();
await createPlanRepository.createPacketOption(slug: plan.slug, model: option);
}
emit(state.copyWith(isLoading: false, planCreated: true));
}
}
}

View File

@@ -9,7 +9,9 @@ class CreatePlanState with _$CreatePlanState {
const factory CreatePlanState({
required bool isLoading,
required String? errorMessage,
required CreatePlanModel planData,
required CreatePlanModel planData,
required PlanValidationState validationState,
required bool planCreated,
int? durationIndex,
int? selectedOptionIndex,
}) = _CreatePlanState;
@@ -17,9 +19,55 @@ class CreatePlanState with _$CreatePlanState {
factory CreatePlanState.initial() => CreatePlanState(
isLoading: false,
errorMessage: null,
planCreated: false,
validationState: PlanValidationState.empty(),
planData: CreatePlanModel(
type: [false, false, false, false],
options: [PacketOptions.empty(), PacketOptions.empty(), PacketOptions.empty()],
),
),
);
}
@freezed
class PlanValidationState with _$PlanValidationState {
factory PlanValidationState({
required bool titleHasError,
required bool durationHasError,
required bool typeHasError,
required bool descriptionHasError,
required bool imageHasError,
required List<OptionValidationState> optionsValidationState,
}) = _PlanValidationState;
factory PlanValidationState.empty() => PlanValidationState(
titleHasError: false,
durationHasError: false,
typeHasError: false,
descriptionHasError: false,
imageHasError: false,
optionsValidationState: [
OptionValidationState.empty(),
OptionValidationState.empty(),
OptionValidationState.empty(),
],
);
}
@freezed
class OptionValidationState with _$OptionValidationState {
factory OptionValidationState({
required bool titleHasError,
required bool priceHasError,
required bool descriptionHasError,
required Map<int, bool> itemsErrors,
required bool isItemsEmpty,
}) = _OptionValidationState;
factory OptionValidationState.empty() => OptionValidationState(
descriptionHasError: false,
titleHasError: false,
priceHasError: false,
isItemsEmpty: false,
itemsErrors: {},
);
}