diff --git a/core/design-system/src/main/res/drawable/ic_agency_delete.png b/core/design-system/src/main/res/drawable/ic_agency_delete.png new file mode 100644 index 00000000..7a3b6b7f Binary files /dev/null and b/core/design-system/src/main/res/drawable/ic_agency_delete.png differ diff --git a/core/network/src/main/java/com/moneymong/moneymong/network/api/AgencyApi.kt b/core/network/src/main/java/com/moneymong/moneymong/network/api/AgencyApi.kt index 7407a87e..c03d0c1f 100644 --- a/core/network/src/main/java/com/moneymong/moneymong/network/api/AgencyApi.kt +++ b/core/network/src/main/java/com/moneymong/moneymong/network/api/AgencyApi.kt @@ -8,6 +8,7 @@ import com.moneymong.moneymong.model.agency.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse import com.moneymong.moneymong.model.member.InvitationCodeResponse import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.Header import retrofit2.http.PATCH @@ -49,4 +50,10 @@ interface AgencyApi { suspend fun reInvitationCode( @Path("agencyId") agencyId: Long ): Result + + //DELETE + @DELETE("api/v1/agencies/{agencyId}") + suspend fun deleteAgency( + @Path("agencyId") agencyId: Int + ) : Result } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSource.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSource.kt index 8b5713b9..da900da5 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSource.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSource.kt @@ -11,4 +11,6 @@ interface MemberRemoteDataSource { suspend fun getMemberLists(agencyId: Long): Result suspend fun updateMemberAuthor(agencyId : Long, data : UpdateAuthorRequest) : Result suspend fun blockMemberAuthor(agencyId: Long, data : MemberBlockRequest) : Result + suspend fun deleteAgency(agencyId: Int) : Result + } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSourceImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSourceImpl.kt index 222d5038..0cdcc50c 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSourceImpl.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSourceImpl.kt @@ -31,4 +31,8 @@ class MemberRemoteDataSourceImpl @Inject constructor( override suspend fun blockMemberAuthor(agencyId: Long, data: MemberBlockRequest): Result { return memberApi.blockMemberAuthor(agencyId, data) } + + override suspend fun deleteAgency(agencyId: Int): Result { + return agencyApi.deleteAgency(agencyId) + } } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/repository/member/MemberRepositoryImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/repository/member/MemberRepositoryImpl.kt index 50ca2298..8bfc4841 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/repository/member/MemberRepositoryImpl.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/repository/member/MemberRepositoryImpl.kt @@ -31,4 +31,8 @@ class MemberRepositoryImpl @Inject constructor( return memberRemoteDataSource.blockMemberAuthor(agencyId, data) } + + override suspend fun deleteAgency(agencyId: Int): Result { + return memberRemoteDataSource.deleteAgency(agencyId) + } } \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/repository/member/MemberRepository.kt b/domain/src/main/java/com/moneymong/moneymong/domain/repository/member/MemberRepository.kt index c789f21c..4ff59b80 100644 --- a/domain/src/main/java/com/moneymong/moneymong/domain/repository/member/MemberRepository.kt +++ b/domain/src/main/java/com/moneymong/moneymong/domain/repository/member/MemberRepository.kt @@ -11,4 +11,6 @@ interface MemberRepository { suspend fun getMemberLists(agencyId: Long) : Result suspend fun updateMemberAuthor(agencyId : Long, data : UpdateAuthorRequest) : Result suspend fun blockMemberAuthor(agencyId: Long, data: MemberBlockRequest) : Result + suspend fun deleteAgency(agencyId: Int) :Result + } \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/member/AgencyDeleteUseCase.kt b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/member/AgencyDeleteUseCase.kt new file mode 100644 index 00000000..a6b57b25 --- /dev/null +++ b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/member/AgencyDeleteUseCase.kt @@ -0,0 +1,12 @@ +package com.moneymong.moneymong.domain.usecase.member + +import com.moneymong.moneymong.domain.repository.member.MemberRepository +import javax.inject.Inject + +class AgencyDeleteUseCase @Inject constructor( + private val memberRepository: MemberRepository +) { + suspend operator fun invoke(agencyId: Int) : Result { + return memberRepository.deleteAgency(agencyId) + } +} \ No newline at end of file diff --git a/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerScreen.kt b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerScreen.kt index 36d6afaa..d8c5685b 100644 --- a/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerScreen.kt +++ b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerScreen.kt @@ -333,7 +333,17 @@ fun LedgerScreen( } } else { Box(modifier = modifier.fillMaxSize()) { - MemberScreen(agencyId = state.agencyId) + MemberScreen( + agencyId = state.agencyId, + agencyList = state.agencyList, + onClickItem = { + viewModel.eventEmit( + LedgerSideEffect.LedgerSelectedAgencyChange( + it + ) + ) + }, + changeAgencyList = { viewModel.changeAgencyList(it) }) } } } diff --git a/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerViewModel.kt b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerViewModel.kt index e3aa1497..1f3116ad 100644 --- a/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerViewModel.kt +++ b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerViewModel.kt @@ -1,5 +1,6 @@ package com.moneymong.moneymong.ledger +import android.util.Log import androidx.lifecycle.SavedStateHandle import com.moneymong.moneymong.common.base.BaseViewModel import com.moneymong.moneymong.common.error.MoneyMongError @@ -14,6 +15,7 @@ import com.moneymong.moneymong.domain.usecase.member.MemberListUseCase import com.moneymong.moneymong.domain.usecase.user.FetchUserIdUseCase import com.moneymong.moneymong.ledger.navigation.LedgerArgs import com.moneymong.moneymong.ledger.view.LedgerTransactionType +import com.moneymong.moneymong.model.agency.MyAgencyResponse import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.collectLatest import org.orbitmvi.orbit.annotation.OrbitExperimental @@ -69,6 +71,7 @@ class LedgerViewModel @Inject constructor( reduce { state.copy(isAgencyExistLoading = true) } fetchAgencyExistLedgerUseCase(state.agencyId) .onSuccess { + Log.d("fetchAgencyExistLedger${state.agencyId}",it.toString() ) reduce { state.copy( isExistLedger = it, @@ -90,6 +93,8 @@ class LedgerViewModel @Inject constructor( page = 0, limit = 100 ).onSuccess { + Log.d("fetchLedgerTransactionList${state.existAgency}",it.toString() ) + reduce { state.copy( ledgerTransaction = it, @@ -97,11 +102,17 @@ class LedgerViewModel @Inject constructor( ) } }.onFailure { - reduce { + if (it.message.equals("장부가 존재하지 않습니다.")) { //TODO - 서버 의논 후 변경 예정 state.copy( - visibleError = true, - errorMessage = it.message ?: MoneyMongError.UnExpectedError.message + visibleError = false, ) + } else { + reduce { + state.copy( + visibleError = true, + errorMessage = it.message ?: MoneyMongError.UnExpectedError.message + ) + } } }.also { reduce { state.copy(isLedgerTransactionLoading = false) } } } @@ -111,6 +122,8 @@ class LedgerViewModel @Inject constructor( reduce { state.copy(isMyAgencyLoading = true) } fetchMyAgencyListUseCase() .onSuccess { + Log.d("fetchMyAgencyList${state.existAgency}",it.toString() ) + reduce { state.copy( agencyList = it, @@ -142,11 +155,20 @@ class LedgerViewModel @Inject constructor( ) } }.onFailure { - reduce { - state.copy( - visibleError = true, - errorMessage = it.message ?: MoneyMongError.UnExpectedError.message - ) + if (it.message.equals("소속에 가입 후 장부를 사용할 수 있습니다.")) { //TODO - 서버 의논 후 변경 예정 + reduce { + state.copy( + visibleError = false + ) + } + } else { + reduce { + state.copy( + visibleError = true, + errorMessage = it.message + ?: MoneyMongError.UnExpectedError.message + ) + } } }.also { reduce { state.copy(isAgencyMemberLoading = false) } } } @@ -216,4 +238,13 @@ class LedgerViewModel @Inject constructor( postDisplayedLedgerOnboardingUseCase(onboardingType = state.onboardingType) reduce { state.copy(visibleOnboarding = false) } } -} \ No newline at end of file + + + fun changeAgencyList(filteredAgencyList: List) = intent { + reduce { + state.copy( + agencyList = filteredAgencyList + ) + } + } +} diff --git a/feature/member/src/main/java/com/example/member/MemberScreen.kt b/feature/member/src/main/java/com/example/member/MemberScreen.kt index e7a4019c..75ee832e 100644 --- a/feature/member/src/main/java/com/example/member/MemberScreen.kt +++ b/feature/member/src/main/java/com/example/member/MemberScreen.kt @@ -37,6 +37,7 @@ import com.moneymong.moneymong.design_system.component.bottomSheet.MDSBottomShee import com.moneymong.moneymong.design_system.component.button.MDSButton import com.moneymong.moneymong.design_system.component.button.MDSButtonSize import com.moneymong.moneymong.design_system.component.button.MDSButtonType +import com.moneymong.moneymong.design_system.component.modal.MDSModal import com.moneymong.moneymong.design_system.component.snackbar.MDSSnackbarHost import com.moneymong.moneymong.design_system.error.ErrorDialog import com.moneymong.moneymong.design_system.error.ErrorScreen @@ -50,6 +51,7 @@ import com.moneymong.moneymong.design_system.theme.Gray08 import com.moneymong.moneymong.design_system.theme.MMHorizontalSpacing import com.moneymong.moneymong.design_system.theme.Red03 import com.moneymong.moneymong.design_system.theme.White +import com.moneymong.moneymong.model.agency.MyAgencyResponse import kotlinx.coroutines.launch import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect @@ -59,7 +61,10 @@ import org.orbitmvi.orbit.compose.collectSideEffect @Composable fun MemberScreen( viewModel: MemberViewModel = hiltViewModel(), - agencyId: Int + agencyId: Int, + agencyList: List, + onClickItem: (agencyId: Int) -> Unit, + changeAgencyList: (changeAgencyList: List) -> Unit ) { val sheetState = rememberModalBottomSheetState( skipPartiallyExpanded = true, @@ -196,6 +201,44 @@ fun MemberScreen( ) } + if (state.deleteAgency) { + MDSModal( + icon = R.drawable.ic_warning_filled, + title = "소속을 정말 삭제하시겠어요?", + description = "등록된 회비 내역이 모두 사라져요.", + negativeBtnText = "취소", + positiveBtnText = "확인", + onClickNegative = { viewModel.deleteAgencyBtnClicked(false) }, + onClickPositive = { + viewModel.deleteAgency( + agencyId, + agencyList, + onClickItem, + changeAgencyList + ) + } + ) + } + + if (state.deleteAgency) { + MDSModal( + icon = R.drawable.ic_warning_filled, + title = "소속을 정말 삭제하시겠어요?", + description = "등록된 회비 내역이 모두 사라져요.", + negativeBtnText = "취소", + positiveBtnText = "확인", + onClickNegative = { viewModel.deleteAgencyBtnClicked(false) }, + onClickPositive = { + viewModel.deleteAgency( + agencyId, + agencyList, + onClickItem, + changeAgencyList + ) + } + ) + } + if (state.visibleBottomSheet) { viewModel.isRoleChanged(false) MDSBottomSheet( @@ -383,6 +426,8 @@ fun MemberScreen( invitationCode = state.invitationCode, isReInvitationCode = { viewModel.eventEmit(MemberSideEffect.GetReInvitationCode(it)) }, //TODO onCopyChange = { onCopyClick -> viewModel.onCopyClickChanged(onCopyClick) }, + deleteAgencyBtnClicked = { onClick -> viewModel.deleteAgencyBtnClicked(onClick)} + ) MemberListView( @@ -428,6 +473,8 @@ fun MemberScreen( invitationCode = state.invitationCode, isReInvitationCode = { viewModel.eventEmit(MemberSideEffect.GetReInvitationCode(it)) }, onCopyChange = { onCopyClick -> viewModel.onCopyClickChanged(onCopyClick) }, + deleteAgencyBtnClicked = { onClick -> viewModel.deleteAgencyBtnClicked(onClick)} + ) MemberListView( diff --git a/feature/member/src/main/java/com/example/member/MemberState.kt b/feature/member/src/main/java/com/example/member/MemberState.kt index cc11fb63..3a4ae76a 100644 --- a/feature/member/src/main/java/com/example/member/MemberState.kt +++ b/feature/member/src/main/java/com/example/member/MemberState.kt @@ -31,4 +31,6 @@ data class MemberState( val isUserAuthor: String = "", val agencyId: Int = 0, val isBlockedUser : Boolean = false, + val deleteAgency : Boolean = false, + ) : State \ No newline at end of file diff --git a/feature/member/src/main/java/com/example/member/MemberViewModel.kt b/feature/member/src/main/java/com/example/member/MemberViewModel.kt index e0283de1..fe15ac2b 100644 --- a/feature/member/src/main/java/com/example/member/MemberViewModel.kt +++ b/feature/member/src/main/java/com/example/member/MemberViewModel.kt @@ -3,12 +3,14 @@ package com.example.member import android.util.Log import com.moneymong.moneymong.common.base.BaseViewModel import com.moneymong.moneymong.domain.usecase.agency.FetchAgencyIdUseCase +import com.moneymong.moneymong.domain.usecase.member.AgencyDeleteUseCase import com.moneymong.moneymong.domain.usecase.member.MemberBlockUseCase import com.moneymong.moneymong.domain.usecase.member.MemberInvitationCodeUseCase import com.moneymong.moneymong.domain.usecase.member.MemberListUseCase import com.moneymong.moneymong.domain.usecase.member.MemberReInvitationCodeUseCase import com.moneymong.moneymong.domain.usecase.member.UpdateMemberAuthorUseCase import com.moneymong.moneymong.domain.usecase.user.GetMyInfoUseCase +import com.moneymong.moneymong.model.agency.MyAgencyResponse import com.moneymong.moneymong.model.member.AgencyUser import com.moneymong.moneymong.model.member.MemberBlockRequest import com.moneymong.moneymong.model.member.UpdateAuthorRequest @@ -27,7 +29,8 @@ class MemberViewModel @Inject constructor( private val getMyInfoUseCase: GetMyInfoUseCase, private val updateMemberAuthorUseCase: UpdateMemberAuthorUseCase, private val memberBlockUseCase: MemberBlockUseCase, - private val fetchAgencyIdUseCase: FetchAgencyIdUseCase + private val fetchAgencyIdUseCase: FetchAgencyIdUseCase, + private val delecteAgencyUseCase: AgencyDeleteUseCase ) : BaseViewModel(MemberState()) { init { @@ -256,6 +259,7 @@ class MemberViewModel @Inject constructor( } } + fun blockMemberAuthor(agencyId: Long, userId: Long) = intent { memberBlockUseCase(agencyId, MemberBlockRequest(userId)) .onSuccess { @@ -263,7 +267,7 @@ class MemberViewModel @Inject constructor( updateMemberListByBlock(userId) } .onFailure { - reduce{ + reduce { state.copy( visiblePopUpError = true, errorPopUpMessage = it.message.toString() @@ -272,6 +276,48 @@ class MemberViewModel @Inject constructor( } } + + fun deleteAgency( + agencyId: Int, + agencyList: List, + onClickItem: (agencyId: Int) -> Unit, + changeAgencyList: (agencyList: List) -> Unit + ) = intent { + delecteAgencyUseCase.invoke(agencyId) + .onSuccess { + Log.d("deleteAgency${agencyId}", it.toString()) + val filteredList = agencyList.filter { it.id != agencyId } + if (filteredList.isNotEmpty()) { + val randomAgency = filteredList.random() + onClickItem(randomAgency.id) + changeAgencyList(filteredList) + } else { + changeAgencyList(emptyList()) + } + reduce { + state.copy( + deleteAgency = false + ) + } + }.onFailure { + reduce { + state.copy( + visiblePopUpError = true, + errorPopUpMessage = it.message.toString() + ) + } + } + } + + + fun deleteAgencyBtnClicked(deleteAgencyBtnClicked : Boolean) = intent { + reduce { + state.copy( + deleteAgency = deleteAgencyBtnClicked + ) + } + } + private fun updateFilteredMemberListByBlock(userId: Long) = intent { val currentMemberList = state.filteredMemberList val updateBlockedMemberList = currentMemberList.filterNot { member -> diff --git a/feature/member/src/main/java/com/example/member/component/MemberCardView.kt b/feature/member/src/main/java/com/example/member/component/MemberCardView.kt index 42b2336c..b0ca5ba8 100644 --- a/feature/member/src/main/java/com/example/member/component/MemberCardView.kt +++ b/feature/member/src/main/java/com/example/member/component/MemberCardView.kt @@ -5,6 +5,7 @@ import android.content.ClipboardManager import android.content.Context import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -31,6 +32,7 @@ import com.moneymong.moneymong.design_system.theme.Body4 import com.moneymong.moneymong.design_system.theme.Gray02 import com.moneymong.moneymong.design_system.theme.Gray10 import com.moneymong.moneymong.design_system.theme.Mint03 +import com.moneymong.moneymong.design_system.theme.Red03 import com.moneymong.moneymong.design_system.theme.White import com.moneymong.moneymong.model.member.AgencyUser @@ -45,6 +47,8 @@ fun MemberCardView( invitationCode: String, isReInvitationCode: (Long) -> Unit, onCopyChange: (Boolean) -> Unit, + deleteAgencyBtnClicked : (Boolean) -> Unit + ) { val context = LocalContext.current @@ -92,6 +96,27 @@ fun MemberCardView( contentColor = White, ) + if(memberMyInfo.agencyUserRole != "MEMBER"){ + Row ( + modifier = Modifier + .fillMaxWidth() + .noRippleClickable { deleteAgencyBtnClicked(true) }, + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically + ){ + Text( + text = "소속삭제", + color = Red03, + style = Body3 + ) + Image( + modifier= Modifier.size(18.dp), + painter =painterResource(id = R.drawable.ic_agency_delete), + contentDescription ="" + ) + + } + } } if (memberMyInfo.agencyUserRole == "STAFF") {