feat: added education and projects

This commit is contained in:
dany
2025-11-22 16:27:18 +03:00
parent 17fdc5c76e
commit 385671e603
2 changed files with 281 additions and 13 deletions
@@ -199,21 +199,21 @@ fun CreateResumeScreen(
TTTextField(
value = workExp.description,
onValueChange = {
viewModel.changeWorkExperiencePlace(index, it)
viewModel.changeWorkExperienceDescription(index, it)
},
singleLine = false,
maxLines = 10,
label = "Расскажите подробнее",
error = formState.value.errors[ResumeField.WorkExperiencePlace(index)]
error = formState.value.errors[ResumeField.WorkExperienceDescription(index)]
)
TTTextField(
value = workExp.place,
value = workExp.monthDuration?.toString() ?: "",
onValueChange = {
viewModel.changeWorkExperiencePlace(index, it)
viewModel.changeWorkExperienceMonthDuration(index, it)
},
label = "Место работы",
error = formState.value.errors[ResumeField.WorkExperiencePlace(index)]
label = "Продолжительность (в месяцах)",
error = formState.value.errors[ResumeField.WorkExperienceMonthDuration(index)]
)
}
@@ -246,6 +246,161 @@ fun CreateResumeScreen(
fontSize = 18.sp,
)
}
Spacer(modifier = Modifier.height(Paddings.large))
Text(
modifier = Modifier.fillMaxWidth(),
text = "Образование:",
style = typography.titleMedium,
fontSize = 20.sp,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(Paddings.large))
formState.value.education.forEachIndexed { index, education ->
Text(
text = "${index + 1}:",
style = typography.labelLarge,
fontSize = 18.sp
)
Spacer(modifier = Modifier.height(Paddings.medium))
TTTextField(
value = education.place,
onValueChange = { viewModel.changeEducationPlace(index, it) },
label = "Учебное заведение",
error = formState.value.errors[ResumeField.EducationPlace(index)]
)
TTTextFieldWithDropdown(
value = education.grade.friendlyName,
onValueChange = {},
singleLine = false,
maxLines = Int.MAX_VALUE,
label = "Уровень образования",
error = formState.value.errors[ResumeField.EducationGrade(index)],
dropdownItems = viewModel.educationGradeOptions,
dropDownItem = {
Text(text = it.friendlyName, style = typography.titleMedium, fontSize = 16.sp)
},
onDropdownItemSelected = { viewModel.changeEducationGrade(index, it) }
)
TTTextField(
value = education.specialization,
onValueChange = { viewModel.changeEducationSpecialization(index, it) },
label = "Специализация",
error = formState.value.errors[ResumeField.EducationSpecialization(index)]
)
TTTextField(
value = education.description,
onValueChange = { viewModel.changeEducationDescription(index, it) },
singleLine = false,
maxLines = 10,
label = "Расскажите подробнее",
error = formState.value.errors[ResumeField.EducationDescription(index)]
)
}
if (formState.value.education.isEmpty()){
Text(
modifier = Modifier.fillMaxWidth(),
text = "Пока ничего нет",
style = typography.labelLarge,
fontSize = 18.sp,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(Paddings.medium))
}
Button(
modifier = Modifier
.fillMaxWidth(),
shape = Shapes.smallRoundedBox,
onClick = viewModel::addNewEducation,
colors = ButtonColors(
containerColor = colorScheme.onSecondary,
contentColor = colorScheme.secondary,
disabledContainerColor = colorScheme.onSecondary,
disabledContentColor = colorScheme.secondary
)
) {
Text(
text = "Добавить",
style = typography.labelLarge,
fontSize = 18.sp,
)
}
Spacer(modifier = Modifier.height(Paddings.large))
Text(
modifier = Modifier.fillMaxWidth(),
text = "Проекты:",
style = typography.titleMedium,
fontSize = 20.sp,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(Paddings.large))
formState.value.projects.forEachIndexed { index, project ->
Text(
text = "${index + 1}:",
style = typography.labelLarge,
fontSize = 18.sp
)
Spacer(modifier = Modifier.height(Paddings.medium))
TTTextField(
value = project.name,
onValueChange = { viewModel.changeProjectName(index, it) },
label = "Название проекта",
error = formState.value.errors[ResumeField.ProjectName(index)]
)
TTTextField(
value = project.description,
onValueChange = { viewModel.changeProjectDescription(index, it) },
singleLine = false,
maxLines = 10,
label = "Расскажите подробнее",
error = formState.value.errors[ResumeField.ProjectDescription(index)]
)
}
if (formState.value.projects.isEmpty()){
Text(
modifier = Modifier.fillMaxWidth(),
text = "Пока ничего нет",
style = typography.labelLarge,
fontSize = 18.sp,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(Paddings.medium))
}
Button(
modifier = Modifier
.fillMaxWidth(),
shape = Shapes.smallRoundedBox,
onClick = viewModel::addNewProject,
colors = ButtonColors(
containerColor = colorScheme.onSecondary,
contentColor = colorScheme.secondary,
disabledContainerColor = colorScheme.onSecondary,
disabledContentColor = colorScheme.secondary
)
) {
Text(
text = "Добавить",
style = typography.labelLarge,
fontSize = 18.sp,
)
}
Spacer(modifier = Modifier.height(Paddings.large))
BigButton(
onClick = viewModel::submit,
@@ -22,7 +22,6 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.koin.android.annotation.KoinViewModel
import kotlin.collections.minus
import kotlin.math.exp
data class ResumeFormState(
val about: String = "",
@@ -67,6 +66,15 @@ sealed class UIEducationGrade(val friendlyName: String) {
data object HighNotFinished : UIEducationGrade("Неоконченное высшее")
data object High : UIEducationGrade("Высшее")
data object Additional : UIEducationGrade("Другое")
fun mapToDomain(): EducationGrades = when (this) {
Common -> EducationGrades.Common
Middle -> EducationGrades.Middle
MiddleSpec -> EducationGrades.MiddleSpec
HighNotFinished -> EducationGrades.HighNotFinished
High -> EducationGrades.High
Additional -> EducationGrades.Additional
}
}
@KoinViewModel
@@ -219,6 +227,104 @@ class CreateResumeViewModel(
}
}
// Education
val educationGradeOptions = listOf(
UIEducationGrade.Common,
UIEducationGrade.Middle,
UIEducationGrade.MiddleSpec,
UIEducationGrade.HighNotFinished,
UIEducationGrade.High,
UIEducationGrade.Additional
)
fun addNewEducation() {
_formStateFillResume.update {
it.copy(
education = it.education + UIEducation(
place = "",
grade = UIEducationGrade.High,
specialization = "",
description = ""
)
)
}
}
fun changeEducationPlace(index: Int, value: String) {
_formStateFillResume.update {
it.copy(
education = it.education.mapIndexed { ind, education ->
if (ind == index) education.copy(place = value) else education
},
errors = it.errors - ResumeField.EducationPlace(index)
)
}
}
fun changeEducationGrade(index: Int, value: UIEducationGrade) {
_formStateFillResume.update {
it.copy(
education = it.education.mapIndexed { ind, education ->
if (ind == index) education.copy(grade = value) else education
},
errors = it.errors - ResumeField.EducationGrade(index)
)
}
}
fun changeEducationSpecialization(index: Int, value: String) {
_formStateFillResume.update {
it.copy(
education = it.education.mapIndexed { ind, education ->
if (ind == index) education.copy(specialization = value) else education
},
errors = it.errors - ResumeField.EducationSpecialization(index)
)
}
}
fun changeEducationDescription(index: Int, value: String) {
_formStateFillResume.update {
it.copy(
education = it.education.mapIndexed { ind, education ->
if (ind == index) education.copy(description = value) else education
},
errors = it.errors - ResumeField.EducationDescription(index)
)
}
}
// Projects
fun addNewProject() {
_formStateFillResume.update {
it.copy(
projects = it.projects + Project("", "")
)
}
}
fun changeProjectName(index: Int, value: String) {
_formStateFillResume.update {
it.copy(
projects = it.projects.mapIndexed { ind, project ->
if (ind == index) project.copy(name = value) else project
},
errors = it.errors - ResumeField.ProjectName(index)
)
}
}
fun changeProjectDescription(index: Int, value: String) {
_formStateFillResume.update {
it.copy(
projects = it.projects.mapIndexed { ind, project ->
if (ind == index) project.copy(description = value) else project
},
errors = it.errors - ResumeField.ProjectDescription(index)
)
}
}
fun submit() {
viewModelScope.launch {
val validation = validateDataUseCase.validateResume(
@@ -242,10 +348,17 @@ class CreateResumeViewModel(
about = about,
skills = keySkills.toList(),
experienceType = experience!!.mapToDomain(),
city = null,
experience = emptyList(),
education = emptyList(),
projects = emptyList()
city = city.ifBlank { null },
experience = workExperience,
education = education.map {
Education(
place = it.place,
grade = it.grade.mapToDomain(),
specialization = it.specialization,
description = it.description
)
},
projects = projects
)
}