From 0b1303c8cc0def9403f5fbafb2b5677884ce4094 Mon Sep 17 00:00:00 2001 From: kimyujin-com <80165026+kimyujin-com@users.noreply.github.com> Date: Sat, 17 Feb 2024 00:18:31 +0900 Subject: [PATCH] =?UTF-8?q?Fix:=20=EC=BB=A4=EB=A6=AC=EC=96=B4=20=EB=8D=94?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=ED=99=9C=EC=84=B1?= =?UTF-8?q?=ED=99=94=20=EC=A1=B0=EA=B1=B4=EC=97=90=20type=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/career/CareerAddCertificateFragment.kt | 125 --------------- .../career/CareerAddCertificateViewModel.kt | 149 ++++++++++++++++++ .../ui/career/CareerAddContestFragment.kt | 110 ------------- .../ui/career/CareerAddContestViewModel.kt | 147 +++++++++++++++++ 4 files changed, 296 insertions(+), 235 deletions(-) delete mode 100644 app/src/main/java/umc/com/mobile/project/ui/career/CareerAddCertificateFragment.kt create mode 100644 app/src/main/java/umc/com/mobile/project/ui/career/CareerAddCertificateViewModel.kt delete mode 100644 app/src/main/java/umc/com/mobile/project/ui/career/CareerAddContestFragment.kt create mode 100644 app/src/main/java/umc/com/mobile/project/ui/career/CareerAddContestViewModel.kt diff --git a/app/src/main/java/umc/com/mobile/project/ui/career/CareerAddCertificateFragment.kt b/app/src/main/java/umc/com/mobile/project/ui/career/CareerAddCertificateFragment.kt deleted file mode 100644 index f16f5b32..00000000 --- a/app/src/main/java/umc/com/mobile/project/ui/career/CareerAddCertificateFragment.kt +++ /dev/null @@ -1,125 +0,0 @@ -package umc.com.mobile.project.ui.career - -import android.content.Context -import android.os.Bundle -import android.text.Editable -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.activityViewModels -import umc.com.mobile.project.R -import umc.com.mobile.project.databinding.FragmentCareerAddCertificateBinding -import umc.com.mobile.project.ui.career.viewmodel.CareerAddCertificateViewModel -import umc.com.mobile.project.ui.career.viewmodel.CareerAddViewModel -import umc.com.mobile.project.ui.common.NavigationUtil.navigate - -class CareerAddCertificateFragment : Fragment() { - private var _binding: FragmentCareerAddCertificateBinding? = null - private val viewModel: CareerAddCertificateViewModel by activityViewModels() - private val sharedViewModel: CareerAddViewModel by activityViewModels() - private lateinit var mContext: Context - private val binding get() = _binding!! - private var startYear: String? = null - private var startMonth: String? = null - private var startDay: String? = null - private var endYear: String? = null - private var endMonth: String? = null - private var endDay: String? = null - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - _binding = FragmentCareerAddCertificateBinding.inflate(inflater, container, false) - binding.vm = viewModel - binding.lifecycleOwner = this - mContext = requireContext() - - _binding!!.etCareerAddCertificateStartDate.setOnClickListener { - val bottomSheet = PeriodBottomFragment(mContext, true, 1) - bottomSheet.setStyle( - DialogFragment.STYLE_NORMAL, - R.style.RoundCornerBottomSheetDialogTheme - ) - bottomSheet.show(requireActivity().supportFragmentManager, bottomSheet.tag) - } - _binding!!.etCareerAddCertificateEndDate.setOnClickListener { - val bottomSheet = PeriodBottomFragment(mContext, false, 1) - bottomSheet.setStyle( - DialogFragment.STYLE_NORMAL, - R.style.RoundCornerBottomSheetDialogTheme - ) - bottomSheet.show(requireActivity().supportFragmentManager, bottomSheet.tag) - } - _binding!!.etCareerAddCertificateType.setOnClickListener { - val bottomSheet = CertificateTypeBottomFragment(mContext, 1) - bottomSheet.setStyle( - DialogFragment.STYLE_NORMAL, - R.style.RoundCornerBottomSheetDialogTheme - ) - bottomSheet.show(requireActivity().supportFragmentManager, bottomSheet.tag) - } - _binding!!.btnCareerAdd.setOnClickListener { - //api 연결 - viewModel.addCareer() - navigate(R.id.action_fragment_career_add_to_fragment_career_confirm) - } - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - viewModel.init() - viewModel.selectedCertificateType.observe(viewLifecycleOwner) { selectedType -> - binding.etCareerAddCertificateType.text = - Editable.Factory.getInstance().newEditable(selectedType) - } - sharedViewModel.selectedStartYear.observe(viewLifecycleOwner) { year -> - startYear = year - updateStartDateEditText() - } - sharedViewModel.selectedStartMonth.observe(viewLifecycleOwner) { month -> - startMonth = month - updateStartDateEditText() - } - sharedViewModel.selectedStartDay.observe(viewLifecycleOwner) { day -> - startDay = day - updateStartDateEditText() - } - sharedViewModel.selectedEndYear.observe(viewLifecycleOwner) { year -> - endYear = year - updateEndDateEditText() - } - sharedViewModel.selectedEndMonth.observe(viewLifecycleOwner) { month -> - endMonth = month - updateEndDateEditText() - } - sharedViewModel.selectedEndDay.observe(viewLifecycleOwner) { day -> - endDay = day - updateEndDateEditText() - } - } - - private fun buildFormattedDate(year: String?, month: String?, day: String?): String { - return "$year$month$day" - } - - private fun updateStartDateEditText() { - val formattedDate = buildFormattedDate(startYear, startMonth, startDay) - binding.etCareerAddCertificateStartDate.text = - Editable.Factory.getInstance().newEditable(formattedDate) - } - - private fun updateEndDateEditText() { - val formattedDate = buildFormattedDate(endYear, endMonth, endDay) - binding.etCareerAddCertificateEndDate.text = - Editable.Factory.getInstance().newEditable(formattedDate) - } - - override fun onDestroyView() { - super.onDestroyView() - } -} diff --git a/app/src/main/java/umc/com/mobile/project/ui/career/CareerAddCertificateViewModel.kt b/app/src/main/java/umc/com/mobile/project/ui/career/CareerAddCertificateViewModel.kt new file mode 100644 index 00000000..5bbe3c3d --- /dev/null +++ b/app/src/main/java/umc/com/mobile/project/ui/career/CareerAddCertificateViewModel.kt @@ -0,0 +1,149 @@ +package umc.com.mobile.project.ui.career.viewmodel + +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.google.gson.GsonBuilder +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import umc.com.mobile.project.data.model.career.AddCareerResponse +import umc.com.mobile.project.data.network.ApiClient +import umc.com.mobile.project.data.network.api.CareerApi +import umc.com.mobile.project.ui.career.adapter.LocalDateAdapter +import umc.com.mobile.project.ui.career.data.CertificateDto +import java.time.LocalDate +import java.time.format.DateTimeFormatter + +class CareerAddCertificateViewModel : ViewModel() { + val title: MutableLiveData = MutableLiveData() + val selectedCertificateType: MutableLiveData = MutableLiveData() + val startDate: MutableLiveData = MutableLiveData() + val endDate: MutableLiveData = MutableLiveData() + + init { + title.value = "" + selectedCertificateType.value = "" + startDate.value = "" + endDate.value = "" + } + + fun init() { + title.value = "" + selectedCertificateType.value = "" + startDate.value = "" + endDate.value = "" + } + + fun updateCertificateType(type: String) { + selectedCertificateType.value = type + } + + /* 버튼 활성화 기능 */ + val isFilledAllOptions: LiveData = MediatorLiveData().apply { + addSource(title) { value = areBothFieldsFilled() } + addSource(selectedCertificateType) { value = areBothFieldsFilled() } + addSource(startDate) { value = areBothFieldsFilled() } + addSource(endDate) { value = areBothFieldsFilled() } + } + + private fun areBothFieldsFilled(): Boolean { + return !(title.value.isNullOrEmpty() || title.value!!.contains(" ") || title.value!!.length > 20) && !selectedCertificateType.value.isNullOrEmpty() + && isDateValid(startDate.value) && isDateValid(endDate.value) + } + + private fun isDateValid(date: String?): Boolean { + return !date.isNullOrBlank() && date.length == 8 + } + + private val imageList: MutableList = mutableListOf() + + //API에 전송할 데이터를 포함하는 RequestDto 생성 함수 + fun createRequestDto(): CertificateDto? { + val startDateString = startDate.value + val endDateString = endDate.value + //날짜가 입력되지 않은 경우 + if (startDateString.isNullOrBlank() || endDateString.isNullOrBlank()) { + return null + } + + val formatter = DateTimeFormatter.ofPattern("yyyyMMdd") + val formattedStartDate = LocalDate.parse(startDateString, formatter) + val formattedEndDate = LocalDate.parse(endDateString, formatter) + + val certificationType = when (selectedCertificateType.value) { + "실기" -> "PRACTICAL_EXAM" + "필기" -> "WRITTEN_EXAM" + else -> "INTERVIEW" + } + + return CertificateDto( + title = title.value!!, + category = "CERTIFICATIONS", + startDate = formattedStartDate, + endDate = formattedEndDate, + certificationType = certificationType + ) + } + + private val careerApiService = ApiClient.createService() + + private val _addedCareerInfo: MutableLiveData = MutableLiveData() + val addedCareerInfo: MutableLiveData + get() = _addedCareerInfo + + private val _error: MutableLiveData = MutableLiveData() + val error: LiveData + get() = _error + + fun addCareer() { + val requestDto = createRequestDto() + + val gson = GsonBuilder() + .setDateFormat("yyyy-MM-dd") + .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter()) + .create() + + val requestDtoJson = gson.toJson(requestDto) + val requestDtoPart: RequestBody = + requestDtoJson.toRequestBody("application/json".toMediaTypeOrNull()) + + careerApiService.addCareer(imageList, requestDtoPart) + .enqueue(object : Callback { + override fun onResponse( + call: Call, + response: Response + ) { + if (response.isSuccessful) { + val addCareerResponse = response.body() + if (addCareerResponse != null) { + _addedCareerInfo.postValue(addCareerResponse) + Log.d("addedCareerInfo 성공", "${response.body()}") + } else { + _error.postValue("서버 응답이 올바르지 않습니다.") + } + } else { + _error.postValue("커리어를 추가하지 못했습니다.") + try { + throw response.errorBody()?.string()?.let { + RuntimeException(it) + } ?: RuntimeException("Unknown error") + } catch (e: Exception) { + Log.e("addCareerInfo", "addCareer API 오류: ${e.message}") + } + } + } + + override fun onFailure(call: Call, t: Throwable) { + _error.postValue("네트워크 오류: ${t.message}") + Log.d("addCareerInfo", "addCareer 네트워크 오류: ${t.message}") + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/umc/com/mobile/project/ui/career/CareerAddContestFragment.kt b/app/src/main/java/umc/com/mobile/project/ui/career/CareerAddContestFragment.kt deleted file mode 100644 index 6d277899..00000000 --- a/app/src/main/java/umc/com/mobile/project/ui/career/CareerAddContestFragment.kt +++ /dev/null @@ -1,110 +0,0 @@ -package umc.com.mobile.project.ui.career - -import android.content.Context -import android.os.Bundle -import android.text.Editable -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.activityViewModels -import umc.com.mobile.project.R -import umc.com.mobile.project.databinding.FragmentCareerAddContestBinding -import umc.com.mobile.project.ui.career.viewmodel.CareerAddContestViewModel -import umc.com.mobile.project.ui.career.viewmodel.CareerAddViewModel -import umc.com.mobile.project.ui.common.NavigationUtil.navigate - -class CareerAddContestFragment : Fragment() { - private var _binding: FragmentCareerAddContestBinding? = null - private val viewModel: CareerAddContestViewModel by activityViewModels() - private val sharedViewModel: CareerAddViewModel by activityViewModels() - private lateinit var mContext: Context - private val binding get() = _binding!! - private var award: String? = null - private var startYear: String? = null - private var startMonth: String? = null - private var startDay: String? = null - private var endYear: String? = null - private var endMonth: String? = null - private var endDay: String? = null - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - _binding = FragmentCareerAddContestBinding.inflate(inflater, container, false) - binding.vm = viewModel - binding.lifecycleOwner = this - mContext = requireContext() - - _binding!!.etCareerAddContestStartYear.setOnClickListener { - val bottomSheet = PeriodBottomFragment(mContext, true, 1) - bottomSheet.setStyle(DialogFragment.STYLE_NORMAL, R.style.RoundCornerBottomSheetDialogTheme) - bottomSheet.show(requireActivity().supportFragmentManager, bottomSheet.tag) - } - _binding!!.etCareerAddContestEndYear.setOnClickListener { - val bottomSheet = PeriodBottomFragment(mContext, false, 1) - bottomSheet.setStyle(DialogFragment.STYLE_NORMAL, R.style.RoundCornerBottomSheetDialogTheme) - bottomSheet.show(requireActivity().supportFragmentManager, bottomSheet.tag) - } - _binding!!.etCareerAddContestAward.setOnClickListener { - val bottomSheet = AwardBottomFragment(mContext, 1) - bottomSheet.setStyle(DialogFragment.STYLE_NORMAL, R.style.RoundCornerBottomSheetDialogTheme) - bottomSheet.show(requireActivity().supportFragmentManager, bottomSheet.tag) - } - _binding!!.btnCareerAdd.setOnClickListener { - //api 연결 - viewModel.addCareer() - navigate(R.id.action_fragment_career_add_to_fragment_career_confirm) - } - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - viewModel.init() - viewModel.selectedAward.observe(viewLifecycleOwner) { selectedAward -> - binding.etCareerAddContestAward.text = Editable.Factory.getInstance().newEditable(selectedAward) - } - sharedViewModel.selectedStartYear.observe(viewLifecycleOwner) { year -> - startYear = year - updateStartDateEditText() - } - sharedViewModel.selectedStartMonth.observe(viewLifecycleOwner) { month -> - startMonth = month - updateStartDateEditText() - } - sharedViewModel.selectedStartDay.observe(viewLifecycleOwner) { day -> - startDay = day - updateStartDateEditText() - } - sharedViewModel.selectedEndYear.observe(viewLifecycleOwner) { year -> - endYear = year - updateEndDateEditText() - } - sharedViewModel.selectedEndMonth.observe(viewLifecycleOwner) { month -> - endMonth = month - updateEndDateEditText() - } - sharedViewModel.selectedEndDay.observe(viewLifecycleOwner) { day -> - endDay = day - updateEndDateEditText() - } - } - private fun buildFormattedDate(year: String?, month: String?, day: String?): String { - return "$year$month$day" - } - private fun updateStartDateEditText() { - val formattedDate = buildFormattedDate(startYear, startMonth, startDay) - binding.etCareerAddContestStartYear.text = Editable.Factory.getInstance().newEditable(formattedDate) - } - private fun updateEndDateEditText() { - val formattedDate = buildFormattedDate(endYear, endMonth, endDay) - binding.etCareerAddContestEndYear.text = Editable.Factory.getInstance().newEditable(formattedDate) - } - override fun onDestroyView() { - super.onDestroyView() - } -} diff --git a/app/src/main/java/umc/com/mobile/project/ui/career/CareerAddContestViewModel.kt b/app/src/main/java/umc/com/mobile/project/ui/career/CareerAddContestViewModel.kt new file mode 100644 index 00000000..4acb3157 --- /dev/null +++ b/app/src/main/java/umc/com/mobile/project/ui/career/CareerAddContestViewModel.kt @@ -0,0 +1,147 @@ +package umc.com.mobile.project.ui.career.viewmodel + +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import umc.com.mobile.project.data.model.career.AddCareerResponse +import umc.com.mobile.project.data.network.ApiClient +import umc.com.mobile.project.data.network.api.CareerApi +import umc.com.mobile.project.ui.career.data.RequestDto +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import com.google.gson.GsonBuilder +import okhttp3.RequestBody.Companion.toRequestBody +import umc.com.mobile.project.ui.career.adapter.LocalDateAdapter + +class CareerAddContestViewModel : ViewModel() { + val title: MutableLiveData = MutableLiveData() + val selectedAward: MutableLiveData = MutableLiveData() + val startDate: MutableLiveData = MutableLiveData() + val endDate: MutableLiveData = MutableLiveData() + + init { + title.value = "" + selectedAward.value = "" + startDate.value = "" + endDate.value = "" + } + + fun init() { + title.value = "" + selectedAward.value = "" + startDate.value = "" + endDate.value = "" + } + + fun updateSelectedAward(award: String) { + selectedAward.value = award + } + + /* 버튼 활성화 기능 */ + val isFilledAllOptions: LiveData = MediatorLiveData().apply { + addSource(title) { value = areBothFieldsFilled() } + addSource(selectedAward) { value = areBothFieldsFilled() } + addSource(startDate) { value = areBothFieldsFilled() } + addSource(endDate) { value = areBothFieldsFilled() } + } + + private fun areBothFieldsFilled(): Boolean { + return !(title.value.isNullOrEmpty() || title.value!!.contains(" ") || title.value!!.length > 20) && !selectedAward.value.isNullOrEmpty() + && isDateValid(startDate.value) && isDateValid(endDate.value) + } + + private fun isDateValid(date: String?): Boolean { + return !date.isNullOrBlank() && date.length == 8 + } + + private val imageList: MutableList = mutableListOf() + + //API에 전송할 데이터를 포함하는 RequestDto 생성 함수 + fun createRequestDto(): RequestDto? { + val startDateString = startDate.value + val endDateString = endDate.value + + val formatter = DateTimeFormatter.ofPattern("yyyyMMdd") + val formattedStartDate = LocalDate.parse(startDateString, formatter) + val formattedEndDate = LocalDate.parse(endDateString, formatter) + + val certificationType = when (selectedAward.value) { + "대상" -> "GRAND_PRIZE" + "최우수상" -> "EXCELLENT_PRIZE" + "우수상" -> "GOOD_PRIZE" + else -> "ENCOURAGEMENT_PRIZE" + } + + return RequestDto( + title = title.value!!, + content = "", + category = "COMPETITIONS", + startDate = formattedStartDate, + endDate = formattedEndDate, + award = certificationType + ) + } + + private val careerApiService = ApiClient.createService() + + private val _addedCareerInfo: MutableLiveData = MutableLiveData() + val addedCareerInfo: MutableLiveData + get() = _addedCareerInfo + + private val _error: MutableLiveData = MutableLiveData() + val error: LiveData + get() = _error + + fun addCareer() { + val requestDto = createRequestDto() + + val gson = GsonBuilder() + .setDateFormat("yyyy-MM-dd") + .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter()) + .create() + + val requestDtoJson = gson.toJson(requestDto) + val requestDtoPart: RequestBody = + requestDtoJson.toRequestBody("application/json".toMediaTypeOrNull()) + + careerApiService.addCareer(imageList, requestDtoPart) + .enqueue(object : Callback { + override fun onResponse( + call: Call, + response: Response + ) { + if (response.isSuccessful) { + val addCareerResponse = response.body() + if (addCareerResponse != null) { + _addedCareerInfo.postValue(addCareerResponse) + Log.d("addedCareerInfo 성공", "${response.body()}") + } else { + _error.postValue("서버 응답이 올바르지 않습니다.") + } + } else { + _error.postValue("커리어를 추가하지 못했습니다.") + try { + throw response.errorBody()?.string()?.let { + RuntimeException(it) + } ?: RuntimeException("Unknown error") + } catch (e: Exception) { + Log.e("addCareerInfo", "addCareer API 오류: ${e.message}") + } + } + } + + override fun onFailure(call: Call, t: Throwable) { + _error.postValue("네트워크 오류: ${t.message}") + Log.d("addCareerInfo", "addCareer 네트워크 오류: ${t.message}") + } + }) + } +} \ No newline at end of file