diff --git a/core/model/src/main/java/com/moneymong/moneymong/model/agency/CategoryCreateRequest.kt b/core/model/src/main/java/com/moneymong/moneymong/model/agency/CategoryCreateRequest.kt new file mode 100644 index 00000000..c7c1d059 --- /dev/null +++ b/core/model/src/main/java/com/moneymong/moneymong/model/agency/CategoryCreateRequest.kt @@ -0,0 +1,6 @@ +package com.moneymong.moneymong.model.agency + +data class CategoryCreateRequest( + val agencyId: Long, + val name: String, +) diff --git a/core/model/src/main/java/com/moneymong/moneymong/model/agency/CategoryCreateResponse.kt b/core/model/src/main/java/com/moneymong/moneymong/model/agency/CategoryCreateResponse.kt new file mode 100644 index 00000000..cbf5d8bc --- /dev/null +++ b/core/model/src/main/java/com/moneymong/moneymong/model/agency/CategoryCreateResponse.kt @@ -0,0 +1,6 @@ +package com.moneymong.moneymong.model.agency + +data class CategoryCreateResponse( + val agencyId: Long, + val name: String, +) 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 new file mode 100644 index 00000000..a54fa491 --- /dev/null +++ b/core/model/src/main/java/com/moneymong/moneymong/model/agency/CategoryReadResponse.kt @@ -0,0 +1,9 @@ +package com.moneymong.moneymong.model.agency + +data class CategoryReadResponse( + val categories: List, +) + +data class CategoryResponse( + val name: String, +) 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 bda1058b..7c666c20 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 @@ -5,6 +5,8 @@ import com.moneymong.moneymong.model.agency.AgencyGetResponse import com.moneymong.moneymong.model.agency.AgencyJoinRequest 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.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse import com.moneymong.moneymong.model.member.InvitationCodeResponse @@ -49,6 +51,11 @@ interface AgencyApi { @Body request: AgencyRegisterRequest ): Result + @POST("api/v1/agencies/categories") + suspend fun createCategory( + @Body request: CategoryCreateRequest + ): Result + // PATCH @PATCH("api/v1/agencies/{agencyId}/invitation-code") suspend fun reInvitationCode( 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 3bd7fe51..23637a70 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 @@ -5,6 +5,8 @@ import com.moneymong.moneymong.model.agency.AgencyGetResponse import com.moneymong.moneymong.model.agency.AgencyJoinRequest 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.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse @@ -14,4 +16,5 @@ interface AgencyRemoteDataSource { suspend fun fetchMyAgencyList(): Result> suspend fun fetchAgencyByName(agencyName: String): Result> suspend fun agencyCodeNumbers(data: AgencyJoinRequest): Result + suspend fun createCategory(request: CategoryCreateRequest): 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 2495a6d4..8af2e0e7 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 @@ -5,6 +5,8 @@ import com.moneymong.moneymong.model.agency.AgencyGetResponse import com.moneymong.moneymong.model.agency.AgencyJoinRequest 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.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse import com.moneymong.moneymong.network.api.AgencyApi @@ -35,4 +37,8 @@ class AgencyRemoteDataSourceImpl @Inject constructor( ): Result { return agencyApi.agencyCodeNumbers(body = data) } + + override suspend fun createCategory(request: CategoryCreateRequest): Result { + return agencyApi.createCategory(request = request) + } } \ 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 2aceeedd..9ef13a1d 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 @@ -5,6 +5,8 @@ import com.moneymong.moneymong.model.agency.AgencyGetResponse import com.moneymong.moneymong.model.agency.AgencyJoinRequest 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.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse import kotlinx.coroutines.delay @@ -36,6 +38,10 @@ class AgencyRemoteDataSourceMock : AgencyRemoteDataSource { ) } + override suspend fun createCategory(request: CategoryCreateRequest): Result { + return Result.success(CategoryCreateResponse(agencyId = 1L, name = "category")) + } + private companion object { val agenciesMockOfSuccess = listOf( Result.success( 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 459a807c..4c15c851 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 @@ -11,6 +11,8 @@ import com.moneymong.moneymong.model.agency.AgencyGetResponse import com.moneymong.moneymong.model.agency.AgencyJoinRequest 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.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse import kotlinx.coroutines.flow.Flow @@ -49,4 +51,7 @@ class AgencyRepositoryImpl @Inject constructor( override suspend fun fetchAgencyId(): Int = agencyLocalDataSource.fetchAgencyId() + + override suspend fun createCategory(request: CategoryCreateRequest): Result = + agencyRemoteDataSource.createCategory(request = request) } \ 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 94762027..f7d686c5 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 @@ -5,6 +5,8 @@ import com.moneymong.moneymong.model.agency.AgencyGetResponse import com.moneymong.moneymong.model.agency.AgencyJoinRequest 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.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse import kotlinx.coroutines.flow.Flow @@ -18,4 +20,5 @@ interface AgencyRepository { suspend fun saveAgencyId(agencyId: Int) suspend fun fetchAgencyId(): Int + suspend fun createCategory(request: CategoryCreateRequest): Result } \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/agency/CreateCategoryUseCase.kt b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/agency/CreateCategoryUseCase.kt new file mode 100644 index 00000000..21594d71 --- /dev/null +++ b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/agency/CreateCategoryUseCase.kt @@ -0,0 +1,13 @@ +package com.moneymong.moneymong.domain.usecase.agency + +import com.moneymong.moneymong.domain.repository.agency.AgencyRepository +import com.moneymong.moneymong.model.agency.CategoryCreateRequest +import com.moneymong.moneymong.model.agency.CategoryCreateResponse +import javax.inject.Inject + +class CreateCategoryUseCase @Inject constructor( + private val agencyRepository: AgencyRepository, +) { + suspend operator fun invoke(request: CategoryCreateRequest): Result = + agencyRepository.createCategory(request = request) +} \ No newline at end of file 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 288df93f..7a8d7a31 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 @@ -59,7 +59,6 @@ import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi import com.bumptech.glide.integration.compose.GlideImage import com.moneymong.moneymong.android.util.base64ToFile import com.moneymong.moneymong.android.util.encodingBase64 -import com.moneymong.moneymong.design_system.R import com.moneymong.moneymong.ui.noRippleClickable import com.moneymong.moneymong.design_system.R.drawable import com.moneymong.moneymong.design_system.component.button.MDSButton @@ -186,7 +185,7 @@ fun LedgerManualScreen( if (state.showBottomSheet) { LedgerManualCategoryBottomSheet( sheetState = sheetState, - categories = emptyList(), + categories = state.categories, categoryValue = state.categoryValue, isSystemCategoryError = state.isSystemCategoryError, onDismissRequest = { @@ -194,7 +193,8 @@ fun LedgerManualScreen( sheetState.hide() }.invokeOnCompletion { viewModel.onDismissBottomSheet() } }, - onChangeCategoryValue = viewModel::onChangeCategoryValue + onChangeCategoryValue = viewModel::onChangeCategoryValue, + onCategoryCreate = viewModel::createCategory, ) } 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 1a3a2eea..24617e6f 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 @@ -4,6 +4,7 @@ import androidx.compose.ui.text.input.TextFieldValue import com.moneymong.moneymong.android.State import com.moneymong.moneymong.common.util.toZonedDateTime import com.moneymong.moneymong.design_system.component.textfield.util.PriceType +import com.moneymong.moneymong.model.agency.CategoryResponse import com.moneymong.moneymong.model.ledger.FundType import java.text.SimpleDateFormat @@ -28,6 +29,7 @@ data class LedgerManualState( val errorMessage: String = "", val showBottomSheet: Boolean = false, val categoryValue: TextFieldValue = TextFieldValue(), + val categories: List? = 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 89579c21..d028d303 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 @@ -3,6 +3,8 @@ package com.moneymong.moneymong.ledgermanual import androidx.compose.ui.text.input.TextFieldValue import com.moneymong.moneymong.android.BaseViewModel import com.moneymong.moneymong.android.util.toMultipart +import com.moneymong.moneymong.common.error.MoneyMongError +import com.moneymong.moneymong.domain.usecase.agency.CreateCategoryUseCase import com.moneymong.moneymong.ui.isValidPaymentDate import com.moneymong.moneymong.ui.isValidPaymentTime import com.moneymong.moneymong.ui.validateValue @@ -10,6 +12,7 @@ import com.moneymong.moneymong.domain.usecase.agency.FetchAgencyIdUseCase import com.moneymong.moneymong.domain.usecase.ledger.PostLedgerTransactionUseCase 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.ledger.FundType import com.moneymong.moneymong.model.ledger.LedgerTransactionRequest import com.moneymong.moneymong.model.ocr.FileUploadRequest @@ -27,7 +30,8 @@ class LedgerManualViewModel @Inject constructor( private val postLedgerTransactionUseCase: PostLedgerTransactionUseCase, private val postFileUploadUseCase: PostFileUploadUseCase, private val fetchAgencyIdUseCase: FetchAgencyIdUseCase, - private val fetchUserNicknameUseCase: FetchUserNicknameUseCase + private val fetchUserNicknameUseCase: FetchUserNicknameUseCase, + private val createCategoryUseCase: CreateCategoryUseCase, ) : BaseViewModel(LedgerManualState()) { init { @@ -92,6 +96,28 @@ class LedgerManualViewModel @Inject constructor( } } + fun createCategory() = intent { + val request = + CategoryCreateRequest(agencyId = state.agencyId.toLong(), name = state.categoryValue.text) + + createCategoryUseCase(request) + .onSuccess { + reduce { + state.copy( + showBottomSheet = false, + categoryValue = TextFieldValue(), + ) + } + }.onFailure { + reduce { + state.copy( + showErrorDialog = true, + errorMessage = it.message ?: MoneyMongError.UnExpectedError.message + ) + } + } + } + fun onChangeStoreNameValue(value: TextFieldValue) = blockingIntent { val validate = value.text.validateValue(length = 20) if (!validate) { 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 ffd36754..d473d583 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 @@ -55,6 +55,7 @@ import com.moneymong.moneymong.design_system.theme.Gray07 import com.moneymong.moneymong.design_system.theme.Heading1 import com.moneymong.moneymong.design_system.theme.Heading4 import com.moneymong.moneymong.design_system.theme.White +import com.moneymong.moneymong.model.agency.CategoryResponse import com.moneymong.moneymong.ui.noRippleClickable @OptIn(ExperimentalMaterial3Api::class) @@ -62,11 +63,12 @@ import com.moneymong.moneymong.ui.noRippleClickable fun LedgerManualCategoryBottomSheet( modifier: Modifier = Modifier, sheetState: SheetState, - categories: List, // TODO API response + categories: List?, categoryValue: TextFieldValue, isSystemCategoryError: Boolean, onDismissRequest: () -> Unit, onChangeCategoryValue: (TextFieldValue) -> Unit, + onCategoryCreate: () -> Unit, ) { var sheetType by remember { mutableStateOf(LedgerManualBottomSheetType.LIST) } @@ -106,7 +108,7 @@ fun LedgerManualCategoryBottomSheet( isSystemCategoryError = isSystemCategoryError, categories = categories, onValueChange = onChangeCategoryValue, - onClickRegister = {}, + onClickRegister = onCategoryCreate, onPrev = { sheetType = LedgerManualBottomSheetType.LIST } ) } @@ -119,7 +121,7 @@ fun LedgerManualCategoryBottomSheet( @Composable fun LedgerManualCategoryBottomSheetContent( modifier: Modifier = Modifier, - categories: List, + categories: List?, onDismissRequest: () -> Unit, onClickCreate: () -> Unit, ) { @@ -165,9 +167,9 @@ fun LedgerManualCategoryBottomSheetContent( FlowRow( horizontalArrangement = Arrangement.spacedBy(12.dp), ) { - categories.forEach { + categories?.forEach { MDSOutlineTag( - text = it, + text = it.name, iconResource = R.drawable.ic_close_default, onClick = {}, ) @@ -181,7 +183,7 @@ fun LedgerManualCategoryCreateBottomSheetContent( modifier: Modifier = Modifier, textFieldValue: TextFieldValue, isSystemCategoryError: Boolean, - categories: List, // TODO API response + categories: List?, onValueChange: (TextFieldValue) -> Unit, onClickRegister: () -> Unit, onPrev: () -> Unit, @@ -190,7 +192,7 @@ fun LedgerManualCategoryCreateBottomSheetContent( var isFilled by remember { mutableStateOf(false) } val focusRequester = remember { FocusRequester() } val keyboard = LocalSoftwareKeyboardController.current - val isExists = categories.contains(textFieldValue.text) + val isExists = categories?.any { it.name == textFieldValue.text } ?: false val helperText by remember(isSystemCategoryError, isExists) { derivedStateOf { when { @@ -277,7 +279,7 @@ fun LedgerManualCategoryCreateBottomSheetContent( @Preview(showBackground = true) @Composable fun LedgerManualCategoryBottomSheetContentPreview() { - val categories = listOf("testTooLongTextOverFlow", "test") + val categories = listOf(CategoryResponse("testTooLongTextOverFlow"), CategoryResponse("test")) LedgerManualCategoryBottomSheetContent( categories = categories,