From 6904b59e1fcac22564393f5edf3dca0a4180c7b2 Mon Sep 17 00:00:00 2001 From: Kyu hyunSung Date: Wed, 12 Nov 2025 21:49:44 +0900 Subject: [PATCH 1/6] feat/#28: remove my articles --- .../presentation/mypage/screen/main/MypageScreen.kt | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt index 5cc119c..209a227 100644 --- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt @@ -88,19 +88,6 @@ fun MypageScreen( ) } - item { - MyPageCardItemContainer( - text = "마이페이지", - items = listOf( - MyPageItemData(id = "1", title = "나의 게시글", route = "/posts"), - MyPageItemData(id = "2", title = "나의 댓글", route = "/settings") - ), - onItemClick = { item -> - // 클릭 처리 - } - ) - } - item { Spacer(modifier = Modifier.height(24.dp)) } From 22258f75743cd1adf9195707c63b48026bd2fab9 Mon Sep 17 00:00:00 2001 From: Kyu hyunSung Date: Wed, 12 Nov 2025 23:14:00 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat/#28:=20GET=20/users/myprofile=20API=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hsLink/hslink/data/di/RepositoryModule.kt | 7 +++ .../hsLink/hslink/data/di/ServiceModule.kt | 6 +++ .../response/mypage/MyPageUserProfileDto.kt | 16 ++++++ .../mypage/MypageRepositoryImpl.kt | 32 ++++++++++++ .../data/service/mypage/MypageService.kt | 13 +++++ .../repository/mypage/MypageRepository.kt | 9 ++++ .../mypage/screen/main/MypageScreen.kt | 52 ++++++++++++++++--- .../mypage/viewmodel/MypageViewModel.kt | 52 +++++++++++++++++++ 8 files changed, 179 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/MyPageUserProfileDto.kt create mode 100644 app/src/main/java/com/hsLink/hslink/data/repositoryimpl/mypage/MypageRepositoryImpl.kt create mode 100644 app/src/main/java/com/hsLink/hslink/data/service/mypage/MypageService.kt create mode 100644 app/src/main/java/com/hsLink/hslink/domain/repository/mypage/MypageRepository.kt create mode 100644 app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt diff --git a/app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt b/app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt index 18a6b30..92f3ddf 100644 --- a/app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt +++ b/app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt @@ -4,11 +4,13 @@ import com.hsLink.hslink.data.repositoryimpl.AuthRepositoryImpl import com.hsLink.hslink.data.repositoryimpl.CommunityRepositoryImpl import com.hsLink.hslink.data.repositoryimpl.DummyRepositoryImpl import com.hsLink.hslink.data.repositoryimpl.home.PostRepositoryImpl +import com.hsLink.hslink.data.repositoryimpl.mypage.MypageRepositoryImpl import com.hsLink.hslink.data.repositoryimpl.search.SearchRepositoryImpl import com.hsLink.hslink.domain.DummyRepository import com.hsLink.hslink.domain.repository.AuthRepository import com.hsLink.hslink.domain.repository.community.CommunityRepository import com.hsLink.hslink.domain.repository.home.PostRepository +import com.hsLink.hslink.domain.repository.mypage.MypageRepository import com.hsLink.hslink.domain.repository.search.SearchRepository import dagger.Binds import dagger.Module @@ -45,4 +47,9 @@ interface RepositoryModule { fun bindsSearchRepository( searchRepositoryImpl: SearchRepositoryImpl, ): SearchRepository + + @Binds + fun bindMypageRepository( + mypageRepositoryImpl: MypageRepositoryImpl + ): MypageRepository } diff --git a/app/src/main/java/com/hsLink/hslink/data/di/ServiceModule.kt b/app/src/main/java/com/hsLink/hslink/data/di/ServiceModule.kt index 694d57e..81a5c0b 100644 --- a/app/src/main/java/com/hsLink/hslink/data/di/ServiceModule.kt +++ b/app/src/main/java/com/hsLink/hslink/data/di/ServiceModule.kt @@ -1,6 +1,7 @@ package com.hsLink.hslink.data.di import com.hsLink.hslink.data.service.DummyService +import com.hsLink.hslink.data.service.mypage.MypageService import com.hsLink.hslink.data.service.search.SearchService import dagger.Module import dagger.Provides @@ -23,5 +24,10 @@ object ServiceModule { fun provideSearchService(retrofit: Retrofit): SearchService { return retrofit.create(SearchService::class.java) } + @Provides + @Singleton + fun provideMypageService(retrofit: Retrofit): MypageService { + return retrofit.create(MypageService::class.java) + } } \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/MyPageUserProfileDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/MyPageUserProfileDto.kt new file mode 100644 index 0000000..e4b1bce --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/MyPageUserProfileDto.kt @@ -0,0 +1,16 @@ +package com.hsLink.hslink.data.dto.response.mypage + +import kotlinx.serialization.Serializable + +@Serializable +data class MyPageUserProfileDto( + val userId: Long, + val studentNumber: String, + val name: String, + val major: String, + val mentor: Boolean, + val jobSeeking: Boolean, + val academicStatus: String, + val careers: List, + val links: List +) \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/mypage/MypageRepositoryImpl.kt b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/mypage/MypageRepositoryImpl.kt new file mode 100644 index 0000000..c5dba70 --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/mypage/MypageRepositoryImpl.kt @@ -0,0 +1,32 @@ +// data/repositoryimpl/mypage/MypageRepositoryImpl.kt +package com.hsLink.hslink.data.repositoryimpl.mypage + +import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto +import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto +import com.hsLink.hslink.data.service.mypage.MypageService +import com.hsLink.hslink.domain.repository.mypage.MypageRepository +import javax.inject.Inject + +class MypageRepositoryImpl @Inject constructor( + private val mypageService: MypageService +) : MypageRepository { + + override suspend fun getUserProfile(): Result { + return try { + val response = mypageService.getUserProfile() + if (response.isSuccessful) { + response.body()?.let { baseResponse -> + if (baseResponse.isSuccess) { + Result.success(baseResponse.result) + } else { + Result.failure(Exception(baseResponse.message)) + } + } ?: Result.failure(Exception("응답이 비어있습니다")) + } else { + Result.failure(Exception("API 호출 실패: ${response.code()}")) + } + } catch (e: Exception) { + Result.failure(e) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/data/service/mypage/MypageService.kt b/app/src/main/java/com/hsLink/hslink/data/service/mypage/MypageService.kt new file mode 100644 index 0000000..e290bd6 --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/data/service/mypage/MypageService.kt @@ -0,0 +1,13 @@ +// data/service/mypage/MypageService.kt +package com.hsLink.hslink.data.service.mypage + +import com.hsLink.hslink.core.network.BaseResponse +import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto +import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto +import retrofit2.Response +import retrofit2.http.GET + +interface MypageService { + @GET("users/myprofile") + suspend fun getUserProfile(): Response> +} \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/domain/repository/mypage/MypageRepository.kt b/app/src/main/java/com/hsLink/hslink/domain/repository/mypage/MypageRepository.kt new file mode 100644 index 0000000..6cdff1f --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/domain/repository/mypage/MypageRepository.kt @@ -0,0 +1,9 @@ +// domain/repository/mypage/MypageRepository.kt +package com.hsLink.hslink.domain.repository.mypage + +import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto +import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto + +interface MypageRepository { + suspend fun getUserProfile(): Result +} \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt index 209a227..7e5bbf4 100644 --- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt @@ -10,19 +10,39 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import com.hsLink.hslink.R import com.hsLink.hslink.core.designsystem.component.HsLinkTopBar import com.hsLink.hslink.core.designsystem.theme.HsLinkTheme +import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto +import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto import com.hsLink.hslink.presentation.mypage.component.main.MyPageCardItemContainer import com.hsLink.hslink.presentation.mypage.component.main.MyPageDetailItemContent import com.hsLink.hslink.presentation.mypage.component.main.MyPageItemData import com.hsLink.hslink.presentation.mypage.navigation.profile.navigateToProfileEdit +import com.hsLink.hslink.presentation.mypage.viewmodel.MypageViewModel + +// 상태 텍스트 만드는 함수 +private fun buildStatusText(jobSeeking: Boolean, academicStatus: String): String { + val jobText = if (jobSeeking) "구직 중" else "구직 중 아님" + val academicText = when (academicStatus) { + "ENROLLED" -> "재학중" + "GRADUATED" -> "졸업" + "EXPECTED_GRADUATION" -> "졸업예정" + "COMPLETED" -> "수료" + "LEAVE_OF_ABSENCE" -> "휴학" + else -> academicStatus + } + return "$jobText · $academicText" +} @Preview(showBackground = true) @Composable @@ -34,9 +54,17 @@ private fun MypageScreenPreview() { fun MypageRoute( paddingValues: PaddingValues, navController: NavController, + viewModel: MypageViewModel = hiltViewModel() // ← ViewModel 추가 ) { + val userProfile by viewModel.userProfile.collectAsState() + val isLoading by viewModel.isLoading.collectAsState() + val error by viewModel.error.collectAsState() + MypageScreen( paddingValues = paddingValues, + userProfile = userProfile, // ← 데이터 전달 + isLoading = isLoading, + error = error, onNavigateToProfile = { navController.navigateToProfileEdit() } @@ -47,11 +75,14 @@ fun MypageRoute( fun MypageScreen( modifier: Modifier = Modifier, paddingValues: PaddingValues, - onNavigateToProfile: () -> Unit = {}, // 프로필로 이동 - onNavigateToPosts: () -> Unit = {}, // 게시글로 이동 - onNavigateToSettings: () -> Unit = {}, // 설정으로 이동 - onLogout: () -> Unit = {}, // 로그아웃 - onQuit: () -> Unit = {}, // 탈퇴 + userProfile: MyPageUserProfileDto? = null, + isLoading: Boolean = false, + error: String? = null, + onNavigateToProfile: () -> Unit = {}, + onNavigateToPosts: () -> Unit = {}, + onNavigateToSettings: () -> Unit = {}, + onLogout: () -> Unit = {}, + onQuit: () -> Unit = {}, ) { LazyColumn( modifier = modifier @@ -81,9 +112,14 @@ fun MypageScreen( item { MyPageDetailItemContent( - name = "송효재", - title = "21학번 회계재무경영", - subtitle = "구직 중 · 재직 중 · 졸업", + name = userProfile?.name ?: "로딩중...", // ← API 데이터 사용 + title = if (userProfile != null) { + "${userProfile.studentNumber}학번 ${userProfile.major}" + } else "로딩중...", + subtitle = if (userProfile != null) { + // jobSeeking, employed, academicStatus로 상태 텍스트 만들기 + buildStatusText(userProfile.jobSeeking, userProfile.academicStatus) + } else "로딩중...", onClick = onNavigateToProfile ) } diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt new file mode 100644 index 0000000..d2f3aec --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt @@ -0,0 +1,52 @@ +// presentation/mypage/viewmodel/MypageViewModel.kt +package com.hsLink.hslink.presentation.mypage.viewmodel + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto +import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto +import com.hsLink.hslink.domain.repository.mypage.MypageRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class MypageViewModel @Inject constructor( + private val mypageRepository: MypageRepository +) : ViewModel() { + + private val _userProfile = MutableStateFlow(null) + val userProfile: StateFlow = _userProfile.asStateFlow() + + private val _isLoading = MutableStateFlow(false) + val isLoading: StateFlow = _isLoading.asStateFlow() + + private val _error = MutableStateFlow(null) + val error: StateFlow = _error.asStateFlow() + + init { + getUserProfile() + } + + private fun getUserProfile() { + viewModelScope.launch { + _isLoading.value = true + Log.d("MypageViewModel", "API 호출 시작") // <- 로그 추가 + mypageRepository.getUserProfile() + .onSuccess { profile -> + Log.d("MypageViewModel", "API 성공: ${profile.name}") // <- 로그 추가 + _userProfile.value = profile + _error.value = null + } + .onFailure { exception -> + Log.e("MypageViewModel", "API 실패: ${exception.message}") // <- 로그 추가 + _error.value = exception.message + } + _isLoading.value = false + } + } +} \ No newline at end of file From 78bc09e0eb2f4b980beeca938b98d32edc0bc0e3 Mon Sep 17 00:00:00 2001 From: Kyu hyunSung Date: Wed, 12 Nov 2025 23:53:11 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat/#28:=20PATCH=20/users/myprofile=20API?= =?UTF-8?q?=20=EC=97=B0=EB=8F=99=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/mypage/UpdateProfileRequestDto.kt | 13 ++++ .../response/mypage/ProfileUpdateResponse.kt | 10 +++ .../mypage/MypageRepositoryImpl.kt | 14 ++++ .../data/service/mypage/MypageService.kt | 11 ++- .../repository/mypage/MypageRepository.kt | 4 + .../screen/profile/ProfileEditScreen.kt | 75 +++++++++++++++++-- .../mypage/viewmodel/MypageViewModel.kt | 25 +++++++ 7 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/hsLink/hslink/data/dto/request/mypage/UpdateProfileRequestDto.kt create mode 100644 app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/ProfileUpdateResponse.kt diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/request/mypage/UpdateProfileRequestDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/request/mypage/UpdateProfileRequestDto.kt new file mode 100644 index 0000000..555a15f --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/data/dto/request/mypage/UpdateProfileRequestDto.kt @@ -0,0 +1,13 @@ +// data/dto/request/mypage/UpdateProfileRequestDto.kt +package com.hsLink.hslink.data.dto.request.mypage + +import kotlinx.serialization.Serializable + +@Serializable +data class UpdateProfileRequestDto( + val studentNumber: String? = null, + val name: String? = null, + val major: String? = null, + val mentor: Boolean? = null, + val jobSeeking: Boolean? = null +) \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/ProfileUpdateResponse.kt b/app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/ProfileUpdateResponse.kt new file mode 100644 index 0000000..59b7e28 --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/ProfileUpdateResponse.kt @@ -0,0 +1,10 @@ +package com.hsLink.hslink.data.dto.response.mypage + +import kotlinx.serialization.Serializable + +@Serializable +data class ProfileUpdateResponse( + val isSuccess: Boolean, + val code: String, + val message: String +) \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/mypage/MypageRepositoryImpl.kt b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/mypage/MypageRepositoryImpl.kt index c5dba70..8be7a4b 100644 --- a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/mypage/MypageRepositoryImpl.kt +++ b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/mypage/MypageRepositoryImpl.kt @@ -1,6 +1,7 @@ // data/repositoryimpl/mypage/MypageRepositoryImpl.kt package com.hsLink.hslink.data.repositoryimpl.mypage +import com.hsLink.hslink.data.dto.request.mypage.UpdateProfileRequestDto import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto import com.hsLink.hslink.data.service.mypage.MypageService @@ -29,4 +30,17 @@ class MypageRepositoryImpl @Inject constructor( Result.failure(e) } } + + override suspend fun updateProfile(request: UpdateProfileRequestDto): Result { + return try { + val response = mypageService.updateProfile(request) + if (response.isSuccess) { + Result.success(Unit) + } else { + Result.failure(Exception(response.message)) + } + } catch (e: Exception) { + Result.failure(e) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/data/service/mypage/MypageService.kt b/app/src/main/java/com/hsLink/hslink/data/service/mypage/MypageService.kt index e290bd6..d42b3ba 100644 --- a/app/src/main/java/com/hsLink/hslink/data/service/mypage/MypageService.kt +++ b/app/src/main/java/com/hsLink/hslink/data/service/mypage/MypageService.kt @@ -2,12 +2,21 @@ package com.hsLink.hslink.data.service.mypage import com.hsLink.hslink.core.network.BaseResponse +import com.hsLink.hslink.data.dto.request.mypage.UpdateProfileRequestDto import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto +import com.hsLink.hslink.data.dto.response.mypage.ProfileUpdateResponse import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto import retrofit2.Response +import retrofit2.http.Body import retrofit2.http.GET +import retrofit2.http.PATCH interface MypageService { @GET("users/myprofile") suspend fun getUserProfile(): Response> -} \ No newline at end of file + + @PATCH("users/myprofile") + suspend fun updateProfile( + @Body request: UpdateProfileRequestDto + ): ProfileUpdateResponse +} diff --git a/app/src/main/java/com/hsLink/hslink/domain/repository/mypage/MypageRepository.kt b/app/src/main/java/com/hsLink/hslink/domain/repository/mypage/MypageRepository.kt index 6cdff1f..4f9ff6c 100644 --- a/app/src/main/java/com/hsLink/hslink/domain/repository/mypage/MypageRepository.kt +++ b/app/src/main/java/com/hsLink/hslink/domain/repository/mypage/MypageRepository.kt @@ -1,9 +1,13 @@ // domain/repository/mypage/MypageRepository.kt package com.hsLink.hslink.domain.repository.mypage +import com.hsLink.hslink.data.dto.request.mypage.UpdateProfileRequestDto import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto interface MypageRepository { suspend fun getUserProfile(): Result + + suspend fun updateProfile(request: UpdateProfileRequestDto): Result + } \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/profile/ProfileEditScreen.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/profile/ProfileEditScreen.kt index 030da98..580b2bf 100644 --- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/profile/ProfileEditScreen.kt +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/profile/ProfileEditScreen.kt @@ -1,5 +1,6 @@ package com.hsLink.hslink.presentation.mypage.screen.profile +import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable @@ -18,6 +19,8 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -30,6 +33,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import com.hsLink.hslink.R import com.hsLink.hslink.core.designsystem.component.HsLinkActionButton @@ -39,10 +43,12 @@ import com.hsLink.hslink.core.designsystem.component.HsLinkSelectButton import com.hsLink.hslink.core.designsystem.component.HsLinkTextField import com.hsLink.hslink.core.designsystem.component.HsLinkTopBar import com.hsLink.hslink.core.designsystem.theme.HsLinkTheme +import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto import com.hsLink.hslink.presentation.mypage.component.profile.CareerCard import com.hsLink.hslink.presentation.mypage.component.profile.SNSCard import com.hsLink.hslink.presentation.mypage.navigation.career.navigateToCareerEdit import com.hsLink.hslink.presentation.mypage.navigation.sns.navigateToSNSEdit +import com.hsLink.hslink.presentation.mypage.viewmodel.MypageViewModel enum class MajorType(val displayName: String) { ACCOUNTING("회계재무경영"), @@ -55,6 +61,8 @@ enum class MajorType(val displayName: String) { BRAND("브랜드 디자인"), } + + @Preview(showBackground = true) @Composable private fun ProfileEditScreenPreview() { @@ -67,22 +75,41 @@ private fun ProfileEditScreenPreview() { } } +// ProfileEditScreenRoute 수정 @Composable fun ProfileEditScreenRoute( paddingValues: PaddingValues, navController: NavController, onBackClick: () -> Unit, onCloseClick: () -> Unit, + viewModel: MypageViewModel = hiltViewModel() // <- ViewModel 추가 ) { + val userProfile by viewModel.userProfile.collectAsState() + val isLoading by viewModel.isLoading.collectAsState() + ProfileEditScreen( paddingValues = paddingValues, + userProfile = userProfile, // <- 기존 데이터 전달 + isLoading = isLoading, onBackClick = onBackClick, onCloseClick = onCloseClick, + onSaveClick = { studentNumber, name, major, mentor, jobSeeking -> + // ViewModel의 updateProfile 호출 + viewModel.updateProfile( + studentNumber = studentNumber, + name = name, + major = major, + mentor = mentor, + jobSeeking = jobSeeking + ) + // 저장 후 뒤로 가기 + onBackClick() + }, onCareerClick = { navController.navigateToCareerEdit() }, onSNSClick = { - navController.navigateToSNSEdit() // ← 추가 + navController.navigateToSNSEdit() } ) } @@ -91,12 +118,15 @@ fun ProfileEditScreenRoute( fun ProfileEditScreen( modifier: Modifier = Modifier, paddingValues: PaddingValues, + userProfile: MyPageUserProfileDto? = null, // <- 추가 + isLoading: Boolean = false, // <- 추가 onBackClick: () -> Unit, onCloseClick: () -> Unit, - onSaveClick: () -> Unit = {}, + onSaveClick: (String?, String?, String?, Boolean?, Boolean?) -> Unit = { _, _, _, _, _ -> }, onCareerClick: () -> Unit = {}, onSNSClick: () -> Unit = {}, ) { + var studentId by remember { mutableStateOf("") } var isStudentIdFocused by remember { mutableStateOf(false) } @@ -111,6 +141,19 @@ fun ProfileEditScreen( var selectedJobStatus by remember { mutableStateOf("") } + // userProfile이 변경될 때마다 상태 업데이트 + LaunchedEffect(userProfile) { + Log.d("ProfileEditScreen", "LaunchedEffect 실행: ${userProfile?.name}") + userProfile?.let { profile -> + studentId = profile.studentNumber + name = profile.name + selectedMajor = profile.major + selectedMentorType = if (profile.mentor) "mentor" else "mentee" + selectedJobStatus = if (profile.jobSeeking) "seeking" else "not_seeking" + Log.d("ProfileEditScreen", "상태 업데이트 완료: $name") + } + } + fun isFormValid(): Boolean { return studentId.isNotEmpty() && name.isNotEmpty() && @@ -162,7 +205,11 @@ fun ProfileEditScreen( HsLinkTextField( value = studentId, placeholder = "21311114", - onValueChanged = { studentId = it }, + onValueChanged = { + if (it.length <= 10) { // <- 길이 제한 + studentId = it + } + }, borderColor = if (isStudentIdFocused) { HsLinkTheme.colors.DeepBlue500 } else { @@ -327,7 +374,7 @@ fun ProfileEditScreen( selectedMentorType = "mentor" }, size = HsLinkButtonSize.Large, - isSelected = selectedMentorType == "", + isSelected = selectedMentorType == "mentor", modifier = Modifier.fillMaxWidth() ) @@ -389,7 +436,7 @@ fun ProfileEditScreen( selectedJobStatus = "seeking" }, size = HsLinkButtonSize.Large, - isSelected = selectedJobStatus == "", + isSelected = selectedJobStatus == "seeking", modifier = Modifier.fillMaxWidth() ) @@ -409,8 +456,22 @@ fun ProfileEditScreen( HsLinkActionButton( label = "수정완료", onClick = { - // 수정 완료 로직 - onSaveClick() + // 실제 값들 전달 + onSaveClick( + studentId.takeIf { it.isNotEmpty() }, + name.takeIf { it.isNotEmpty() }, + selectedMajor.takeIf { it.isNotEmpty() }, + when(selectedMentorType) { + "mentor" -> true + "mentee" -> false + else -> null + }, + when(selectedJobStatus) { + "seeking" -> true + "not_seeking" -> false + else -> null + } + ) }, size = HsLinkActionButtonSize.Large, isEnabled = isFormValid(), // ← 폼 유효성 검사 diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt index d2f3aec..ad91738 100644 --- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt @@ -4,6 +4,7 @@ package com.hsLink.hslink.presentation.mypage.viewmodel import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.hsLink.hslink.data.dto.request.mypage.UpdateProfileRequestDto import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto import com.hsLink.hslink.domain.repository.mypage.MypageRepository @@ -49,4 +50,28 @@ class MypageViewModel @Inject constructor( _isLoading.value = false } } + + fun updateProfile( + studentNumber: String? = null, + name: String? = null, + major: String? = null, + mentor: Boolean? = null, + jobSeeking: Boolean? = null + ) { + viewModelScope.launch { + _isLoading.value = true + val request = UpdateProfileRequestDto(studentNumber, name, major, mentor, jobSeeking) + mypageRepository.updateProfile(request) + .onSuccess { + Log.d("MypageViewModel", "프로필 수정 성공") + // 수정 후 다시 조회 + getUserProfile() + } + .onFailure { exception -> + Log.e("MypageViewModel", "프로필 수정 실패: ${exception.message}") + _error.value = exception.message + } + _isLoading.value = false + } + } } \ No newline at end of file From a805480d692df25ca50f5004c924a58dfb4d9276 Mon Sep 17 00:00:00 2001 From: Kyu hyunSung Date: Thu, 13 Nov 2025 00:18:30 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat/#28:=20=20GET=20/users/summary=20API?= =?UTF-8?q?=20=EC=97=B0=EB=8F=99=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/mypage/MyPageUserSummaryDto.kt | 15 ++++++++++ .../mypage/MypageRepositoryImpl.kt | 14 +++++++++ .../data/service/mypage/MypageService.kt | 4 +++ .../repository/mypage/MypageRepository.kt | 3 ++ .../mypage/screen/main/MypageScreen.kt | 25 ++++++++-------- .../screen/profile/ProfileEditScreen.kt | 7 ++++- .../mypage/viewmodel/MypageViewModel.kt | 29 ++++++++++++++++++- 7 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/MyPageUserSummaryDto.kt diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/MyPageUserSummaryDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/MyPageUserSummaryDto.kt new file mode 100644 index 0000000..742c287 --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/data/dto/response/mypage/MyPageUserSummaryDto.kt @@ -0,0 +1,15 @@ +package com.hsLink.hslink.data.dto.response.mypage + +import kotlinx.serialization.Serializable + + +@Serializable +data class MyPageUserSummaryDto( + val userId: Long, + val name: String, + val studentNumberPrefix: String, // 학번 앞 두 자리 (예: "21") + val major: String, + val jobSeeking: Boolean, + val academicStatus: String, // <- String으로 변경 + val employed: Boolean +) \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/mypage/MypageRepositoryImpl.kt b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/mypage/MypageRepositoryImpl.kt index 8be7a4b..010eb2d 100644 --- a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/mypage/MypageRepositoryImpl.kt +++ b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/mypage/MypageRepositoryImpl.kt @@ -3,6 +3,7 @@ package com.hsLink.hslink.data.repositoryimpl.mypage import com.hsLink.hslink.data.dto.request.mypage.UpdateProfileRequestDto import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto +import com.hsLink.hslink.data.dto.response.mypage.MyPageUserSummaryDto import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto import com.hsLink.hslink.data.service.mypage.MypageService import com.hsLink.hslink.domain.repository.mypage.MypageRepository @@ -43,4 +44,17 @@ class MypageRepositoryImpl @Inject constructor( Result.failure(e) } } + + override suspend fun getUserSummary(): Result { + return try { + val response = mypageService.getUserSummary() + if (response.isSuccess) { + Result.success(response.result) + } else { + Result.failure(Exception(response.message)) + } + } catch (e: Exception) { + Result.failure(e) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/data/service/mypage/MypageService.kt b/app/src/main/java/com/hsLink/hslink/data/service/mypage/MypageService.kt index d42b3ba..8cbd1fe 100644 --- a/app/src/main/java/com/hsLink/hslink/data/service/mypage/MypageService.kt +++ b/app/src/main/java/com/hsLink/hslink/data/service/mypage/MypageService.kt @@ -4,6 +4,7 @@ package com.hsLink.hslink.data.service.mypage import com.hsLink.hslink.core.network.BaseResponse import com.hsLink.hslink.data.dto.request.mypage.UpdateProfileRequestDto import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto +import com.hsLink.hslink.data.dto.response.mypage.MyPageUserSummaryDto import com.hsLink.hslink.data.dto.response.mypage.ProfileUpdateResponse import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto import retrofit2.Response @@ -19,4 +20,7 @@ interface MypageService { suspend fun updateProfile( @Body request: UpdateProfileRequestDto ): ProfileUpdateResponse + + @GET("users/summary") + suspend fun getUserSummary(): BaseResponse } diff --git a/app/src/main/java/com/hsLink/hslink/domain/repository/mypage/MypageRepository.kt b/app/src/main/java/com/hsLink/hslink/domain/repository/mypage/MypageRepository.kt index 4f9ff6c..fc64f4e 100644 --- a/app/src/main/java/com/hsLink/hslink/domain/repository/mypage/MypageRepository.kt +++ b/app/src/main/java/com/hsLink/hslink/domain/repository/mypage/MypageRepository.kt @@ -3,6 +3,7 @@ package com.hsLink.hslink.domain.repository.mypage import com.hsLink.hslink.data.dto.request.mypage.UpdateProfileRequestDto import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto +import com.hsLink.hslink.data.dto.response.mypage.MyPageUserSummaryDto import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto interface MypageRepository { @@ -10,4 +11,6 @@ interface MypageRepository { suspend fun updateProfile(request: UpdateProfileRequestDto): Result + suspend fun getUserSummary(): Result + } \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt index 7e5bbf4..1af3f45 100644 --- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt @@ -23,6 +23,7 @@ import com.hsLink.hslink.R import com.hsLink.hslink.core.designsystem.component.HsLinkTopBar import com.hsLink.hslink.core.designsystem.theme.HsLinkTheme import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto +import com.hsLink.hslink.data.dto.response.mypage.MyPageUserSummaryDto import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto import com.hsLink.hslink.presentation.mypage.component.main.MyPageCardItemContainer import com.hsLink.hslink.presentation.mypage.component.main.MyPageDetailItemContent @@ -31,8 +32,9 @@ import com.hsLink.hslink.presentation.mypage.navigation.profile.navigateToProfil import com.hsLink.hslink.presentation.mypage.viewmodel.MypageViewModel // 상태 텍스트 만드는 함수 -private fun buildStatusText(jobSeeking: Boolean, academicStatus: String): String { +private fun buildStatusText(jobSeeking: Boolean, academicStatus: String, employed: Boolean): String { val jobText = if (jobSeeking) "구직 중" else "구직 중 아님" + val employedText = if (employed) "재직 중" else "재직 중 아님" val academicText = when (academicStatus) { "ENROLLED" -> "재학중" "GRADUATED" -> "졸업" @@ -41,7 +43,7 @@ private fun buildStatusText(jobSeeking: Boolean, academicStatus: String): String "LEAVE_OF_ABSENCE" -> "휴학" else -> academicStatus } - return "$jobText · $academicText" + return "$jobText · $employedText · $academicText" } @Preview(showBackground = true) @@ -54,15 +56,15 @@ private fun MypageScreenPreview() { fun MypageRoute( paddingValues: PaddingValues, navController: NavController, - viewModel: MypageViewModel = hiltViewModel() // ← ViewModel 추가 + viewModel: MypageViewModel = hiltViewModel() ) { - val userProfile by viewModel.userProfile.collectAsState() + val userSummary by viewModel.userSummary.collectAsState() // ← 변경 val isLoading by viewModel.isLoading.collectAsState() val error by viewModel.error.collectAsState() MypageScreen( paddingValues = paddingValues, - userProfile = userProfile, // ← 데이터 전달 + userSummary = userSummary, // ← 변경 isLoading = isLoading, error = error, onNavigateToProfile = { @@ -75,7 +77,7 @@ fun MypageRoute( fun MypageScreen( modifier: Modifier = Modifier, paddingValues: PaddingValues, - userProfile: MyPageUserProfileDto? = null, + userSummary: MyPageUserSummaryDto? = null, // ← 변경 isLoading: Boolean = false, error: String? = null, onNavigateToProfile: () -> Unit = {}, @@ -112,13 +114,12 @@ fun MypageScreen( item { MyPageDetailItemContent( - name = userProfile?.name ?: "로딩중...", // ← API 데이터 사용 - title = if (userProfile != null) { - "${userProfile.studentNumber}학번 ${userProfile.major}" + name = userSummary?.name ?: "로딩중...", // ← 변경 + title = if (userSummary != null) { + "${userSummary.studentNumberPrefix}학번 ${userSummary.major}" // ← 변경 } else "로딩중...", - subtitle = if (userProfile != null) { - // jobSeeking, employed, academicStatus로 상태 텍스트 만들기 - buildStatusText(userProfile.jobSeeking, userProfile.academicStatus) + subtitle = if (userSummary != null) { + buildStatusText(userSummary.jobSeeking, userSummary.academicStatus, userSummary.employed) // ← employed 추가 } else "로딩중...", onClick = onNavigateToProfile ) diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/profile/ProfileEditScreen.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/profile/ProfileEditScreen.kt index 580b2bf..44aac61 100644 --- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/profile/ProfileEditScreen.kt +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/profile/ProfileEditScreen.kt @@ -82,11 +82,16 @@ fun ProfileEditScreenRoute( navController: NavController, onBackClick: () -> Unit, onCloseClick: () -> Unit, - viewModel: MypageViewModel = hiltViewModel() // <- ViewModel 추가 + viewModel: MypageViewModel = hiltViewModel() ) { val userProfile by viewModel.userProfile.collectAsState() val isLoading by viewModel.isLoading.collectAsState() + // 프로필 수정 화면 진입 시 전체 프로필 데이터 로드 + LaunchedEffect(Unit) { + viewModel.loadUserProfile() // <- 이 함수 추가 필요 + } + ProfileEditScreen( paddingValues = paddingValues, userProfile = userProfile, // <- 기존 데이터 전달 diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt index ad91738..17e9838 100644 --- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt @@ -1,11 +1,13 @@ // presentation/mypage/viewmodel/MypageViewModel.kt package com.hsLink.hslink.presentation.mypage.viewmodel +import android.content.ContentValues.TAG import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.hsLink.hslink.data.dto.request.mypage.UpdateProfileRequestDto import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto +import com.hsLink.hslink.data.dto.response.mypage.MyPageUserSummaryDto import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto import com.hsLink.hslink.domain.repository.mypage.MypageRepository import dagger.hilt.android.lifecycle.HiltViewModel @@ -30,6 +32,10 @@ class MypageViewModel @Inject constructor( val error: StateFlow = _error.asStateFlow() init { + getUserSummary() // ← getUserProfile() 대신 변경 + } + + fun loadUserProfile() { getUserProfile() } @@ -65,7 +71,7 @@ class MypageViewModel @Inject constructor( .onSuccess { Log.d("MypageViewModel", "프로필 수정 성공") // 수정 후 다시 조회 - getUserProfile() + getUserProfile() // ← 이건 그대로 유지 (전체 정보 필요) } .onFailure { exception -> Log.e("MypageViewModel", "프로필 수정 실패: ${exception.message}") @@ -74,4 +80,25 @@ class MypageViewModel @Inject constructor( _isLoading.value = false } } + private val _userSummary = MutableStateFlow(null) + val userSummary: StateFlow = _userSummary.asStateFlow() + + private fun getUserSummary() { + viewModelScope.launch { + _isLoading.value = true + Log.d(TAG, "Summary API 호출 시작") + + mypageRepository.getUserSummary().fold( + onSuccess = { summary -> + _userSummary.value = summary + Log.d(TAG, "Summary API 성공: ${summary.name}") + }, + onFailure = { exception -> + Log.e(TAG, "Summary API 실패", exception) + } + ).also { + _isLoading.value = false + } + } + } } \ No newline at end of file From 062330f31448ad1b48b0056e4aeed5de5dcc7c67 Mon Sep 17 00:00:00 2001 From: Kyu hyunSung Date: Thu, 13 Nov 2025 12:18:24 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat/#28:=20=EC=BB=A4=EB=A6=AC=EC=96=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20API=20=EC=A0=81=EC=9A=A9=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @GET("careers/mycareers") @GET("careers/{careerId}") @PUT("careers/{careerId}") --- .../hsLink/hslink/data/di/NetworkModule.kt | 6 + .../hsLink/hslink/data/di/RepositoryModule.kt | 23 +++- .../hslink/data/dto/request/common/JobType.kt | 12 ++ .../dto/request/onboarding/CareerRequest.kt | 12 +- .../data/dto/response/CareerResponse.kt | 13 +++ .../repositoryimpl/CareerRepositoryImpl.kt | 50 ++++++++ .../hslink/data/service/CareerService.kt | 27 +++++ .../domain/repository/CareerRepository.kt | 10 ++ .../navigation/career/CareerEditNavigation.kt | 18 ++- .../mypage/screen/career/CareerEditScreen.kt | 109 +++++++++++++----- .../screen/profile/ProfileEditScreen.kt | 45 +++++--- .../mypage/viewmodel/CareerViewModel.kt | 92 +++++++++++++++ 12 files changed, 367 insertions(+), 50 deletions(-) create mode 100644 app/src/main/java/com/hsLink/hslink/data/dto/request/common/JobType.kt create mode 100644 app/src/main/java/com/hsLink/hslink/data/repositoryimpl/CareerRepositoryImpl.kt create mode 100644 app/src/main/java/com/hsLink/hslink/data/service/CareerService.kt create mode 100644 app/src/main/java/com/hsLink/hslink/domain/repository/CareerRepository.kt create mode 100644 app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/CareerViewModel.kt diff --git a/app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt b/app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt index 55b58a2..37694dd 100644 --- a/app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt +++ b/app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt @@ -2,6 +2,7 @@ package com.hsLink.hslink.data.di import com.hsLink.hslink.BuildConfig import com.hsLink.hslink.data.remote.AuthInterceptor +import com.hsLink.hslink.data.service.CareerService import com.hsLink.hslink.data.service.commuunity.CommunityPostService import com.hsLink.hslink.data.service.home.PostService import com.hsLink.hslink.data.service.login.AuthService @@ -76,4 +77,9 @@ object NetworkModule { fun provideOnboardingService(retrofit: Retrofit): OnboardingService { return retrofit.create(OnboardingService::class.java) } + @Provides + @Singleton + fun provideCareerService(retrofit: Retrofit): CareerService { + return retrofit.create(CareerService::class.java) + } } \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt b/app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt index 18a6b30..b85a144 100644 --- a/app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt +++ b/app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt @@ -1,14 +1,20 @@ package com.hsLink.hslink.data.di import com.hsLink.hslink.data.repositoryimpl.AuthRepositoryImpl +import com.hsLink.hslink.data.repositoryimpl.CareerRepositoryImpl import com.hsLink.hslink.data.repositoryimpl.CommunityRepositoryImpl import com.hsLink.hslink.data.repositoryimpl.DummyRepositoryImpl import com.hsLink.hslink.data.repositoryimpl.home.PostRepositoryImpl +import com.hsLink.hslink.data.repositoryimpl.mypage.MypageRepositoryImpl +import com.hsLink.hslink.data.repositoryimpl.onboarding.OnboardingRepositoryImpl import com.hsLink.hslink.data.repositoryimpl.search.SearchRepositoryImpl import com.hsLink.hslink.domain.DummyRepository import com.hsLink.hslink.domain.repository.AuthRepository +import com.hsLink.hslink.domain.repository.CareerRepository import com.hsLink.hslink.domain.repository.community.CommunityRepository import com.hsLink.hslink.domain.repository.home.PostRepository +import com.hsLink.hslink.domain.repository.mypage.MypageRepository +import com.hsLink.hslink.domain.repository.onboarding.OnboardingRepository import com.hsLink.hslink.domain.repository.search.SearchRepository import dagger.Binds import dagger.Module @@ -45,4 +51,19 @@ interface RepositoryModule { fun bindsSearchRepository( searchRepositoryImpl: SearchRepositoryImpl, ): SearchRepository -} + + @Binds + fun bindMypageRepository( + mypageRepositoryImpl: MypageRepositoryImpl + ): MypageRepository + + @Binds + fun bindsOnboardingRepository( + onboardingRepositoryImpl: OnboardingRepositoryImpl, + ): OnboardingRepository + + @Binds + fun bindCareerRepository( + careerRepositoryImpl: CareerRepositoryImpl + ): CareerRepository +} \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/request/common/JobType.kt b/app/src/main/java/com/hsLink/hslink/data/dto/request/common/JobType.kt new file mode 100644 index 0000000..ac5cdd2 --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/data/dto/request/common/JobType.kt @@ -0,0 +1,12 @@ +package com.hsLink.hslink.data.dto.request.common + +import kotlinx.serialization.Serializable + +// data/dto/common/JobType.kt +@Serializable +enum class JobType { + PERMANENT, // 정규직 + TEMPORARY, // 계약직 + INTERN, // 인턴 + FREELANCER // 프리랜서 +} \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/request/onboarding/CareerRequest.kt b/app/src/main/java/com/hsLink/hslink/data/dto/request/onboarding/CareerRequest.kt index b0d0f58..2924d2f 100644 --- a/app/src/main/java/com/hsLink/hslink/data/dto/request/onboarding/CareerRequest.kt +++ b/app/src/main/java/com/hsLink/hslink/data/dto/request/onboarding/CareerRequest.kt @@ -27,4 +27,14 @@ data class LinkRequest( val type: LinkType, @SerialName("url") val url: String -) \ No newline at end of file +) +@Serializable +data class CareerUpdateRequestDto( + val companyName: String, + val position: String, + val jobType: JobType, + val startYm: String, + val endYm: String?, + val employed: Boolean +) + diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/response/CareerResponse.kt b/app/src/main/java/com/hsLink/hslink/data/dto/response/CareerResponse.kt index 985b98d..1be37d8 100644 --- a/app/src/main/java/com/hsLink/hslink/data/dto/response/CareerResponse.kt +++ b/app/src/main/java/com/hsLink/hslink/data/dto/response/CareerResponse.kt @@ -25,6 +25,19 @@ data class LinkResponse( @SerialName("url") val url: String, ) + +@Serializable +data class CareerDto( + val id: Long, + val companyName: String, + val position: String, + val jobType: JobType, + val employed: Boolean, + val startYm: String, + val endYm: String? +) + + typealias CareerListResponseDto = List @Serializable diff --git a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/CareerRepositoryImpl.kt b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/CareerRepositoryImpl.kt new file mode 100644 index 0000000..3d7c523 --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/CareerRepositoryImpl.kt @@ -0,0 +1,50 @@ +package com.hsLink.hslink.data.repositoryimpl + +import com.hsLink.hslink.data.dto.request.onboarding.CareerUpdateRequestDto +import com.hsLink.hslink.data.dto.response.onboarding.CareerDto +import com.hsLink.hslink.data.service.CareerService +import com.hsLink.hslink.domain.repository.CareerRepository +import javax.inject.Inject + +class CareerRepositoryImpl @Inject constructor( + private val careerService: CareerService +) : CareerRepository { + + override suspend fun getMyCareers(): Result> { + return try { + val response = careerService.getMyCareers() + if (response.isSuccess) { + Result.success(response.result) + } else { + Result.failure(Exception(response.message)) + } + } catch (e: Exception) { + Result.failure(e) + } + } + + override suspend fun updateCareer(careerId: Long, request: CareerUpdateRequestDto): Result { + return try { + val response = careerService.updateCareer(careerId, request) + if (response.isSuccess) { + Result.success(response.result) + } else { + Result.failure(Exception(response.message)) + } + } catch (e: Exception) { + Result.failure(e) + } + } + override suspend fun getCareer(careerId: Long): Result { + return try { + val response = careerService.getCareer(careerId) + if (response.isSuccess) { + Result.success(response.result) + } else { + Result.failure(Exception(response.message)) + } + } catch (e: Exception) { + Result.failure(e) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/data/service/CareerService.kt b/app/src/main/java/com/hsLink/hslink/data/service/CareerService.kt new file mode 100644 index 0000000..c9628af --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/data/service/CareerService.kt @@ -0,0 +1,27 @@ +// data/service/CareerService.kt +package com.hsLink.hslink.data.service + +import com.hsLink.hslink.core.network.BaseResponse +import com.hsLink.hslink.data.dto.request.onboarding.CareerUpdateRequestDto +import com.hsLink.hslink.data.dto.response.onboarding.CareerDto +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.PUT +import retrofit2.http.Path + +interface CareerService { + + @GET("careers/mycareers") + suspend fun getMyCareers(): BaseResponse> + + @GET("careers/{careerId}") + suspend fun getCareer( + @Path("careerId") careerId: Long + ): BaseResponse + + @PUT("careers/{careerId}") + suspend fun updateCareer( + @Path("careerId") careerId: Long, + @Body request: CareerUpdateRequestDto + ): BaseResponse +} \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/domain/repository/CareerRepository.kt b/app/src/main/java/com/hsLink/hslink/domain/repository/CareerRepository.kt new file mode 100644 index 0000000..f360b31 --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/domain/repository/CareerRepository.kt @@ -0,0 +1,10 @@ +package com.hsLink.hslink.domain.repository + +import com.hsLink.hslink.data.dto.request.onboarding.CareerUpdateRequestDto +import com.hsLink.hslink.data.dto.response.onboarding.CareerDto + +interface CareerRepository { + suspend fun getCareer(careerId: Long): Result + suspend fun getMyCareers(): Result> + suspend fun updateCareer(careerId: Long, request: CareerUpdateRequestDto): Result +} \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/navigation/career/CareerEditNavigation.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/navigation/career/CareerEditNavigation.kt index c0b1065..34b34ea 100644 --- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/navigation/career/CareerEditNavigation.kt +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/navigation/career/CareerEditNavigation.kt @@ -9,21 +9,29 @@ import com.hsLink.hslink.core.navigation.Route import com.hsLink.hslink.presentation.mypage.screen.career.CareerEditRoute import kotlinx.serialization.Serializable -fun NavController.navigateToCareerEdit(navOptions: NavOptions? = null) { - navigate(CareerEdit, navOptions) +// ← careerId 파라미터 추가 +fun NavController.navigateToCareerEdit(careerId: Long, navOptions: NavOptions? = null) { + navigate(CareerEdit(careerId = careerId), navOptions) } fun NavGraphBuilder.careerNavGraph( padding: PaddingValues, navController: NavController, ) { - composable { + composable { backStackEntry -> + val careerEdit = backStackEntry.arguments?.let { + // ← careerId 추출 + CareerEdit(careerId = it.getLong("careerId")) + } ?: CareerEdit(careerId = 0L) + CareerEditRoute( paddingValues = padding, - navController = navController + navController = navController, + careerId = careerEdit.careerId // ← careerId 전달 ) } } +// ← data object에서 data class로 변경 @Serializable -data object CareerEdit : Route \ No newline at end of file +data class CareerEdit(val careerId: Long) : Route \ No newline at end of file diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/career/CareerEditScreen.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/career/CareerEditScreen.kt index 7f3b2a0..e9ca081 100644 --- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/career/CareerEditScreen.kt +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/career/CareerEditScreen.kt @@ -11,6 +11,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -22,6 +24,7 @@ import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import com.hsLink.hslink.R import com.hsLink.hslink.core.designsystem.component.HsLinkActionButton @@ -31,19 +34,22 @@ import com.hsLink.hslink.core.designsystem.component.HsLinkSelectButton import com.hsLink.hslink.core.designsystem.component.HsLinkTextField import com.hsLink.hslink.core.designsystem.component.HsLinkTopBar import com.hsLink.hslink.core.designsystem.theme.HsLinkTheme +import com.hsLink.hslink.data.dto.request.onboarding.CareerUpdateRequestDto +import com.hsLink.hslink.data.dto.response.onboarding.CareerDto import com.hsLink.hslink.presentation.mypage.component.career.UnsavedChangesDialog +import com.hsLink.hslink.presentation.mypage.viewmodel.CareerViewModel import com.hsLink.hslink.presentation.onboarding.model.JobType @Preview(showBackground = true) @Composable private fun CareerEditScreenPreview() { HsLinkTheme { - CareerEditScreen( - paddingValues = PaddingValues(), - onBackClick = { }, - onCloseClick = { }, - onSaveClick = { } - ) +// CareerEditScreen( +// paddingValues = PaddingValues(), +// onBackClick = { }, +// onCloseClick = { }, +// onSaveClick = { } +// ) } } @@ -51,13 +57,36 @@ private fun CareerEditScreenPreview() { fun CareerEditRoute( paddingValues: PaddingValues, navController: NavController, + careerId: Long?, // ← Long? + careerViewModel: CareerViewModel = hiltViewModel() ) { + val selectedCareer by careerViewModel.selectedCareer.collectAsState() + val isLoading by careerViewModel.isLoading.collectAsState() + + LaunchedEffect(careerId) { + careerId?.let { id -> + careerViewModel.loadCareer(id) + } + } + CareerEditScreen( paddingValues = paddingValues, + career = selectedCareer, + isLoading = isLoading, onBackClick = { navController.popBackStack() }, onCloseClick = { navController.popBackStack() }, - onSaveClick = { - // 저장 로직 후 이전 화면으로 + onSaveClick = { companyName, position, jobType, startYm, endYm, employed -> + careerId?.let { id -> // ← null 체크 + val requestDto = CareerUpdateRequestDto( // ← 실제 DTO 생성 + companyName = companyName, + position = position, + jobType = jobType, + startYm = startYm, + endYm = endYm, + employed = employed + ) + careerViewModel.updateCareer(id, requestDto) // ← 실제 API 호출 + } navController.popBackStack() } ) @@ -68,29 +97,36 @@ fun CareerEditScreen( paddingValues: PaddingValues, onBackClick: () -> Unit, onCloseClick: () -> Unit, - onSaveClick: () -> Unit, + onSaveClick: (String, String, JobType, String, String?, Boolean) -> Unit, modifier: Modifier = Modifier, + career: CareerDto? = null, // ← 추가 + isLoading: Boolean = false, // ← 추가 ) { - var startDate by remember { mutableStateOf("2024.04") } - var endDate by remember { mutableStateOf("2024.08") } - var isCurrentlyEmployed by remember { mutableStateOf(false) } - var companyName by remember { mutableStateOf("한성대학교") } - var jobName by remember { mutableStateOf("영업직") } - var selectedJobType by remember { mutableStateOf(JobType.PERMANENT) } + // 기존 하드코딩된 초기값들을 career 데이터로 교체 + var startDate by remember(career) { mutableStateOf(career?.startYm ?: "") } + var endDate by remember(career) { mutableStateOf(career?.endYm ?: "") } + var isCurrentlyEmployed by remember(career) { mutableStateOf(career?.employed ?: false) } + var companyName by remember(career) { mutableStateOf(career?.companyName ?: "") } + var jobName by remember(career) { mutableStateOf(career?.position ?: "") } + var selectedJobType by remember(career) { mutableStateOf(career?.jobType) } + // ← 누락된 Focus 상태 변수들 추가 var companyFocused by remember { mutableStateOf(false) } var jobNameFocused by remember { mutableStateOf(false) } var startDateFocused by remember { mutableStateOf(false) } var endDateFocused by remember { mutableStateOf(false) } + // ← 누락된 Dialog 상태 변수 추가 var showExitDialog by remember { mutableStateOf(false) } + // ← 수정된 hasUnsavedChanges 함수 fun hasUnsavedChanges(): Boolean { - return startDate != "2024.04" || - endDate != "2024.08" || - companyName != "한성대학교" || - jobName != "영업직" || - selectedJobType != JobType.PERMANENT + return startDate != (career?.startYm ?: "") || + endDate != (career?.endYm ?: "") || + companyName != (career?.companyName ?: "") || + jobName != (career?.position ?: "") || + selectedJobType != career?.jobType || + isCurrentlyEmployed != (career?.employed ?: false) } fun handleExit() { @@ -132,7 +168,7 @@ fun CareerEditScreen( item { Text( - text = "아래의 정보를 등록해주세요", + text = "아래의 정보를 수정해주세요", // ← 문구 수정 style = HsLinkTheme.typography.title_20Strong, color = HsLinkTheme.colors.Grey700 ) @@ -142,7 +178,7 @@ fun CareerEditScreen( Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Text( text = buildAnnotatedString { - append("현재 재직 여부 (형식 : 24.04) ") + append("재직 기간 (형식 : 24.04) ") withStyle(style = SpanStyle(color = HsLinkTheme.colors.Red500)) { append("*") } @@ -167,7 +203,11 @@ fun CareerEditScreen( HsLinkTextField( value = endDate, placeholder = "근무종료일", - onValueChanged = { endDate = it }, + onValueChanged = { + if (!isCurrentlyEmployed) { // ← 재직중이 아닐 때만 변경 허용 + endDate = it + } + }, modifier = Modifier.weight(1f), borderColor = if (endDateFocused) HsLinkTheme.colors.SkyBlue500 else HsLinkTheme.colors.Grey300, backgroundColor = HsLinkTheme.colors.Common, @@ -175,7 +215,10 @@ fun CareerEditScreen( ) HsLinkSelectButton( label = "재직중", - onClick = { isCurrentlyEmployed = !isCurrentlyEmployed }, + onClick = { + isCurrentlyEmployed = !isCurrentlyEmployed + if (isCurrentlyEmployed) endDate = "" // 재직중이면 종료일 초기화 + }, size = HsLinkButtonSize.Medium, isSelected = isCurrentlyEmployed ) @@ -297,13 +340,26 @@ fun CareerEditScreen( item { HsLinkActionButton( label = "수정완료", - onClick = onSaveClick, + onClick = { + // 폼 데이터 수집해서 onSaveClick에 전달 + selectedJobType?.let { jobType -> + onSaveClick( + companyName, // String + jobName, // String + jobType, // JobType + startDate, // String + if (isCurrentlyEmployed) null else endDate, // String? + isCurrentlyEmployed // Boolean + ) + } + }, size = HsLinkActionButtonSize.Large, - isEnabled = isFormValid, + isEnabled = isFormValid && !isLoading, modifier = Modifier.fillMaxWidth() ) } } + if (showExitDialog) { UnsavedChangesDialog( onDismiss = { showExitDialog = false }, @@ -313,5 +369,4 @@ fun CareerEditScreen( } ) } - } diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/profile/ProfileEditScreen.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/profile/ProfileEditScreen.kt index 44aac61..d15faf2 100644 --- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/profile/ProfileEditScreen.kt +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/profile/ProfileEditScreen.kt @@ -44,10 +44,14 @@ import com.hsLink.hslink.core.designsystem.component.HsLinkTextField import com.hsLink.hslink.core.designsystem.component.HsLinkTopBar import com.hsLink.hslink.core.designsystem.theme.HsLinkTheme import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto +import com.hsLink.hslink.data.dto.request.common.JobType // ← 경로 수정 +import com.hsLink.hslink.data.dto.response.onboarding.CareerDto +import com.hsLink.hslink.presentation.mypage.component.profile.CareerCard import com.hsLink.hslink.presentation.mypage.component.profile.CareerCard import com.hsLink.hslink.presentation.mypage.component.profile.SNSCard import com.hsLink.hslink.presentation.mypage.navigation.career.navigateToCareerEdit import com.hsLink.hslink.presentation.mypage.navigation.sns.navigateToSNSEdit +import com.hsLink.hslink.presentation.mypage.viewmodel.CareerViewModel import com.hsLink.hslink.presentation.mypage.viewmodel.MypageViewModel enum class MajorType(val displayName: String) { @@ -82,24 +86,30 @@ fun ProfileEditScreenRoute( navController: NavController, onBackClick: () -> Unit, onCloseClick: () -> Unit, - viewModel: MypageViewModel = hiltViewModel() + viewModel: MypageViewModel = hiltViewModel(), + careerViewModel: CareerViewModel = hiltViewModel() // ← 추가 + ) { val userProfile by viewModel.userProfile.collectAsState() val isLoading by viewModel.isLoading.collectAsState() + val careers by careerViewModel.careers.collectAsState() // ← 추가 + val isCareerLoading by careerViewModel.isLoading.collectAsState() // ← 추가 // 프로필 수정 화면 진입 시 전체 프로필 데이터 로드 LaunchedEffect(Unit) { - viewModel.loadUserProfile() // <- 이 함수 추가 필요 + viewModel.loadUserProfile() + careerViewModel.loadMyCareers() } ProfileEditScreen( paddingValues = paddingValues, - userProfile = userProfile, // <- 기존 데이터 전달 + userProfile = userProfile, + careers = careers, // ← 추가 isLoading = isLoading, + isCareerLoading = isCareerLoading, // ← 추가 onBackClick = onBackClick, onCloseClick = onCloseClick, onSaveClick = { studentNumber, name, major, mentor, jobSeeking -> - // ViewModel의 updateProfile 호출 viewModel.updateProfile( studentNumber = studentNumber, name = name, @@ -107,11 +117,10 @@ fun ProfileEditScreenRoute( mentor = mentor, jobSeeking = jobSeeking ) - // 저장 후 뒤로 가기 onBackClick() }, - onCareerClick = { - navController.navigateToCareerEdit() + onCareerClick = { career -> + navController.navigateToCareerEdit(careerId = career.id) }, onSNSClick = { navController.navigateToSNSEdit() @@ -123,12 +132,14 @@ fun ProfileEditScreenRoute( fun ProfileEditScreen( modifier: Modifier = Modifier, paddingValues: PaddingValues, - userProfile: MyPageUserProfileDto? = null, // <- 추가 - isLoading: Boolean = false, // <- 추가 + userProfile: MyPageUserProfileDto? = null, + isLoading: Boolean = false, + careers: List = emptyList(), + isCareerLoading: Boolean = false, onBackClick: () -> Unit, onCloseClick: () -> Unit, onSaveClick: (String?, String?, String?, Boolean?, Boolean?) -> Unit = { _, _, _, _, _ -> }, - onCareerClick: () -> Unit = {}, + onCareerClick: (CareerDto) -> Unit = {}, onSNSClick: () -> Unit = {}, ) { @@ -344,12 +355,14 @@ fun ProfileEditScreen( } Box { - CareerCard( - name = "투썸플레이스", - title = "영업", - dateRange = "2024.02 ~ 2024.10", // ← subtitle을 dateRange로 변경 - onClick = onCareerClick - ) + careers.forEach { career -> + CareerCard( + name = career.companyName, + title = career.position, + dateRange = "${career.startYm} ~ ${career.endYm ?: "현재"}", + onClick = { onCareerClick(career) } + ) + } } } } diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/CareerViewModel.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/CareerViewModel.kt new file mode 100644 index 0000000..89572a6 --- /dev/null +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/CareerViewModel.kt @@ -0,0 +1,92 @@ +package com.hsLink.hslink.presentation.mypage.viewmodel + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.hsLink.hslink.data.dto.request.onboarding.CareerUpdateRequestDto +import com.hsLink.hslink.data.dto.response.onboarding.CareerDto +import com.hsLink.hslink.domain.repository.CareerRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class CareerViewModel @Inject constructor( + private val careerRepository: CareerRepository +) : ViewModel() { + + private val _careers = MutableStateFlow>(emptyList()) + val careers: StateFlow> = _careers.asStateFlow() + + private val _selectedCareer = MutableStateFlow(null) + val selectedCareer: StateFlow = _selectedCareer.asStateFlow() + + private val _isLoading = MutableStateFlow(false) + val isLoading: StateFlow = _isLoading.asStateFlow() + + private val _error = MutableStateFlow(null) + val error: StateFlow = _error.asStateFlow() + + fun loadMyCareers() { + viewModelScope.launch { + _isLoading.value = true + Log.d("CareerViewModel", "커리어 목록 조회 시작") + + careerRepository.getMyCareers().fold( + onSuccess = { careerList -> + _careers.value = careerList + _error.value = null + Log.d("CareerViewModel", "커리어 조회 성공: ${careerList.size}개") + }, + onFailure = { exception -> + _error.value = exception.message + Log.e("CareerViewModel", "커리어 조회 실패", exception) + } + ) + _isLoading.value = false + } + } + + fun updateCareer(careerId: Long, request: CareerUpdateRequestDto) { + viewModelScope.launch { + _isLoading.value = true + Log.d("CareerViewModel", "커리어 수정 시작: $careerId") + + careerRepository.updateCareer(careerId, request).fold( + onSuccess = { + Log.d("CareerViewModel", "커리어 수정 성공") + // 수정 후 목록 다시 조회 + loadMyCareers() + }, + onFailure = { exception -> + _error.value = exception.message + Log.e("CareerViewModel", "커리어 수정 실패", exception) + } + ) + _isLoading.value = false + } + } + + fun loadCareer(careerId: Long) { + viewModelScope.launch { + _isLoading.value = true + Log.d("CareerViewModel", "커리어 단건 조회 시작: $careerId") + + careerRepository.getCareer(careerId).fold( + onSuccess = { career -> + _selectedCareer.value = career + _error.value = null + Log.d("CareerViewModel", "커리어 조회 성공: ${career.companyName}") + }, + onFailure = { exception -> + _error.value = exception.message + Log.e("CareerViewModel", "커리어 조회 실패", exception) + } + ) + _isLoading.value = false + } + } +} \ No newline at end of file From a996816e5d73261adffbfc05521ac5c75aca9590 Mon Sep 17 00:00:00 2001 From: Kyu hyunSung Date: Thu, 13 Nov 2025 12:30:15 +0900 Subject: [PATCH 6/6] feat/#28: restore data in profile --- .../mypage/screen/main/MypageScreen.kt | 16 +++++++++++++++- .../mypage/viewmodel/MypageViewModel.kt | 5 +++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt index 1af3f45..4fa3729 100644 --- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier @@ -19,6 +20,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController +import androidx.navigation.compose.currentBackStackEntryAsState import com.hsLink.hslink.R import com.hsLink.hslink.core.designsystem.component.HsLinkTopBar import com.hsLink.hslink.core.designsystem.theme.HsLinkTheme @@ -49,7 +51,6 @@ private fun buildStatusText(jobSeeking: Boolean, academicStatus: String, employe @Preview(showBackground = true) @Composable private fun MypageScreenPreview() { - MypageScreen(paddingValues = PaddingValues()) } @Composable @@ -62,6 +63,19 @@ fun MypageRoute( val isLoading by viewModel.isLoading.collectAsState() val error by viewModel.error.collectAsState() + LaunchedEffect(Unit) { + viewModel.loadUserSummary() // 또는 loadMypage() + } + + // ← 프로필 수정 후 돌아왔을 때 새로고침 + val currentBackStackEntry by navController.currentBackStackEntryAsState() + LaunchedEffect(currentBackStackEntry) { + // 프로필 수정 화면에서 돌아왔을 때만 새로고침 + if (currentBackStackEntry?.destination?.route?.contains("mypage") == true) { + viewModel.loadUserSummary() + } + } + MypageScreen( paddingValues = paddingValues, userSummary = userSummary, // ← 변경 diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt index 17e9838..fbef525 100644 --- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt +++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt @@ -39,6 +39,10 @@ class MypageViewModel @Inject constructor( getUserProfile() } + // ← 새로 추가: public loadUserSummary 함수 + fun loadUserSummary() { + getUserSummary() + } private fun getUserProfile() { viewModelScope.launch { _isLoading.value = true @@ -72,6 +76,7 @@ class MypageViewModel @Inject constructor( Log.d("MypageViewModel", "프로필 수정 성공") // 수정 후 다시 조회 getUserProfile() // ← 이건 그대로 유지 (전체 정보 필요) + getUserSummary() } .onFailure { exception -> Log.e("MypageViewModel", "프로필 수정 실패: ${exception.message}")