From 7e25fbd5314a9cd03b54ec5b66e36cb12cfb1223 Mon Sep 17 00:00:00 2001 From: Heon Date: Wed, 22 Oct 2025 22:19:15 +0900 Subject: [PATCH 01/10] feat: fetch categories api interface from network layer --- .../moneymong/model/agency/CategoryReadResponse.kt | 1 + .../java/com/moneymong/moneymong/network/api/AgencyApi.kt | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/core/model/src/main/java/com/moneymong/moneymong/model/agency/CategoryReadResponse.kt b/core/model/src/main/java/com/moneymong/moneymong/model/agency/CategoryReadResponse.kt index a54fa491..1625d09b 100644 --- a/core/model/src/main/java/com/moneymong/moneymong/model/agency/CategoryReadResponse.kt +++ b/core/model/src/main/java/com/moneymong/moneymong/model/agency/CategoryReadResponse.kt @@ -1,6 +1,7 @@ package com.moneymong.moneymong.model.agency data class CategoryReadResponse( + val agencyId: Long, val categories: List, ) 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 7c666c20..5f2c4b5f 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 @@ -7,6 +7,7 @@ import com.moneymong.moneymong.model.agency.AgencyJoinResponse import com.moneymong.moneymong.model.agency.AgencyRegisterRequest import com.moneymong.moneymong.model.agency.CategoryCreateRequest import com.moneymong.moneymong.model.agency.CategoryCreateResponse +import com.moneymong.moneymong.model.agency.CategoryReadResponse import com.moneymong.moneymong.model.agency.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse import com.moneymong.moneymong.model.member.InvitationCodeResponse @@ -40,6 +41,11 @@ interface AgencyApi { @Query("keyword") name: String ): Result> + @GET("api/v1/agencies/categories/{agencyId}") + suspend fun fetchCategories( + @Path("agencyId") agencyId: Long + ): Result + // POST @POST("/api/v2/agencies/invitation-code") suspend fun agencyCodeNumbers( From aabdb0be43a0fae857c172799ceba560862b4da6 Mon Sep 17 00:00:00 2001 From: Heon Date: Wed, 22 Oct 2025 22:19:47 +0900 Subject: [PATCH 02/10] feat: fetch categories api datasource --- .../data/datasource/agency/AgencyRemoteDataSource.kt | 2 ++ .../data/datasource/agency/AgencyRemoteDataSourceImpl.kt | 5 +++++ .../data/datasource/agency/AgencyRemoteDataSourceMock.kt | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/agency/AgencyRemoteDataSource.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/agency/AgencyRemoteDataSource.kt index 23637a70..b437c7d6 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/agency/AgencyRemoteDataSource.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/agency/AgencyRemoteDataSource.kt @@ -7,6 +7,7 @@ import com.moneymong.moneymong.model.agency.AgencyJoinResponse import com.moneymong.moneymong.model.agency.AgencyRegisterRequest import com.moneymong.moneymong.model.agency.CategoryCreateRequest import com.moneymong.moneymong.model.agency.CategoryCreateResponse +import com.moneymong.moneymong.model.agency.CategoryReadResponse import com.moneymong.moneymong.model.agency.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse @@ -17,4 +18,5 @@ interface AgencyRemoteDataSource { suspend fun fetchAgencyByName(agencyName: String): Result> suspend fun agencyCodeNumbers(data: AgencyJoinRequest): Result suspend fun createCategory(request: CategoryCreateRequest): Result + suspend fun fetchCategories(agencyId: Long): Result } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/agency/AgencyRemoteDataSourceImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/agency/AgencyRemoteDataSourceImpl.kt index 8af2e0e7..fc2e9e8c 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/agency/AgencyRemoteDataSourceImpl.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/agency/AgencyRemoteDataSourceImpl.kt @@ -7,6 +7,7 @@ import com.moneymong.moneymong.model.agency.AgencyJoinResponse import com.moneymong.moneymong.model.agency.AgencyRegisterRequest import com.moneymong.moneymong.model.agency.CategoryCreateRequest import com.moneymong.moneymong.model.agency.CategoryCreateResponse +import com.moneymong.moneymong.model.agency.CategoryReadResponse import com.moneymong.moneymong.model.agency.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse import com.moneymong.moneymong.network.api.AgencyApi @@ -41,4 +42,8 @@ class AgencyRemoteDataSourceImpl @Inject constructor( override suspend fun createCategory(request: CategoryCreateRequest): Result { return agencyApi.createCategory(request = request) } + + override suspend fun fetchCategories(agencyId: Long): Result { + return agencyApi.fetchCategories(agencyId = agencyId) + } } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/agency/AgencyRemoteDataSourceMock.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/agency/AgencyRemoteDataSourceMock.kt index 9ef13a1d..b1ebc95b 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/agency/AgencyRemoteDataSourceMock.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/agency/AgencyRemoteDataSourceMock.kt @@ -7,6 +7,7 @@ import com.moneymong.moneymong.model.agency.AgencyJoinResponse import com.moneymong.moneymong.model.agency.AgencyRegisterRequest import com.moneymong.moneymong.model.agency.CategoryCreateRequest import com.moneymong.moneymong.model.agency.CategoryCreateResponse +import com.moneymong.moneymong.model.agency.CategoryReadResponse import com.moneymong.moneymong.model.agency.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse import kotlinx.coroutines.delay @@ -42,6 +43,10 @@ class AgencyRemoteDataSourceMock : AgencyRemoteDataSource { return Result.success(CategoryCreateResponse(agencyId = 1L, name = "category")) } + override suspend fun fetchCategories(agencyId: Long): Result { + return Result.success(CategoryReadResponse(agencyId = agencyId, categories = emptyList())) + } + private companion object { val agenciesMockOfSuccess = listOf( Result.success( From 6bf43832e0cd542a1c599ae8fa6f6235e190c2f4 Mon Sep 17 00:00:00 2001 From: Heon Date: Wed, 22 Oct 2025 22:21:53 +0900 Subject: [PATCH 03/10] feat: fetch categories repository --- .../moneymong/data/repository/agency/AgencyRepositoryImpl.kt | 4 ++++ .../moneymong/domain/repository/agency/AgencyRepository.kt | 2 ++ 2 files changed, 6 insertions(+) diff --git a/data/src/main/java/com/moneymong/moneymong/data/repository/agency/AgencyRepositoryImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/repository/agency/AgencyRepositoryImpl.kt index 4c15c851..ffb7591d 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/repository/agency/AgencyRepositoryImpl.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/repository/agency/AgencyRepositoryImpl.kt @@ -13,6 +13,7 @@ import com.moneymong.moneymong.model.agency.AgencyJoinResponse import com.moneymong.moneymong.model.agency.AgencyRegisterRequest import com.moneymong.moneymong.model.agency.CategoryCreateRequest import com.moneymong.moneymong.model.agency.CategoryCreateResponse +import com.moneymong.moneymong.model.agency.CategoryReadResponse import com.moneymong.moneymong.model.agency.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse import kotlinx.coroutines.flow.Flow @@ -54,4 +55,7 @@ class AgencyRepositoryImpl @Inject constructor( override suspend fun createCategory(request: CategoryCreateRequest): Result = agencyRemoteDataSource.createCategory(request = request) + + override suspend fun fetchCategories(agencyId: Long): Result = + agencyRemoteDataSource.fetchCategories(agencyId = agencyId) } \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/repository/agency/AgencyRepository.kt b/domain/src/main/java/com/moneymong/moneymong/domain/repository/agency/AgencyRepository.kt index f7d686c5..79a75d63 100644 --- a/domain/src/main/java/com/moneymong/moneymong/domain/repository/agency/AgencyRepository.kt +++ b/domain/src/main/java/com/moneymong/moneymong/domain/repository/agency/AgencyRepository.kt @@ -7,6 +7,7 @@ import com.moneymong.moneymong.model.agency.AgencyJoinResponse import com.moneymong.moneymong.model.agency.AgencyRegisterRequest import com.moneymong.moneymong.model.agency.CategoryCreateRequest import com.moneymong.moneymong.model.agency.CategoryCreateResponse +import com.moneymong.moneymong.model.agency.CategoryReadResponse import com.moneymong.moneymong.model.agency.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse import kotlinx.coroutines.flow.Flow @@ -21,4 +22,5 @@ interface AgencyRepository { suspend fun saveAgencyId(agencyId: Int) suspend fun fetchAgencyId(): Int suspend fun createCategory(request: CategoryCreateRequest): Result + suspend fun fetchCategories(agencyId: Long): Result } \ No newline at end of file From 9b321f63f3f6fcb232729662ea19566f18be06b3 Mon Sep 17 00:00:00 2001 From: Heon Date: Wed, 22 Oct 2025 22:35:39 +0900 Subject: [PATCH 04/10] refactor: path variables to query parameters --- .../java/com/moneymong/moneymong/network/api/AgencyApi.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 5f2c4b5f..f46e9446 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 @@ -41,9 +41,9 @@ interface AgencyApi { @Query("keyword") name: String ): Result> - @GET("api/v1/agencies/categories/{agencyId}") + @GET("api/v1/agencies/categories") suspend fun fetchCategories( - @Path("agencyId") agencyId: Long + @Query("agencyId") agencyId: Long ): Result // POST From ea234a1bdda9f0b92386617d3c37ff8fa43ee102 Mon Sep 17 00:00:00 2001 From: Heon Date: Wed, 22 Oct 2025 22:36:03 +0900 Subject: [PATCH 05/10] feat: fetch categories usecase --- .../domain/usecase/agency/FetchCategoriesUseCase.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 domain/src/main/java/com/moneymong/moneymong/domain/usecase/agency/FetchCategoriesUseCase.kt diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/agency/FetchCategoriesUseCase.kt b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/agency/FetchCategoriesUseCase.kt new file mode 100644 index 00000000..41501a56 --- /dev/null +++ b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/agency/FetchCategoriesUseCase.kt @@ -0,0 +1,12 @@ +package com.moneymong.moneymong.domain.usecase.agency + +import com.moneymong.moneymong.domain.repository.agency.AgencyRepository +import com.moneymong.moneymong.model.agency.CategoryReadResponse +import javax.inject.Inject + +class FetchCategoriesUseCase @Inject constructor( + private val agencyRepository: AgencyRepository +) { + suspend operator fun invoke(agencyId: Long): Result = + agencyRepository.fetchCategories(agencyId = agencyId) +} \ No newline at end of file From 8a7d13db553448923bd2e4c78a09b06941655922 Mon Sep 17 00:00:00 2001 From: Heon Date: Wed, 22 Oct 2025 22:36:27 +0900 Subject: [PATCH 06/10] feat: fetch categories viewmodel method --- .../ledgermanual/LedgerManualViewModel.kt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt index d028d303..cb75d408 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt @@ -9,6 +9,7 @@ import com.moneymong.moneymong.ui.isValidPaymentDate import com.moneymong.moneymong.ui.isValidPaymentTime import com.moneymong.moneymong.ui.validateValue import com.moneymong.moneymong.domain.usecase.agency.FetchAgencyIdUseCase +import com.moneymong.moneymong.domain.usecase.agency.FetchCategoriesUseCase import com.moneymong.moneymong.domain.usecase.ledger.PostLedgerTransactionUseCase import com.moneymong.moneymong.domain.usecase.ocr.PostFileUploadUseCase import com.moneymong.moneymong.domain.usecase.user.FetchUserNicknameUseCase @@ -32,10 +33,12 @@ class LedgerManualViewModel @Inject constructor( private val fetchAgencyIdUseCase: FetchAgencyIdUseCase, private val fetchUserNicknameUseCase: FetchUserNicknameUseCase, private val createCategoryUseCase: CreateCategoryUseCase, + private val fetchCategoriesUseCase: FetchCategoriesUseCase, ) : BaseViewModel(LedgerManualState()) { init { fetchUserInfo() + fetchCategories() } @OptIn(OrbitExperimental::class) @@ -98,7 +101,10 @@ class LedgerManualViewModel @Inject constructor( fun createCategory() = intent { val request = - CategoryCreateRequest(agencyId = state.agencyId.toLong(), name = state.categoryValue.text) + CategoryCreateRequest( + agencyId = state.agencyId.toLong(), + name = state.categoryValue.text + ) createCategoryUseCase(request) .onSuccess { @@ -118,6 +124,15 @@ class LedgerManualViewModel @Inject constructor( } } + fun fetchCategories() = intent { + fetchCategoriesUseCase(agencyId = state.agencyId.toLong()) + .onSuccess { + reduce { + state.copy(categories = it.categories) + } + } + } + fun onChangeStoreNameValue(value: TextFieldValue) = blockingIntent { val validate = value.text.validateValue(length = 20) if (!validate) { From fa4edad9a5d07a501f70dc20c95143fb1ca4aaba Mon Sep 17 00:00:00 2001 From: Heon Date: Wed, 22 Oct 2025 22:42:43 +0900 Subject: [PATCH 07/10] feat: binding categories --- .../moneymong/ledgermanual/LedgerManualScreen.kt | 14 ++++++++------ .../ledgermanual/LedgerManualViewModel.kt | 1 + .../view/LedgerManualCategoryBottomSheet.kt | 1 + 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt index 7a8d7a31..746ab2b6 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt @@ -360,13 +360,15 @@ fun LedgerManualScreen( } Spacer(modifier = Modifier.height(8.dp)) FlowRow( - horizontalArrangement = Arrangement.spacedBy(10.dp) + horizontalArrangement = Arrangement.spacedBy(10.dp), + verticalArrangement = Arrangement.spacedBy(10.dp), ) { - MDSOutlineTag( - text = "Test", // TODO - iconResource = drawable.ic_close_default, - onClick = {}, - ) + state.categories?.forEach { category -> + MDSOutlineTag( + text = category.name, + onClick = {}, + ) + } } Spacer(modifier = Modifier.height(24.dp)) Text( diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt index cb75d408..2d179ce5 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt @@ -108,6 +108,7 @@ class LedgerManualViewModel @Inject constructor( createCategoryUseCase(request) .onSuccess { + fetchCategories() reduce { state.copy( showBottomSheet = false, diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/view/LedgerManualCategoryBottomSheet.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/view/LedgerManualCategoryBottomSheet.kt index d473d583..5d22a295 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/view/LedgerManualCategoryBottomSheet.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/view/LedgerManualCategoryBottomSheet.kt @@ -166,6 +166,7 @@ fun LedgerManualCategoryBottomSheetContent( Spacer(modifier = Modifier.height(16.dp)) FlowRow( horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), ) { categories?.forEach { MDSOutlineTag( From eb67035b72fe0e974e8fad594d05755f7a05356a Mon Sep 17 00:00:00 2001 From: Heon Date: Wed, 22 Oct 2025 23:06:57 +0900 Subject: [PATCH 08/10] feat: ledger manual category click event handle --- .../design_system/component/tag/Tag.kt | 52 ++++++++++++++----- .../ledgermanual/LedgerManualScreen.kt | 8 +-- .../ledgermanual/LedgerManualState.kt | 3 +- .../ledgermanual/LedgerManualViewModel.kt | 13 +++++ .../view/LedgerManualCategoryBottomSheet.kt | 1 - 5 files changed, 58 insertions(+), 19 deletions(-) diff --git a/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/tag/Tag.kt b/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/tag/Tag.kt index 935b6e12..5929fafb 100644 --- a/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/tag/Tag.kt +++ b/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/tag/Tag.kt @@ -3,6 +3,7 @@ package com.moneymong.moneymong.design_system.component.tag import androidx.annotation.DrawableRes import androidx.compose.foundation.background import androidx.compose.foundation.border +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding @@ -13,16 +14,19 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.moneymong.moneymong.design_system.R import com.moneymong.moneymong.design_system.theme.Blue04 import com.moneymong.moneymong.design_system.theme.Body2 import com.moneymong.moneymong.design_system.theme.Body3 import com.moneymong.moneymong.design_system.theme.Gray03 import com.moneymong.moneymong.design_system.theme.Gray05 import com.moneymong.moneymong.design_system.theme.Gray06 +import com.moneymong.moneymong.design_system.theme.Gray08 import com.moneymong.moneymong.design_system.theme.White import com.moneymong.moneymong.ui.noRippleClickable @@ -64,38 +68,56 @@ fun MDSTag( fun MDSOutlineTag( modifier: Modifier = Modifier, text: String, + selected: Boolean = false, @DrawableRes iconResource: Int? = null, - onClick: () -> Unit, + onClick: () -> Unit = {}, ) { Row( modifier = modifier .border( width = 1.4.dp, - color = Gray03, + color = if (selected) Blue04 else Gray03, shape = RoundedCornerShape(size = Int.MAX_VALUE.dp) ) + .clip(RoundedCornerShape(size = Int.MAX_VALUE.dp)) .background( color = White, shape = RoundedCornerShape(size = Int.MAX_VALUE.dp) ) + .clickable( + enabled = iconResource == null, + onClick = onClick + ) .padding(horizontal = 12.dp, vertical = 6.dp), horizontalArrangement = Arrangement.spacedBy(2.dp), verticalAlignment = Alignment.CenterVertically ) { Text( text = text, - color = Gray06, + color = if (selected) Gray08 else Gray06, style = Body3, ) - if (iconResource != null) { - Icon( - modifier = Modifier - .size(18.dp) - .noRippleClickable(onClick), - painter = painterResource(id = iconResource), - contentDescription = "Tag icon", - tint = Gray05 - ) + when { + !selected && iconResource != null -> { + Icon( + modifier = Modifier + .size(18.dp) + .noRippleClickable(onClick), + painter = painterResource(id = iconResource), + contentDescription = "Tag icon", + tint = Gray05 + ) + } + + selected -> { + Icon( + modifier = Modifier + .size(18.dp), + painter = painterResource(id = R.drawable.ic_check), + contentDescription = "Tag icon", + tint = Blue04 + ) + } } } } @@ -116,7 +138,7 @@ fun MDSTagPreview() { text = "tag", backgroundColor = Blue04, contentColor = White, - iconResource = com.moneymong.moneymong.design_system.R.drawable.ic_pencil + iconResource = R.drawable.ic_pencil ) } } @@ -130,11 +152,13 @@ fun MDSOutlineTagPreview() { ) { MDSOutlineTag( text = "tag", + selected = false, onClick = {}, ) MDSOutlineTag( text = "tag", - iconResource = com.moneymong.moneymong.design_system.R.drawable.ic_close_default, + selected = true, + iconResource = R.drawable.ic_close_default, onClick = {}, ) } diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt index 746ab2b6..70f91506 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt @@ -89,7 +89,8 @@ import kotlinx.coroutines.launch import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect -@OptIn(ExperimentalGlideComposeApi::class, ExperimentalMaterial3Api::class, +@OptIn( + ExperimentalGlideComposeApi::class, ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class ) @Composable @@ -363,10 +364,11 @@ fun LedgerManualScreen( horizontalArrangement = Arrangement.spacedBy(10.dp), verticalArrangement = Arrangement.spacedBy(10.dp), ) { - state.categories?.forEach { category -> + state.categories.forEach { category -> MDSOutlineTag( text = category.name, - onClick = {}, + selected = state.selectedCategories.contains(category), + onClick = { viewModel.onClickCategory(category) }, ) } } diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualState.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualState.kt index 24617e6f..7428badf 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualState.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualState.kt @@ -29,7 +29,8 @@ data class LedgerManualState( val errorMessage: String = "", val showBottomSheet: Boolean = false, val categoryValue: TextFieldValue = TextFieldValue(), - val categories: List? = null, + val categories: List = emptyList(), + val selectedCategories: Set = emptySet() ) : State { val enabled: Boolean diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt index 2d179ce5..6de5b0da 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt @@ -14,6 +14,7 @@ import com.moneymong.moneymong.domain.usecase.ledger.PostLedgerTransactionUseCas import com.moneymong.moneymong.domain.usecase.ocr.PostFileUploadUseCase import com.moneymong.moneymong.domain.usecase.user.FetchUserNicknameUseCase import com.moneymong.moneymong.model.agency.CategoryCreateRequest +import com.moneymong.moneymong.model.agency.CategoryResponse import com.moneymong.moneymong.model.ledger.FundType import com.moneymong.moneymong.model.ledger.LedgerTransactionRequest import com.moneymong.moneymong.model.ocr.FileUploadRequest @@ -231,6 +232,18 @@ class LedgerManualViewModel @Inject constructor( } } + fun onClickCategory(category: CategoryResponse) = intent { + val existsCategory = state.selectedCategories.contains(category) + + val targetCategories = if (existsCategory) { + state.selectedCategories - category + } else { + state.selectedCategories + category + } + + reduce { state.copy(selectedCategories = targetCategories) } + } + private fun trimStartWithZero(value: TextFieldValue) = if (value.text.isNotEmpty() && value.text.all { it == '0' }) { value.copy(text = "0") diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/view/LedgerManualCategoryBottomSheet.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/view/LedgerManualCategoryBottomSheet.kt index 5d22a295..c16af649 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/view/LedgerManualCategoryBottomSheet.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/view/LedgerManualCategoryBottomSheet.kt @@ -172,7 +172,6 @@ fun LedgerManualCategoryBottomSheetContent( MDSOutlineTag( text = it.name, iconResource = R.drawable.ic_close_default, - onClick = {}, ) } } From 18a1cb65f427b0ad3ae3358f4c8b2bdf840d4ebc Mon Sep 17 00:00:00 2001 From: Heon Date: Wed, 22 Oct 2025 23:26:38 +0900 Subject: [PATCH 09/10] feat: category state clean up --- .../moneymong/ledgermanual/LedgerManualScreen.kt | 6 ++++-- .../moneymong/ledgermanual/LedgerManualViewModel.kt | 9 ++------- .../ledgermanual/view/LedgerManualCategoryBottomSheet.kt | 9 ++++++++- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt index 70f91506..888a4a9f 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt @@ -34,7 +34,6 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -192,7 +191,10 @@ fun LedgerManualScreen( onDismissRequest = { scope.launch { sheetState.hide() - }.invokeOnCompletion { viewModel.onDismissBottomSheet() } + }.invokeOnCompletion { + viewModel.onDismissBottomSheet() + viewModel.onChangeCategoryValue(TextFieldValue()) + } }, onChangeCategoryValue = viewModel::onChangeCategoryValue, onCategoryCreate = viewModel::createCategory, diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt index 6de5b0da..fc869ccc 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt @@ -110,12 +110,7 @@ class LedgerManualViewModel @Inject constructor( createCategoryUseCase(request) .onSuccess { fetchCategories() - reduce { - state.copy( - showBottomSheet = false, - categoryValue = TextFieldValue(), - ) - } + reduce { state.copy(showBottomSheet = false) } }.onFailure { reduce { state.copy( @@ -123,7 +118,7 @@ class LedgerManualViewModel @Inject constructor( errorMessage = it.message ?: MoneyMongError.UnExpectedError.message ) } - } + }.also { onChangeCategoryValue(TextFieldValue()) } } fun fetchCategories() = intent { diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/view/LedgerManualCategoryBottomSheet.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/view/LedgerManualCategoryBottomSheet.kt index c16af649..9364348c 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/view/LedgerManualCategoryBottomSheet.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/view/LedgerManualCategoryBottomSheet.kt @@ -16,6 +16,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.material3.ExperimentalMaterial3Api @@ -109,7 +111,10 @@ fun LedgerManualCategoryBottomSheet( categories = categories, onValueChange = onChangeCategoryValue, onClickRegister = onCategoryCreate, - onPrev = { sheetType = LedgerManualBottomSheetType.LIST } + onPrev = { + sheetType = LedgerManualBottomSheetType.LIST + onChangeCategoryValue(TextFieldValue()) + } ) } } @@ -125,6 +130,7 @@ fun LedgerManualCategoryBottomSheetContent( onDismissRequest: () -> Unit, onClickCreate: () -> Unit, ) { + val scrollState = rememberScrollState() Column( modifier = modifier .fillMaxWidth() @@ -165,6 +171,7 @@ fun LedgerManualCategoryBottomSheetContent( ) Spacer(modifier = Modifier.height(16.dp)) FlowRow( + modifier = Modifier.verticalScroll(scrollState), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp), ) { From 1402eb3b698ab36405961a1dd559672670015e2f Mon Sep 17 00:00:00 2001 From: Heon Date: Tue, 28 Oct 2025 22:46:24 +0900 Subject: [PATCH 10/10] refactor: category click event change multiple to single --- .../moneymong/ledgermanual/LedgerManualScreen.kt | 3 ++- .../moneymong/ledgermanual/LedgerManualState.kt | 2 +- .../moneymong/ledgermanual/LedgerManualViewModel.kt | 10 +--------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt index 888a4a9f..f18169da 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualScreen.kt @@ -367,9 +367,10 @@ fun LedgerManualScreen( verticalArrangement = Arrangement.spacedBy(10.dp), ) { state.categories.forEach { category -> + val isSelected = category == state.selectedCategory MDSOutlineTag( text = category.name, - selected = state.selectedCategories.contains(category), + selected = isSelected, onClick = { viewModel.onClickCategory(category) }, ) } diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualState.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualState.kt index 7428badf..c8001f71 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualState.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualState.kt @@ -30,7 +30,7 @@ data class LedgerManualState( val showBottomSheet: Boolean = false, val categoryValue: TextFieldValue = TextFieldValue(), val categories: List = emptyList(), - val selectedCategories: Set = emptySet() + val selectedCategory: CategoryResponse? = null ) : State { val enabled: Boolean diff --git a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt index fc869ccc..a8b7ce5e 100644 --- a/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt +++ b/feature/ledgermanual/src/main/java/com/moneymong/moneymong/ledgermanual/LedgerManualViewModel.kt @@ -228,15 +228,7 @@ class LedgerManualViewModel @Inject constructor( } fun onClickCategory(category: CategoryResponse) = intent { - val existsCategory = state.selectedCategories.contains(category) - - val targetCategories = if (existsCategory) { - state.selectedCategories - category - } else { - state.selectedCategories + category - } - - reduce { state.copy(selectedCategories = targetCategories) } + reduce { state.copy(selectedCategory = category) } } private fun trimStartWithZero(value: TextFieldValue) =