diff --git a/app/src/main/java/com/prodhack/moscow2025/data/dto/ResumeDtos.kt b/app/src/main/java/com/prodhack/moscow2025/data/dto/ResumeDtos.kt index 1f263e4..2739f8a 100644 --- a/app/src/main/java/com/prodhack/moscow2025/data/dto/ResumeDtos.kt +++ b/app/src/main/java/com/prodhack/moscow2025/data/dto/ResumeDtos.kt @@ -125,31 +125,39 @@ data class EducationDTO( @Serializable enum class EducationGradesDTO { - @SerialName("common") - Common, + @SerialName("basic_general_education") + BasicGeneralEducation, - @SerialName("middle") - Middle, + @SerialName("secondary_general_education") + SecondaryGeneralEducation, - @SerialName("middle_spec") - MiddleSpec, + @SerialName("secondary_professional_education") + SecondaryProfessionalEducation, - @SerialName("high_not_finished") - HighNotFinished, + @SerialName("bachelor") + Bachelor, - @SerialName("high") - High, + @SerialName("specialist") + Specialist, - @SerialName("additional") - Additional; + @SerialName("master") + Master, + + @SerialName("postgraduate_studies") + PostgraduateStudies, + + @SerialName("other") + Other; fun mapToDomain(): EducationGrades = when (this) { - Common -> EducationGrades.Common - Middle -> EducationGrades.Middle - MiddleSpec -> EducationGrades.MiddleSpec - HighNotFinished -> EducationGrades.HighNotFinished - High -> EducationGrades.High - Additional -> EducationGrades.Additional + BasicGeneralEducation -> EducationGrades.BasicGeneralEducation + SecondaryGeneralEducation -> EducationGrades.SecondaryGeneralEducation + SecondaryProfessionalEducation -> EducationGrades.SecondaryProfessionalEducation + Bachelor -> EducationGrades.Bachelor + Specialist -> EducationGrades.Specialist + Master -> EducationGrades.Master + PostgraduateStudies -> EducationGrades.PostgraduateStudies + Other -> EducationGrades.Other } } diff --git a/app/src/main/java/com/prodhack/moscow2025/domain/models/ResumeModel.kt b/app/src/main/java/com/prodhack/moscow2025/domain/models/ResumeModel.kt index 1b75227..a0df88d 100644 --- a/app/src/main/java/com/prodhack/moscow2025/domain/models/ResumeModel.kt +++ b/app/src/main/java/com/prodhack/moscow2025/domain/models/ResumeModel.kt @@ -39,12 +39,14 @@ data class Education( ) enum class EducationGrades { - Common, - Middle, - MiddleSpec, - HighNotFinished, - High, - Additional + BasicGeneralEducation, + SecondaryGeneralEducation, + SecondaryProfessionalEducation, + Bachelor, + Specialist, + Master, + PostgraduateStudies, + Other } data class Project( diff --git a/app/src/main/java/com/prodhack/moscow2025/domain/usecase/auth/ValidateFieldsUseCase.kt b/app/src/main/java/com/prodhack/moscow2025/domain/usecase/auth/ValidateFieldsUseCase.kt index dfe9c18..cc1e907 100644 --- a/app/src/main/java/com/prodhack/moscow2025/domain/usecase/auth/ValidateFieldsUseCase.kt +++ b/app/src/main/java/com/prodhack/moscow2025/domain/usecase/auth/ValidateFieldsUseCase.kt @@ -1,10 +1,14 @@ package com.prodhack.moscow2025.domain.usecase.auth +import android.util.Log import android.util.Patterns import com.prodhack.moscow2025.domain.models.AuthField import com.prodhack.moscow2025.domain.models.ExperienceType import com.prodhack.moscow2025.domain.models.PhoneNumberPattern +import com.prodhack.moscow2025.domain.models.Project import com.prodhack.moscow2025.domain.models.ResumeField +import com.prodhack.moscow2025.domain.models.WorkExperience +import com.prodhack.moscow2025.presentation.screens.createResume.UIEducation import org.koin.core.annotation.Single data class ValidationResult( @@ -87,7 +91,11 @@ class ValidateFieldsUseCase { about: String, position: String, experience: ExperienceType?, - keySkills: List + keySkills: List, + city: String, + workExperience: List, + education: List, + projects: List ): ValidationResult { val errors = buildMap { if (about.isBlank()) put(ResumeField.About, "Без этого мы не сможем рассчитать вашу ЗП") @@ -101,6 +109,47 @@ class ValidateFieldsUseCase { ) if (keySkills.isEmpty()) put(ResumeField.KeySkills, "Укажите хотя бы один навык") + if (city.isEmpty()) put(ResumeField.City, "Без этого мы не сможем рассчитать вашу ЗП") + workExperience.forEachIndexed { index, exp -> + if (exp.place.isBlank()) put( + ResumeField.WorkExperiencePlace(index), + "Без этого мы не сможем рассчитать вашу ЗП" + ) + if (exp.description.isBlank()) put( + ResumeField.WorkExperienceDescription(index), + "Без этого мы не сможем рассчитать вашу ЗП" + ) + if (exp.monthDuration == null) put( + ResumeField.WorkExperienceMonthDuration(index), + "Введите корректное число" + ) + } + + education.forEachIndexed { index, educ -> + if (educ.place.isBlank()) put( + ResumeField.EducationPlace(index), + "Без этого мы не сможем рассчитать вашу ЗП" + ) + if (educ.description.isBlank()) put( + ResumeField.EducationDescription(index), + "Без этого мы не сможем рассчитать вашу ЗП" + ) + if (educ.specialization.isBlank()) put( + ResumeField.EducationSpecialization(index), + "Без этого мы не сможем рассчитать вашу ЗП" + ) + } + + projects.forEachIndexed { index, prj -> + if (prj.name.isBlank()) put( + ResumeField.ProjectName(index), + "Без этого мы не сможем рассчитать вашу ЗП" + ) + if (prj.description.isBlank()) put( + ResumeField.ProjectDescription(index), + "Без этого мы не сможем рассчитать вашу ЗП" + ) + } } return ValidationResult(errors) } diff --git a/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeScreen.kt b/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeScreen.kt index d618dc6..f9284b8 100644 --- a/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeScreen.kt +++ b/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeScreen.kt @@ -85,12 +85,16 @@ fun CreateResumeScreen( label = "Какая должность вас интересует?", error = formState.value.errors[ResumeField.Position] ) + Spacer(modifier = Modifier.height(Paddings.medium)) + TTTextField( value = formState.value.city, onValueChange = viewModel::onCityChange, label = "Ваш город", error = formState.value.errors[ResumeField.City] ) + Spacer(modifier = Modifier.height(Paddings.medium)) + TTTextField( value = formState.value.about, onValueChange = viewModel::onAboutChange, @@ -99,6 +103,8 @@ fun CreateResumeScreen( label = "Расскажите о себе", error = formState.value.errors[ResumeField.Position] ) + Spacer(modifier = Modifier.height(Paddings.medium)) + TTTextFieldWithDropdown( value = formState.value.experience?.friendlyName ?: "", onValueChange = {}, @@ -112,6 +118,8 @@ fun CreateResumeScreen( }, onDropdownItemSelected = viewModel::onExperienceSelect ) + Spacer(modifier = Modifier.height(Paddings.medium)) + TTTextFieldWithSearch( value = viewModel.skillSearchQuery.value, onValueChange = { @@ -121,7 +129,7 @@ fun CreateResumeScreen( error = formState.value.errors[ResumeField.Experience], dropdownItems = viewModel.suggestedSkills.collectAsState(emptyList()).value, dropDownItem = { - Text(text = it, style = typography.titleMedium, fontSize = 16.sp) + Text(text = it, style = typography.labelLarge, fontSize = 16.sp) }, onDropdownItemSelected = viewModel::onAddSkill, trailingIcon = { @@ -151,6 +159,8 @@ fun CreateResumeScreen( } } ) + Spacer(modifier = Modifier.height(Paddings.medium)) + FlowRow( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy( @@ -167,7 +177,7 @@ fun CreateResumeScreen( } } - Spacer(modifier = Modifier.height(Paddings.large * 2)) + Spacer(modifier = Modifier.height(Paddings.large)) Text( modifier = Modifier.fillMaxWidth(), @@ -195,7 +205,7 @@ fun CreateResumeScreen( label = "Место работы", error = formState.value.errors[ResumeField.WorkExperiencePlace(index)] ) - + Spacer(modifier = Modifier.height(Paddings.medium)) TTTextField( value = workExp.description, onValueChange = { @@ -206,7 +216,7 @@ fun CreateResumeScreen( label = "Расскажите подробнее", error = formState.value.errors[ResumeField.WorkExperienceDescription(index)] ) - + Spacer(modifier = Modifier.height(Paddings.medium)) TTTextField( value = workExp.monthDuration?.toString() ?: "", onValueChange = { @@ -215,9 +225,10 @@ fun CreateResumeScreen( label = "Продолжительность (в месяцах)", error = formState.value.errors[ResumeField.WorkExperienceMonthDuration(index)] ) + Spacer(modifier = Modifier.height(Paddings.medium)) } - if (formState.value.workExperience.isEmpty()){ + if (formState.value.workExperience.isEmpty()) { Text( modifier = Modifier.fillMaxWidth(), text = "Пока ничего нет", @@ -250,7 +261,7 @@ fun CreateResumeScreen( Text( modifier = Modifier.fillMaxWidth(), - text = "Образование:", + text = "Ваше образование:", style = typography.titleMedium, fontSize = 20.sp, textAlign = TextAlign.Center @@ -272,7 +283,7 @@ fun CreateResumeScreen( label = "Учебное заведение", error = formState.value.errors[ResumeField.EducationPlace(index)] ) - + Spacer(modifier = Modifier.height(Paddings.medium)) TTTextFieldWithDropdown( value = education.grade.friendlyName, onValueChange = {}, @@ -282,29 +293,34 @@ fun CreateResumeScreen( error = formState.value.errors[ResumeField.EducationGrade(index)], dropdownItems = viewModel.educationGradeOptions, dropDownItem = { - Text(text = it.friendlyName, style = typography.titleMedium, fontSize = 16.sp) + Text( + text = it.friendlyName, + style = typography.labelLarge, + fontSize = 16.sp + ) }, onDropdownItemSelected = { viewModel.changeEducationGrade(index, it) } ) - + Spacer(modifier = Modifier.height(Paddings.medium)) TTTextField( value = education.specialization, onValueChange = { viewModel.changeEducationSpecialization(index, it) }, label = "Специализация", error = formState.value.errors[ResumeField.EducationSpecialization(index)] ) - + Spacer(modifier = Modifier.height(Paddings.medium)) TTTextField( value = education.description, onValueChange = { viewModel.changeEducationDescription(index, it) }, singleLine = false, maxLines = 10, - label = "Расскажите подробнее", + label = "Расскажите подробнее (опционально)", error = formState.value.errors[ResumeField.EducationDescription(index)] ) + Spacer(modifier = Modifier.height(Paddings.medium)) } - if (formState.value.education.isEmpty()){ + if (formState.value.education.isEmpty()) { Text( modifier = Modifier.fillMaxWidth(), text = "Пока ничего нет", @@ -338,7 +354,7 @@ fun CreateResumeScreen( Text( modifier = Modifier.fillMaxWidth(), - text = "Проекты:", + text = "Интересные проекты:", style = typography.titleMedium, fontSize = 20.sp, textAlign = TextAlign.Center @@ -360,7 +376,7 @@ fun CreateResumeScreen( label = "Название проекта", error = formState.value.errors[ResumeField.ProjectName(index)] ) - + Spacer(modifier = Modifier.height(Paddings.medium)) TTTextField( value = project.description, onValueChange = { viewModel.changeProjectDescription(index, it) }, @@ -369,9 +385,10 @@ fun CreateResumeScreen( label = "Расскажите подробнее", error = formState.value.errors[ResumeField.ProjectDescription(index)] ) + Spacer(modifier = Modifier.height(Paddings.medium)) } - if (formState.value.projects.isEmpty()){ + if (formState.value.projects.isEmpty()) { Text( modifier = Modifier.fillMaxWidth(), text = "Пока ничего нет", @@ -407,6 +424,8 @@ fun CreateResumeScreen( buttonText = "Узнать свою ЗП", isLoading = viewModel.resumeFillState.collectAsState().value.isLoading ) + Spacer(modifier = Modifier.height(Paddings.large)) + } } } diff --git a/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeViewModel.kt b/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeViewModel.kt index c2bf15e..d8c0848 100644 --- a/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeViewModel.kt +++ b/app/src/main/java/com/prodhack/moscow2025/presentation/screens/createResume/CreateResumeViewModel.kt @@ -59,21 +59,40 @@ data class UIEducation( val description: String ) +//основное общее образование — basic_general_education +// +//среднее общее образование — secondary_general_education +// +//среднее профессиональное образование — secondary_professional_education +// +//бакалавриат — bachelor +// +//специалитет — specialist +// +//магистратура — master +// +//подготовка кадров высшей квалификации (аспірантура, ординатура, докторантура) — postgraduate_studies sealed class UIEducationGrade(val friendlyName: String) { - data object Common : UIEducationGrade("Общее") - data object Middle : UIEducationGrade("Среднее") - data object MiddleSpec : UIEducationGrade("Средне-специальное") - data object HighNotFinished : UIEducationGrade("Неоконченное высшее") - data object High : UIEducationGrade("Высшее") - data object Additional : UIEducationGrade("Другое") + data object BasicGeneralEducation : UIEducationGrade("Общее") + data object SecondaryGeneralEducation : UIEducationGrade("Среднее") + data object SecondaryProfessionalEducation : UIEducationGrade("Средне-специальное") + data object Bachelor : UIEducationGrade("Бакалавриат") + data object Specialist : UIEducationGrade("Специалитет") + data object Master : UIEducationGrade("Магистратура") + + data object PostgraduateStudies: UIEducationGrade("Аспирантура и выше") + + data object Other: UIEducationGrade("Другое") fun mapToDomain(): EducationGrades = when (this) { - Common -> EducationGrades.Common - Middle -> EducationGrades.Middle - MiddleSpec -> EducationGrades.MiddleSpec - HighNotFinished -> EducationGrades.HighNotFinished - High -> EducationGrades.High - Additional -> EducationGrades.Additional + BasicGeneralEducation -> EducationGrades.BasicGeneralEducation + SecondaryGeneralEducation -> EducationGrades.SecondaryGeneralEducation + SecondaryProfessionalEducation -> EducationGrades.SecondaryProfessionalEducation + Bachelor -> EducationGrades.Bachelor + Specialist -> EducationGrades.Specialist + Master -> EducationGrades.Master + PostgraduateStudies -> EducationGrades.PostgraduateStudies + Other -> EducationGrades.Other } } @@ -204,7 +223,8 @@ class CreateResumeViewModel( experience.copy( monthDuration = it ) - } ?: experience + } + ?: if (value.isEmpty()) experience.copy(monthDuration = null) else experience } else experience }, errors = it.errors @@ -229,12 +249,12 @@ class CreateResumeViewModel( // Education val educationGradeOptions = listOf( - UIEducationGrade.Common, - UIEducationGrade.Middle, - UIEducationGrade.MiddleSpec, - UIEducationGrade.HighNotFinished, - UIEducationGrade.High, - UIEducationGrade.Additional + UIEducationGrade.BasicGeneralEducation, + UIEducationGrade.SecondaryGeneralEducation, + UIEducationGrade.SecondaryProfessionalEducation, + UIEducationGrade.Bachelor, + UIEducationGrade.Specialist, + UIEducationGrade.Master ) fun addNewEducation() { @@ -242,7 +262,7 @@ class CreateResumeViewModel( it.copy( education = it.education + UIEducation( place = "", - grade = UIEducationGrade.High, + grade = UIEducationGrade.Specialist, specialization = "", description = "" ) @@ -332,6 +352,10 @@ class CreateResumeViewModel( position = _formStateFillResume.value.position, experience = _formStateFillResume.value.experience?.mapToDomain(), keySkills = _formStateFillResume.value.keySkills.toList(), + city = _formStateFillResume.value.city, + workExperience = _formStateFillResume.value.workExperience, + education = _formStateFillResume.value.education, + projects = _formStateFillResume.value.projects ) if (!validation.isValid) {