diff --git a/composeApp/src/commonMain/kotlin/org/whosin/client/core/navigation/Route.kt b/composeApp/src/commonMain/kotlin/org/whosin/client/core/navigation/Route.kt index e704b89..904417f 100644 --- a/composeApp/src/commonMain/kotlin/org/whosin/client/core/navigation/Route.kt +++ b/composeApp/src/commonMain/kotlin/org/whosin/client/core/navigation/Route.kt @@ -29,7 +29,7 @@ sealed interface Route { data object NicknameInput: Route @Serializable - data object ClubCodeInput: Route + data class ClubCodeInput(val returnToMyPage: Boolean = false): Route /* 메인 화면 */ @Serializable @@ -38,8 +38,4 @@ sealed interface Route { /* 마이 페이지 */ @Serializable data object MyPage: Route - - /* 내 정보 수정하기 */ - @Serializable - data object UpdateMyInfo: Route } diff --git a/composeApp/src/commonMain/kotlin/org/whosin/client/core/navigation/WhosInNavGraph.kt b/composeApp/src/commonMain/kotlin/org/whosin/client/core/navigation/WhosInNavGraph.kt index 9c41efe..e7698a3 100644 --- a/composeApp/src/commonMain/kotlin/org/whosin/client/core/navigation/WhosInNavGraph.kt +++ b/composeApp/src/commonMain/kotlin/org/whosin/client/core/navigation/WhosInNavGraph.kt @@ -6,6 +6,7 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.navigation +import androidx.navigation.toRoute import org.whosin.client.presentation.auth.clubcode.ClubCodeInputScreen import org.whosin.client.presentation.auth.login.EmailVerificationScreen import org.whosin.client.presentation.auth.login.LoginScreen @@ -15,7 +16,6 @@ import org.whosin.client.presentation.auth.login.PasswordInputScreen import org.whosin.client.presentation.auth.login.SignupScreen import org.whosin.client.presentation.auth.login.SplashScreen import org.whosin.client.presentation.home.HomeScreen -import org.whosin.client.presentation.mypage.EditMyInfoScreen import org.whosin.client.presentation.mypage.MyPageScreen @Composable @@ -105,18 +105,25 @@ fun WhosInNavGraph( modifier = modifier, onNavigateBack = { navController.navigateUp() }, onNavigateToClubCode = { - navController.navigate(Route.ClubCodeInput) + navController.navigate(Route.ClubCodeInput(returnToMyPage = false)) } ) } - composable { + composable { backStackEntry -> + val route = backStackEntry.toRoute() ClubCodeInputScreen( modifier = modifier, onNavigateBack = { navController.navigateUp() }, onNavigateToHome = { - navController.navigate(Route.Home) { - popUpTo(Route.AuthGraph) { inclusive = true } + if (route.returnToMyPage) { + // MyPage에서 온 경우 단순히 뒤로가기 + navController.navigateUp() + } else { + // 회원가입 플로우에서 온 경우 Home으로 이동 + navController.navigate(Route.Home) { + popUpTo(Route.AuthGraph) { inclusive = true } + } } } ) @@ -139,23 +146,7 @@ fun WhosInNavGraph( modifier = modifier, onNavigateBack = { navController.navigateUp() }, onNavigateToAddClub = { - - }, - onNavigateToEdit = { - navController.navigate(Route.UpdateMyInfo) - }, - ) - } - - composable { - EditMyInfoScreen( - modifier = modifier, - onNavigateBack = { navController.navigateUp() }, - onNavigateToMyPage = { - navController.navigate(Route.MyPage) { - popUpTo(Route.UpdateMyInfo) { inclusive = true } - launchSingleTop = true - } + navController.navigate(Route.ClubCodeInput(returnToMyPage = true)) } ) } diff --git a/composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/request/UpdateMyInfoRequestDto.kt b/composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/request/UpdateMyInfoRequestDto.kt new file mode 100644 index 0000000..c6d4f8e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/request/UpdateMyInfoRequestDto.kt @@ -0,0 +1,12 @@ +package org.whosin.client.data.dto.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class UpdateMyInfoRequestDto( + @SerialName("nickName") + val nickName: String, + @SerialName("clubList") + val clubList: List? +) diff --git a/composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/MyInfoResponseDto.kt b/composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/MyInfoResponseDto.kt new file mode 100644 index 0000000..6baa257 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/MyInfoResponseDto.kt @@ -0,0 +1,32 @@ +package org.whosin.client.data.dto.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class MyInfoResponseDto( + @SerialName("success") + val success: Boolean, + @SerialName("status") + val status: Int, + @SerialName("message") + val message: String, + @SerialName("data") + val data: MyInfoData +) + +@Serializable +data class MyInfoData( + @SerialName("nickName") + val nickName: String, + @SerialName("clubList") + val clubList: List +) + +@Serializable +data class MyClubData( + @SerialName("id") + val id: Int, + @SerialName("name") + val name: String +) diff --git a/composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/UpdateMyInfoResponseDto.kt b/composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/UpdateMyInfoResponseDto.kt new file mode 100644 index 0000000..88f3d4a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/whosin/client/data/dto/response/UpdateMyInfoResponseDto.kt @@ -0,0 +1,16 @@ +package org.whosin.client.data.dto.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class UpdateMyInfoResponseDto( + @SerialName("success") + val success: Boolean, + @SerialName("status") + val status: Int, + @SerialName("message") + val message: String, + @SerialName("data") + val data: MyInfoData +) diff --git a/composeApp/src/commonMain/kotlin/org/whosin/client/data/remote/RemoteMemberDataSource.kt b/composeApp/src/commonMain/kotlin/org/whosin/client/data/remote/RemoteMemberDataSource.kt index 56f5c22..aa47518 100644 --- a/composeApp/src/commonMain/kotlin/org/whosin/client/data/remote/RemoteMemberDataSource.kt +++ b/composeApp/src/commonMain/kotlin/org/whosin/client/data/remote/RemoteMemberDataSource.kt @@ -3,13 +3,18 @@ package org.whosin.client.data.remote import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.request.get +import io.ktor.client.request.patch import io.ktor.client.request.post import io.ktor.client.request.setBody import io.ktor.client.statement.HttpResponse import io.ktor.http.isSuccess import org.whosin.client.core.network.ApiResult import org.whosin.client.data.dto.request.LoginRequestDto +import org.whosin.client.data.dto.request.UpdateMyInfoRequestDto +import org.whosin.client.data.dto.response.ErrorResponseDto import org.whosin.client.data.dto.response.LoginResponseDto +import org.whosin.client.data.dto.response.MyInfoResponseDto +import org.whosin.client.data.dto.response.UpdateMyInfoResponseDto class RemoteMemberDataSource( private val client: HttpClient @@ -38,4 +43,73 @@ class RemoteMemberDataSource( ApiResult.Error(message = t.message, cause = t) } } + + // 내 정보 조회 + suspend fun getMyInfo(): ApiResult { + return try { + val response: HttpResponse = client.get(urlString = "users/myPage") + + if (response.status.isSuccess()) { + ApiResult.Success( + data = response.body(), + statusCode = response.status.value + ) + } else { + // 에러 응답 파싱 시도 + try { + val errorResponse: ErrorResponseDto = response.body() + ApiResult.Error( + code = response.status.value, + message = errorResponse.message + ) + } catch (e: Exception) { + // 파싱 실패 시 기본 에러 메시지 + ApiResult.Error( + code = response.status.value, + message = "HTTP Error: ${response.status.value}" + ) + } + } + } catch (t: Throwable) { + ApiResult.Error(message = t.message, cause = t) + } + } + + // 내 정보 수정 + suspend fun updateMyInfo( + newNickName: String, + clubList: List? + ): ApiResult { + return try { + val response: HttpResponse = client.patch(urlString = "users/myPage/update") { + setBody( + UpdateMyInfoRequestDto(nickName = newNickName, clubList = clubList) + ) + } + + if (response.status.isSuccess()) { + ApiResult.Success( + data = response.body(), + statusCode = response.status.value + ) + } else { + // 에러 응답 파싱 시도 + try { + val errorResponse: ErrorResponseDto = response.body() + ApiResult.Error( + code = response.status.value, + message = errorResponse.message + ) + } catch (e: Exception) { + // 파싱 실패 시 기본 에러 메시지 + ApiResult.Error( + code = response.status.value, + message = "HTTP Error: ${response.status.value}" + ) + } + } + } catch (t: Throwable) { + ApiResult.Error(message = t.message, cause = t) + } + } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/whosin/client/data/repository/MemberRepository.kt b/composeApp/src/commonMain/kotlin/org/whosin/client/data/repository/MemberRepository.kt index 71adf94..6ccbffe 100644 --- a/composeApp/src/commonMain/kotlin/org/whosin/client/data/repository/MemberRepository.kt +++ b/composeApp/src/commonMain/kotlin/org/whosin/client/data/repository/MemberRepository.kt @@ -3,6 +3,8 @@ package org.whosin.client.data.repository import org.whosin.client.data.remote.RemoteMemberDataSource import org.whosin.client.core.network.ApiResult import org.whosin.client.data.dto.response.LoginResponseDto +import org.whosin.client.data.dto.response.MyInfoResponseDto +import org.whosin.client.data.dto.response.UpdateMyInfoResponseDto class MemberRepository( private val dataSource: RemoteMemberDataSource @@ -10,4 +12,10 @@ class MemberRepository( suspend fun login(email: String, password: String): ApiResult = dataSource.login(email, password) + suspend fun getMyInfo(): ApiResult = + dataSource.getMyInfo() + + suspend fun updateMyInfo(newNickName: String, clubList: List?): ApiResult = + dataSource.updateMyInfo(newNickName = newNickName, clubList = clubList) + } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/EditMyInfoScreen.kt b/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/EditMyInfoScreen.kt deleted file mode 100644 index 1458948..0000000 --- a/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/EditMyInfoScreen.kt +++ /dev/null @@ -1,99 +0,0 @@ -package org.whosin.client.presentation.mypage - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.OutlinedTextFieldDefaults -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import org.jetbrains.compose.resources.stringResource -import org.jetbrains.compose.ui.tooling.preview.Preview -import org.whosin.client.presentation.mypage.component.MyPageButton -import org.whosin.client.presentation.mypage.component.MyPageTopAppBar -import whosinclient.composeapp.generated.resources.Res -import whosinclient.composeapp.generated.resources.complete_edit -import whosinclient.composeapp.generated.resources.edit_my_information -import whosinclient.composeapp.generated.resources.nickname -@Composable -fun EditMyInfoScreen(modifier: Modifier = Modifier, onNavigateBack: () -> Unit, onNavigateToMyPage: () -> Unit) { - var nickName by remember { mutableStateOf("조익성") } - Box( - modifier = modifier.fillMaxSize().background(Color.White).padding(16.dp) - ) { - Column( - verticalArrangement = Arrangement.spacedBy(32.dp) - ) { - MyPageTopAppBar(onNavigateBack) - Text( - text = stringResource(Res.string.edit_my_information), - fontSize = 24.sp, - color = Color.Black, - lineHeight = 24.sp, - fontWeight = FontWeight.SemiBold, - modifier = Modifier.fillMaxWidth() - ) - Column( - modifier = Modifier.fillMaxWidth() - ) { - Text( - text = stringResource(Res.string.nickname), - fontSize = 16.sp, - color = Color.Black, - fontWeight = FontWeight.SemiBold, - modifier = Modifier.padding(bottom = 12.dp) - ) - OutlinedTextField( - value = nickName, - onValueChange = { nickName = it }, - modifier = Modifier.fillMaxWidth(), - textStyle = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Medium), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color(0xFFFBFBFB), - unfocusedContainerColor = Color(0xFFFBFBFB), - focusedBorderColor = Color(0xFFE5E5E5), - unfocusedBorderColor = Color(0xFFE5E5E5), - focusedTextColor = Color.Black, - unfocusedTextColor = Color.Black - ), - shape = RoundedCornerShape(10.dp) - ) - } - // 내 동아리 / 학과 목록 - Column( - modifier = Modifier.fillMaxWidth().background(Color.Gray).height(271.dp) - ) { - Text( - "내 동아리 / 학과 목록" - ) - } - Spacer(modifier = Modifier.weight(1f)) - // 내 정보 수정 버튼 - MyPageButton(onClick = onNavigateToMyPage, text = stringResource(Res.string.complete_edit), enabled = nickName.isNotEmpty()) - } - } - -} - -@Preview -@Composable -private fun EditMyInfoScreenPreview() { - EditMyInfoScreen(onNavigateBack = {}, onNavigateToMyPage = {}) -} diff --git a/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/MyPageScreen.kt b/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/MyPageScreen.kt index a5f9ac5..ae8be84 100644 --- a/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/MyPageScreen.kt +++ b/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/MyPageScreen.kt @@ -1,7 +1,6 @@ package org.whosin.client.presentation.mypage import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -14,10 +13,7 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -25,14 +21,16 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.ui.tooling.preview.Preview import org.koin.compose.viewmodel.koinViewModel -import org.whosin.client.data.dto.response.ClubData +import org.whosin.client.presentation.component.CommonBackHandler import org.whosin.client.presentation.mypage.component.MyClubComponent import org.whosin.client.presentation.mypage.component.MyPageButton import org.whosin.client.presentation.mypage.component.MyPageTopAppBar import whosinclient.composeapp.generated.resources.Res +import whosinclient.composeapp.generated.resources.complete_edit import whosinclient.composeapp.generated.resources.edit_my_information import whosinclient.composeapp.generated.resources.my_information import whosinclient.composeapp.generated.resources.nickname @@ -41,30 +39,33 @@ import whosinclient.composeapp.generated.resources.nickname fun MyPageScreen( modifier: Modifier = Modifier, onNavigateToAddClub: () -> Unit, - onNavigateBack: () -> Unit, - onNavigateToEdit: () -> Unit + onNavigateBack: () -> Unit ) { val viewModel: MyPageViewModel = koinViewModel() - var nickName by remember { mutableStateOf("조익성") } - val myClubs = listOf( - ClubData(clubId = 1, clubName = "메이커스팜"), - ClubData(clubId = 2, clubName = "KUIT"), - ClubData(clubId = 1, clubName = "메이커스팜"), - ClubData(clubId = 2, clubName = "KUIT"), - ClubData(clubId = 1, clubName = "메이커스팜"), - ClubData(clubId = 2, clubName = "KUIT"), - ClubData(clubId = 1, clubName = "메이커스팜"), - ClubData(clubId = 2, clubName = "KUIT"), - ClubData(clubId = 1, clubName = "메이커스팜"), - ClubData(clubId = 2, clubName = "KUIT"), - ClubData(clubId = 1, clubName = "메이커스팜"), - ClubData(clubId = 2, clubName = "KUIT"), - ) + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + + // MyPage로 돌아올 때마다 수정 모드 해제 및 데이터 새로고침 + LaunchedEffect(Unit) { + if (uiState.isEditable) { + viewModel.toggleEditMode() + } + viewModel.getMyInfo() + } + + CommonBackHandler { + if (uiState.isEditable) { + viewModel.toggleEditMode() + viewModel.getMyInfo() + } else { + onNavigateBack() + } + } + Box( modifier = modifier .fillMaxSize() .background(Color.White) - .padding(16.dp) + .padding(top = 16.dp, start = 16.dp, end = 16.dp) ) { Column( modifier = Modifier.fillMaxWidth() @@ -72,7 +73,7 @@ fun MyPageScreen( MyPageTopAppBar(onNavigateBack) Spacer(modifier = Modifier.size(16.dp)) Text( - text = stringResource(Res.string.my_information), + text = stringResource(if (uiState.isEditable) Res.string.edit_my_information else Res.string.my_information), fontSize = 24.sp, color = Color.Black, lineHeight = 24.sp, @@ -91,9 +92,10 @@ fun MyPageScreen( modifier = Modifier.padding(bottom = 12.dp) ) OutlinedTextField( - readOnly = true, - value = nickName, - onValueChange = { nickName = it }, + readOnly = !uiState.isEditable, + value = uiState.nickname, + onValueChange = { viewModel.updateNickName(it) }, + singleLine = true, modifier = Modifier.fillMaxWidth(), textStyle = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Medium), colors = OutlinedTextFieldDefaults.colors( @@ -114,21 +116,31 @@ fun MyPageScreen( .fillMaxWidth() .weight(1f) .padding(bottom = 72.dp), - myClubs = myClubs, - onDeleteClub = { - // TODO: 동아리 삭제 -// viewModel.deleteClub(it) + isEditable = uiState.isEditable, + myClubs = uiState.clubs, + onDeleteClub = { clubId -> + viewModel.deleteClub(clubId) }, - onNavigateToAddClub = onNavigateToAddClub + onNavigateToAddClub = onNavigateToAddClub ) } - + // 내 정보 수정 버튼 - 하단에 고정 MyPageButton( - onClick = onNavigateToEdit, - text = stringResource(Res.string.edit_my_information), - enabled = nickName.isNotEmpty(), - modifier = Modifier.align(Alignment.BottomCenter) + onClick = { + if (uiState.isEditable) { + viewModel.updateMyInfo(uiState.nickname.trim(), uiState.clubs) + } + viewModel.toggleEditMode() + }, + text = stringResource( + if (uiState.isEditable) Res.string.complete_edit + else Res.string.edit_my_information + ), + enabled = uiState.nickname.isNotEmpty(), + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 52.dp) ) } @@ -139,7 +151,6 @@ fun MyPageScreen( private fun MyPageScreenPreview() { MyPageScreen( onNavigateBack = {}, - onNavigateToEdit = {}, onNavigateToAddClub = {} ) } diff --git a/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/MyPageViewModel.kt b/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/MyPageViewModel.kt index 91efcbd..fe98639 100644 --- a/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/MyPageViewModel.kt +++ b/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/MyPageViewModel.kt @@ -1,10 +1,111 @@ package org.whosin.client.presentation.mypage import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import org.whosin.client.core.network.ApiResult +import org.whosin.client.data.dto.response.ClubData import org.whosin.client.data.repository.MemberRepository +data class MyPageUiState( + val isLoading: Boolean = false, + val isEditable: Boolean = false, + val nickname: String = "", + val clubs: List = emptyList(), + val errorMessage: String? = null, +) + class MyPageViewModel( private val repository: MemberRepository ): ViewModel() { + private val _uiState = MutableStateFlow(MyPageUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + init { + getMyInfo() + } + + // 수정 모드 변경 + fun toggleEditMode() { + _uiState.update { it.copy(isEditable = !it.isEditable) } + } + + // 닉네임 변경 + fun updateNickName(newNickname: String) { + _uiState.update { it.copy(nickname = newNickname) } + } + + // 내 정보 조회 + fun getMyInfo() { + viewModelScope.launch { + _uiState.update{ it.copy(isLoading = true) } + when (val result = repository.getMyInfo()) { + is ApiResult.Success -> { + val response = result.data.data + _uiState.update { it -> + it.copy( + isLoading = false, + nickname = response.nickName, + clubs = response.clubList.map { clubData -> + ClubData( + clubId = clubData.id, + clubName = clubData.name + ) + }, + errorMessage = null + ) + } + println("MyPageViewModel: 내 정보 조회 성공") + } + is ApiResult.Error -> { + _uiState.value = _uiState.value.copy( + isLoading = false, + errorMessage = result.message ?: "내 정보 조회에 실패했습니다." + ) + println("MyPageViewModel: 내 정보 조회 실패 - ${result.message}") + } + } + } + } + + // 내 정보 수정 + fun updateMyInfo(newNickName: String, clubList: List?) { + viewModelScope.launch { + _uiState.update{ it.copy(isLoading = true) } + val newClubs = clubList?.map { + it.clubId + } + when (val result = repository.updateMyInfo(newNickName = newNickName, clubList = newClubs)) { + is ApiResult.Success -> { + _uiState.update { + it.copy(isEditable = false) + } + getMyInfo() + println("MyPageViewModel: 내 정보 수정 성공") + } + is ApiResult.Error -> { + _uiState.value = _uiState.value.copy( + isLoading = false, + errorMessage = result.message ?: "내 정보 수정에 실패했습니다." + ) + println("MyPageViewModel: 내 정보 수정 실패 - ${result.message}") + } + } + } + } + + // 클럽 삭제 (UI 상태에서만 제거) + fun deleteClub(clubId: Int) { + _uiState.update { currentState -> + currentState.copy( + clubs = currentState.clubs.filter { it.clubId != clubId } + ) + } + println("MyPageViewModel: 클럽 삭제 - clubId: $clubId") + } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/component/MyClubComponent.kt b/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/component/MyClubComponent.kt index 775e190..3d9577a 100644 --- a/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/component/MyClubComponent.kt +++ b/composeApp/src/commonMain/kotlin/org/whosin/client/presentation/mypage/component/MyClubComponent.kt @@ -33,13 +33,14 @@ import whosinclient.composeapp.generated.resources.Res @Composable fun MyClubComponent( modifier: Modifier = Modifier, + isEditable: Boolean = false, myClubs: List, onDeleteClub: (Int) -> Unit, onNavigateToAddClub: () -> Unit ) { Column( modifier = modifier - ){ + ) { Row( modifier = Modifier .fillMaxWidth(), @@ -53,15 +54,17 @@ fun MyClubComponent( fontWeight = FontWeight.SemiBold, fontSize = 16.sp ) - Text( - modifier = Modifier - .clickable(onClick = onNavigateToAddClub), - text = "추가하기", - color = Color(0xFFF89531), - fontFamily = pretendardFontFamily(), - fontSize = 16.sp, - textDecoration = TextDecoration.Underline - ) + if (isEditable) { + Text( + modifier = Modifier + .clickable(onClick = onNavigateToAddClub), + text = "추가하기", + color = Color(0xFFF89531), + fontFamily = pretendardFontFamily(), + fontSize = 16.sp, + textDecoration = TextDecoration.Underline + ) + } } Spacer(modifier = Modifier.size(20.dp)) LazyColumn( @@ -78,7 +81,8 @@ fun MyClubComponent( items(myClubs) { clubData -> MyClubItem( modifier = Modifier, - clubName = clubData.clubName + clubName = clubData.clubName, + isEditable = isEditable, ) { println("clicked clubId : ${clubData.clubId}") onDeleteClub(clubData.clubId) @@ -91,6 +95,7 @@ fun MyClubComponent( @Composable fun MyClubItem( modifier: Modifier = Modifier, + isEditable: Boolean = false, clubName: String, onDeleteClub: () -> Unit ) { @@ -106,13 +111,15 @@ fun MyClubItem( fontFamily = pretendardFontFamily(), fontSize = 16.sp ) - AsyncImage( - model = Res.getUri("files/ic_x.svg"), - contentDescription = "Delete Club", - modifier = Modifier - .size(24.dp) - .clickable(onClick = onDeleteClub) - ) + if (isEditable) { + AsyncImage( + model = Res.getUri("files/ic_x.svg"), + contentDescription = "Delete Club", + modifier = Modifier + .size(24.dp) + .clickable(onClick = onDeleteClub) + ) + } } }