diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/BottomSheetScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/BottomSheetScreen.kt index 0b71a105..eb826819 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/BottomSheetScreen.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/BottomSheetScreen.kt @@ -42,7 +42,10 @@ fun BottomSheetScreen( .fillMaxWidth() .wrapContentHeight(), color = Color.White, - shape = RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp) + shape = RoundedCornerShape( + topStart = 12.dp, + topEnd = 12.dp + ) ) { content() } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/chip/ChipItem.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/chip/ChipItem.kt index 31ecf612..36a5dff8 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/chip/ChipItem.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/chip/ChipItem.kt @@ -21,6 +21,7 @@ import androidx.compose.material.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.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -28,13 +29,13 @@ import androidx.compose.ui.unit.dp @Composable fun ChipItem( chipType: ChipType = ChipType.LESS_BORDER, - currentSelectedId: Long, + currentSelectedId: Set, chipId: Long, chipText: String, chipCount: Int = 0, onSelectChip: (chipId: Long) -> Unit ) { - val isSelected = currentSelectedId == chipId + val isSelected = chipId in currentSelectedId val backgroundColor = animateColorAsState( targetValue = when (chipType) { ChipType.MAIN -> if (isSelected) Gray700 else Gray000 @@ -59,6 +60,7 @@ fun ChipItem( Row( modifier = Modifier + .clip(RoundedCornerShape(100.dp)) .background( color = backgroundColor.value, shape = RoundedCornerShape(100.dp) @@ -68,13 +70,13 @@ fun ChipItem( width = 1.dp, shape = RoundedCornerShape(100.dp) ) + .clickable { + onSelectChip(chipId) + } .padding( horizontal = 14.dp, vertical = 6.5.dp - ) - .clickable { - onSelectChip(chipId) - }, + ), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center ) { @@ -102,7 +104,7 @@ fun ChipsType1Preview(){ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)){ ChipItem( chipType = ChipType.LESS_BORDER, - currentSelectedId = 0, + currentSelectedId = setOf(0), chipId = 0, chipText = "가족", chipCount = 5, @@ -110,7 +112,7 @@ fun ChipsType1Preview(){ ) ChipItem( chipType = ChipType.LESS_BORDER, - currentSelectedId = 0, + currentSelectedId = setOf(0), chipId = 1, chipText = "친구", chipCount = 0, @@ -125,7 +127,7 @@ fun ChipsType2Preview(){ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)){ ChipItem( chipType = ChipType.BORDER, - currentSelectedId = 0, + currentSelectedId = setOf(0), chipId = 0, chipText = "가족", chipCount = 5, @@ -133,7 +135,7 @@ fun ChipsType2Preview(){ ) ChipItem( chipType = ChipType.BORDER, - currentSelectedId = 0, + currentSelectedId = setOf(0), chipId = 1, chipText = "친구", chipCount = 0, @@ -148,7 +150,7 @@ fun ChipsType3Preview(){ Row(horizontalArrangement = Arrangement.spacedBy(6.dp)){ ChipItem( chipType = ChipType.MAIN, - currentSelectedId = 0, + currentSelectedId = setOf(0), chipId = 0, chipText = "가족", chipCount = 5, @@ -156,7 +158,7 @@ fun ChipsType3Preview(){ ) ChipItem( chipType = ChipType.MAIN, - currentSelectedId = 0, + currentSelectedId = setOf(0), chipId = 1, chipText = "친구", chipCount = 0, diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/chip/GroupChipListComponent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/chip/GroupChipListComponent.kt index bf651358..ebdb07ce 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/chip/GroupChipListComponent.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/chip/GroupChipListComponent.kt @@ -21,7 +21,7 @@ fun GroupChipListComponent( items(groups) { group -> ChipItem( chipType = chipType, - currentSelectedId = currentSelectedId, + currentSelectedId = setOf(currentSelectedId), chipId = group.id, chipText = group.name, chipCount = group.relations.size, diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/component/FieldSelectComponent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/component/FieldSelectComponent.kt new file mode 100644 index 00000000..3fa93d4e --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/component/FieldSelectComponent.kt @@ -0,0 +1,83 @@ +package ac.dnd.bookkeeping.android.presentation.common.view.component + +import ac.dnd.bookkeeping.android.presentation.R +import ac.dnd.bookkeeping.android.presentation.common.theme.Body1 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray000 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray400 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray800 +import ac.dnd.bookkeeping.android.presentation.common.theme.Primary4 +import ac.dnd.bookkeeping.android.presentation.common.theme.Shapes +import ac.dnd.bookkeeping.android.presentation.common.theme.Space12 +import ac.dnd.bookkeeping.android.presentation.common.theme.Space16 +import ac.dnd.bookkeeping.android.presentation.common.theme.Space48 +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.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.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp + +@Composable +fun FieldSelectComponent( + isSelected: Boolean, + text: String, + onClick: () -> Unit +) { + Box( + modifier = Modifier + .clip(Shapes.medium) + .background(color = Gray000) + .border( + width = 1.dp, + color = if (isSelected) Primary4 else Gray400, + shape = Shapes.medium + ) + .fillMaxWidth() + .height(Space48) + .clickable { + onClick() + } + .padding( + start = Space16, + end = Space12, + top = Space12, + bottom = Space12 + ) + ) { + Text( + text = text, + style = Body1.merge( + color = Gray800, + fontWeight = FontWeight.Normal + ), + modifier = Modifier.align(Alignment.CenterStart) + ) + + Image( + painter = painterResource(R.drawable.ic_chevron_right), + contentDescription = null, + modifier = Modifier.align(Alignment.CenterEnd) + ) + } +} + +@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true) +@Composable +fun FieldSelectComponentPreview() { + FieldSelectComponent( + isSelected = true, + text = "2024/01/10", + onClick = {} + ) +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/component/FieldSubjectComponent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/component/FieldSubjectComponent.kt new file mode 100644 index 00000000..ae174e8e --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/component/FieldSubjectComponent.kt @@ -0,0 +1,55 @@ +package ac.dnd.bookkeeping.android.presentation.common.view.component + +import ac.dnd.bookkeeping.android.presentation.R +import ac.dnd.bookkeeping.android.presentation.common.theme.Body1 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray700 +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp + +@Composable +fun FieldSubject( + subject: String, + isViewIcon: Boolean = true +) { + Column { + Row { + Text( + text = subject, + style = Body1.merge( + color = Gray700, + fontWeight = FontWeight.SemiBold + ) + ) + Spacer(modifier = Modifier.width(1.dp)) + Box( + modifier = Modifier + .height(21.dp) + .alpha(if (isViewIcon) 100f else 0f) + .padding(bottom = 3.dp), + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(R.drawable.ic_essential_field), + contentDescription = null + ) + } + } + Spacer( + modifier = Modifier.height(18.dp) + ) + } +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/textfield/PriceChipComponent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/textfield/PriceChipComponent.kt index b6815794..bf3436cf 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/textfield/PriceChipComponent.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/textfield/PriceChipComponent.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue 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.text.font.FontWeight @@ -58,10 +59,8 @@ fun PriceChipComponent( ) Row( modifier = Modifier - .background( - color = backgroundColorState.value, - shape = Shapes.medium - ) + .clip(Shapes.medium) + .background(color = backgroundColorState.value) .clickable { onClickChip(money) scope.launch { diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/textfield/TypingPriceField.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/textfield/TypingPriceField.kt index 1d5123b0..14537866 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/textfield/TypingPriceField.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/textfield/TypingPriceField.kt @@ -9,6 +9,7 @@ import ac.dnd.bookkeeping.android.presentation.common.theme.Headline3 import ac.dnd.bookkeeping.android.presentation.common.theme.Negative import ac.dnd.bookkeeping.android.presentation.common.theme.Primary3 import ac.dnd.bookkeeping.android.presentation.common.util.expansion.NumberCommaTransformation +import ac.dnd.bookkeeping.android.presentation.common.view.component.FieldSubject import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -62,7 +63,7 @@ fun TypingPriceField( textFieldHeight: Dp = 35.dp, keyboardOptions: KeyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword), textFormat : VisualTransformation = NumberCommaTransformation(), - fieldSubjectContent: (@Composable () -> Unit) = { FieldSubject() }, + fieldSubjectContent: (@Composable () -> Unit) = { FieldSubject("금액") }, leadingIconContent: (@Composable () -> Unit)? = null, trailingIconContent: (@Composable () -> Unit)? = null, errorMessageContent: (@Composable () -> Unit) = { }, @@ -154,37 +155,6 @@ fun TypingPriceField( ) errorMessageContent() } - -} - -@Composable -private fun FieldSubject() { - Column { - Row { - Text( - text = "금액", - style = Body1.merge( - color = Gray700, - fontWeight = FontWeight.SemiBold - ) - ) - Spacer(modifier = Modifier.width(1.dp)) - Box( - modifier = Modifier - .height(21.dp) - .padding(bottom = 3.dp), - contentAlignment = Alignment.Center - ) { - Image( - painter = painterResource(R.drawable.ic_essential_field), - contentDescription = null - ) - } - } - Spacer( - modifier = Modifier.height(18.dp) - ) - } } @Composable diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/textfield/TypingTextField.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/textfield/TypingTextField.kt index 14d62a03..207c4d32 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/textfield/TypingTextField.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/textfield/TypingTextField.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon @@ -113,6 +114,9 @@ fun TypingTextField( singleLine = isSingleLine, minLines = if (isSingleLine) 1 else 3, keyboardOptions = keyboardOptions, + keyboardActions = KeyboardActions( + + ), cursorBrush = SolidColor(value = currentColorState.value), interactionSource = interactionSource, ) { textField -> diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/HistoryScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/HistoryScreen.kt index 9cef5002..2bd46264 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/HistoryScreen.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/HistoryScreen.kt @@ -4,6 +4,7 @@ import ac.dnd.bookkeeping.android.presentation.common.util.ErrorObserver import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.main.HistoryMainModel import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.main.HistoryMainScreen +import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.main.HistoryMainViewModel import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.hilt.navigation.compose.hiltViewModel @@ -12,7 +13,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle @Composable fun HistoryScreen( appState: ApplicationState, - viewModel: HistoryViewModel = hiltViewModel() + viewModel: HistoryMainViewModel = hiltViewModel() ) { val model: HistoryMainModel = Unit.let { diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/HistoryViewModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/main/HistoryMainViewModel.kt similarity index 88% rename from presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/HistoryViewModel.kt rename to presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/main/HistoryMainViewModel.kt index 5fd24343..660fdb58 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/HistoryViewModel.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/main/HistoryMainViewModel.kt @@ -1,4 +1,4 @@ -package ac.dnd.bookkeeping.android.presentation.ui.main.home.history +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.main import ac.dnd.bookkeeping.android.domain.model.error.ServerException import ac.dnd.bookkeeping.android.domain.model.legacy.HistoryInfoLegacy @@ -8,9 +8,6 @@ import ac.dnd.bookkeeping.android.presentation.common.base.ErrorEvent import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.EventFlow import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.MutableEventFlow import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.asEventFlow -import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.main.HistoryMainEvent -import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.main.HistoryMainIntent -import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.main.HistoryMainState import androidx.lifecycle.SavedStateHandle import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -19,7 +16,7 @@ import kotlinx.coroutines.flow.asStateFlow import javax.inject.Inject @HiltViewModel -class HistoryViewModel @Inject constructor( +class HistoryMainViewModel @Inject constructor( private val savedStateHandle: SavedStateHandle, private val getHistoryInfoUseCase: GetHistoryInfoUseCase ) : BaseViewModel() { diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationConstant.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationConstant.kt new file mode 100644 index 00000000..c169e566 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationConstant.kt @@ -0,0 +1,7 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration + +import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.HistoryConstant + +object HistoryRegistrationConstant { + const val ROUTE: String = "${HistoryConstant.ROUTE}/registration" +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationDestination.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationDestination.kt new file mode 100644 index 00000000..53c1b27f --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationDestination.kt @@ -0,0 +1,16 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration + +import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable + + +fun NavGraphBuilder.historyRegistrationDestination( + appState: ApplicationState +) { + composable( + route = HistoryRegistrationConstant.ROUTE + ) { + + } +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationEvent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationEvent.kt new file mode 100644 index 00000000..ba1199c2 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationEvent.kt @@ -0,0 +1,11 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration + +import ac.dnd.bookkeeping.android.domain.model.error.ServerException + +sealed interface HistoryRegistrationEvent { + sealed interface Submit : HistoryRegistrationEvent { + data object Success : Submit + data class Failure(val exception: ServerException) : Submit + data class Error(val exception: Throwable) : Submit + } +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationIntent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationIntent.kt new file mode 100644 index 00000000..00e25888 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationIntent.kt @@ -0,0 +1,13 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration + +sealed interface HistoryRegistrationIntent { + data class OnClickSubmit( + val relationId: Long, + val give: Boolean, + val money: Long, + val day: String, + val event: String, + val memo: String?, + val tags: List? + ) : HistoryRegistrationIntent +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationModel.kt new file mode 100644 index 00000000..29641785 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationModel.kt @@ -0,0 +1,8 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration + +import javax.annotation.concurrent.Immutable + +@Immutable +data class HistoryRegistrationModel( + val state: HistoryRegistrationState +) diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationScreen.kt new file mode 100644 index 00000000..e0ee90e1 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationScreen.kt @@ -0,0 +1,486 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration + +import ac.dnd.bookkeeping.android.presentation.R +import ac.dnd.bookkeeping.android.presentation.common.theme.Body1 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray000 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray200 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray600 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray700 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray800 +import ac.dnd.bookkeeping.android.presentation.common.theme.Headline1 +import ac.dnd.bookkeeping.android.presentation.common.theme.Headline3 +import ac.dnd.bookkeeping.android.presentation.common.theme.Space12 +import ac.dnd.bookkeeping.android.presentation.common.theme.Space16 +import ac.dnd.bookkeeping.android.presentation.common.theme.Space20 +import ac.dnd.bookkeeping.android.presentation.common.theme.Space24 +import ac.dnd.bookkeeping.android.presentation.common.theme.Space4 +import ac.dnd.bookkeeping.android.presentation.common.theme.Space56 +import ac.dnd.bookkeeping.android.presentation.common.util.LaunchedEffectWithLifecycle +import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.EventFlow +import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.MutableEventFlow +import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.eventObserve +import ac.dnd.bookkeeping.android.presentation.common.util.expansion.addFocusCleaner +import ac.dnd.bookkeeping.android.presentation.common.view.calendar.CalendarConfig +import ac.dnd.bookkeeping.android.presentation.common.view.chip.ChipItem +import ac.dnd.bookkeeping.android.presentation.common.view.chip.ChipType +import ac.dnd.bookkeeping.android.presentation.common.view.component.FieldSelectComponent +import ac.dnd.bookkeeping.android.presentation.common.view.component.FieldSubject +import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButton +import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButtonProperties +import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButtonSize +import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButtonType +import ac.dnd.bookkeeping.android.presentation.common.view.textfield.TypingPriceField +import ac.dnd.bookkeeping.android.presentation.common.view.textfield.TypingTextField +import ac.dnd.bookkeeping.android.presentation.common.view.textfield.TypingTextFieldType +import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState +import ac.dnd.bookkeeping.android.presentation.ui.main.home.event.common.relation.SearchRelationScreen +import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration.calendar.HistoryCalendarScreen +import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration.type.HistoryRegistrationEventType +import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration.type.HistoryRegistrationTagType +import ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration.type.HistoryRegistrationType +import ac.dnd.bookkeeping.android.presentation.ui.main.rememberApplicationState +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.Image +import androidx.compose.foundation.LocalOverscrollConfiguration +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +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.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableLongStateOf +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.CoroutineExceptionHandler + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun HistoryRegistrationScreen( + appState: ApplicationState, + model: HistoryRegistrationModel, + event: EventFlow, + intent: (HistoryRegistrationIntent) -> Unit, + handler: CoroutineExceptionHandler, + calendarConfig: CalendarConfig = CalendarConfig() +) { + val focusManager = LocalFocusManager.current + val scrollState = rememberScrollState() + var historyTypeState by remember { mutableStateOf(HistoryRegistrationType.TAKE) } + var priceText by remember { mutableStateOf("") } + var userNameText by remember { mutableStateOf("이름 선택") } + var relationId by remember { mutableLongStateOf(-1) } + var selectedYear by remember { mutableIntStateOf(calendarConfig.getCalendarYear()) } + var selectedMonth by remember { mutableIntStateOf(calendarConfig.getCalendarMonth()) } + var selectedDay by remember { mutableIntStateOf(calendarConfig.getCalendarDay()) } + var eventTypeText by remember { mutableStateOf("") } + var selectedEventId by remember { mutableLongStateOf(-1) } + var memoText by remember { mutableStateOf("") } + val tagIdList = remember { mutableStateListOf() } + + var isCalendarShowingState by remember { mutableStateOf(false) } + var isAddNameShowingState by remember { mutableStateOf(false) } + val typePositionState = animateDpAsState( + targetValue = if (historyTypeState == HistoryRegistrationType.TAKE) 0.dp else 106.dp, + label = "type background color " + ) + + @Composable + fun getTypeTextColor(currentType: HistoryRegistrationType) = animateColorAsState( + targetValue = if (historyTypeState == currentType) Gray800 else Gray600, + label = "type text color" + ) + + fun navigateToHistory() { + + } + + fun submit(event: HistoryRegistrationEvent.Submit) { + when (event) { + is HistoryRegistrationEvent.Submit.Success -> navigateToHistory() + is HistoryRegistrationEvent.Submit.Error -> { + // TODO Error + } + + is HistoryRegistrationEvent.Submit.Failure -> { + // TODO Failure + } + } + } + + Box( + modifier = Modifier + .fillMaxSize() + .background(Gray000) + .addFocusCleaner(focusManager) + ) { + Row( + modifier = Modifier + .align(Alignment.TopCenter) + .height(Space56) + .fillMaxWidth() + .padding( + horizontal = 20.dp, + vertical = 13.dp + ), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(R.drawable.ic_chevron_left), + contentDescription = null + ) + Spacer(modifier = Modifier.width(Space4)) + Text( + text = "마음 등록하기", + style = Headline1.merge( + color = Gray800, + fontWeight = FontWeight.SemiBold + ) + ) + } + CompositionLocalProvider(LocalOverscrollConfiguration.provides(null)) { + Column( + modifier = Modifier + .padding(top = Space56) + .fillMaxWidth() + .verticalScroll(state = scrollState) + .padding(horizontal = Space20) + ) { + Spacer(modifier = Modifier.height(Space16)) + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + Box( + modifier = Modifier + .background( + color = Gray200, + shape = RoundedCornerShape(100.dp) + ) + .width(214.dp) + .height(38.dp), + ) { + Box( + modifier = Modifier + .padding(2.dp) + .padding(start = typePositionState.value) + .clip(RoundedCornerShape(100.dp)) + .width(104.dp) + .height(34.dp) + .background(color = Gray000), + ) + HistoryRegistrationType.entries.forEach { registrationType -> + Box( + modifier = Modifier + .align( + when (registrationType) { + HistoryRegistrationType.TAKE -> Alignment.CenterStart + HistoryRegistrationType.GIVE -> Alignment.CenterEnd + } + ) + .padding(2.dp) + .width(104.dp) + .height(34.dp) + .clip(RoundedCornerShape(100.dp)) + .clickable { + historyTypeState = registrationType + } + .background(color = Color.Transparent), + contentAlignment = Alignment.Center + ) { + Text( + text = registrationType.typeName, + style = Body1.merge( + color = getTypeTextColor(registrationType).value, + fontWeight = FontWeight.SemiBold + ) + ) + } + } + } + } + Spacer(modifier = Modifier.height(Space20)) + + TypingPriceField( + modifier = Modifier.fillMaxWidth(), + textValue = priceText, + onValueChange = { + priceText = it + }, + hintText = when (historyTypeState) { + HistoryRegistrationType.GIVE -> "지출하신 금액을 입력해주세요" + HistoryRegistrationType.TAKE -> "받은 금액을 입력해주세요" + } + ) + Spacer(modifier = Modifier.height(Space24)) + + FieldSubject("이름") + Spacer(modifier = Modifier.height(6.dp)) + FieldSelectComponent( + isSelected = isAddNameShowingState, + text = userNameText, + onClick = { + isAddNameShowingState = true + } + ) + Spacer(modifier = Modifier.height(24.dp)) + + FieldSubject("날짜") + Spacer(modifier = Modifier.height(6.dp)) + FieldSelectComponent( + isSelected = isCalendarShowingState, + text = listOf(selectedYear, selectedMonth, selectedDay).joinToString(" / "), + onClick = { + isCalendarShowingState = true + } + ) + Spacer(modifier = Modifier.height(24.dp)) + + FieldSubject("경사 종류") + TypingTextField( + textType = TypingTextFieldType.Basic, + text = eventTypeText, + onValueChange = { + selectedEventId = HistoryRegistrationEventType.getEventId(it) + eventTypeText = it + }, + modifier = Modifier.fillMaxWidth(), + hintText = "직접 입력", + ) + Spacer(modifier = Modifier.height(6.dp)) + HistoryRegistrationEventType.entries.chunked(5).forEach { registrationEventTypes -> + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + registrationEventTypes.forEach { type -> + ChipItem( + chipType = ChipType.BORDER, + chipText = type.eventName, + currentSelectedId = setOf(selectedEventId), + chipId = type.id, + onSelectChip = { selectId -> + selectedEventId = + if (selectedEventId == selectId) -1 else selectId + eventTypeText = + HistoryRegistrationEventType.getEventName(selectId) + }, + ) + } + } + Spacer(modifier = Modifier.height(6.dp)) + } + Spacer(modifier = Modifier.height(18.dp)) + + FieldSubject( + subject = "메모", + isViewIcon = false + ) + Box(modifier = Modifier.clickable { + focusManager.moveFocus(FocusDirection.Down) + }) + TypingTextField( + textType = TypingTextFieldType.LongSentence, + text = memoText, + onValueChange = { + memoText = it + }, + modifier = Modifier.fillMaxWidth(), + hintText = "경사 관련 메모를 입력해주세요", + ) + Spacer(modifier = Modifier.height(24.dp)) + + FieldSubject("태그") + HistoryRegistrationTagType.entries.chunked(5).forEach { registrationTagTypes -> + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + registrationTagTypes.forEach { type -> + ChipItem( + chipType = ChipType.BORDER, + chipText = type.tagName, + currentSelectedId = tagIdList.toSet(), + chipId = type.id, + onSelectChip = { selectId -> + if (tagIdList.contains(selectId)) { + tagIdList.remove(selectId) + } else { + tagIdList.add(selectId) + } + }, + ) + } + } + Spacer(modifier = Modifier.height(6.dp)) + } + Spacer(modifier = Modifier.height(104.dp)) + } + } + Row( + modifier = Modifier + .background(color = Gray000) + .align(Alignment.BottomCenter) + .padding( + vertical = Space12, + horizontal = Space20 + ), + horizontalArrangement = Arrangement.spacedBy(Space12) + ) { + ConfirmButton( + modifier = Modifier.weight(1f), + properties = ConfirmButtonProperties( + size = ConfirmButtonSize.Xlarge, + type = ConfirmButtonType.Secondary + ), + content = { + Text( + text = "연속저장", + style = Headline3.merge( + color = Gray700, + fontWeight = FontWeight.SemiBold + ) + ) + } + ) + ConfirmButton( + modifier = Modifier.weight(1f), + properties = ConfirmButtonProperties( + size = ConfirmButtonSize.Xlarge, + type = ConfirmButtonType.Primary + ), + onClick = { + if ( + checkRegistrable( + relationId = relationId, + money = priceText.toLongOrNull() ?: 0L, + day = listOf( + selectedYear, + selectedMonth, + selectedDay + ).joinToString("-"), + event = eventTypeText + ) + ) { + intent( + HistoryRegistrationIntent.OnClickSubmit( + relationId = relationId, + money = priceText.toLongOrNull() ?: 0L, + give = HistoryRegistrationType.getHistoryRegistration( + historyTypeState + ), + day = listOf( + selectedYear, + selectedMonth, + selectedDay + ).joinToString("-"), + event = eventTypeText, + memo = if (memoText.isEmpty()) null else memoText, + tags = if (tagIdList.isEmpty()) null else HistoryRegistrationTagType.getTagNameList( + tagIdList + ) + ) + ) + } + }, + content = { + Text( + text = "저장하기", + style = Headline3.merge( + color = Gray000, + fontWeight = FontWeight.SemiBold + ) + ) + } + ) + } + + if (isCalendarShowingState) { + HistoryCalendarScreen( + calendarConfig = calendarConfig, + selectedYear = selectedYear, + selectedMonth = selectedMonth, + selectedDay = selectedDay, + onClose = { + isCalendarShowingState = false + }, + onConfirm = { year, month, day -> + selectedYear = year + selectedMonth = month + selectedDay = day + isCalendarShowingState = false + } + ) + } + + if (isAddNameShowingState) { + SearchRelationScreen( + appState = appState, + onDismissRequest = { + isAddNameShowingState = false + }, + onResult = { + userNameText = it.name + relationId = it.id + isAddNameShowingState = false + }, + ) + } + } + + LaunchedEffectWithLifecycle(event, handler) { + event.eventObserve { event -> + when (event) { + is HistoryRegistrationEvent.Submit -> (submit(event)) + } + } + } +} + +private fun checkRegistrable( + relationId: Long, + money: Long, + day: String, + event: String +): Boolean { + return relationId != -1L && money != 0L && day.isNotEmpty() && event.isNotEmpty() +} + +@Preview( + backgroundColor = 0xFFFFFF, + showBackground = true, + apiLevel = 33 +) +@Composable +fun HistoryRegistrationScreenPreview() { + HistoryRegistrationScreen( + appState = rememberApplicationState(), + model = HistoryRegistrationModel( + state = HistoryRegistrationState.Init + ), + event = MutableEventFlow(), + intent = {}, + handler = CoroutineExceptionHandler { _, _ -> } + ) +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationState.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationState.kt new file mode 100644 index 00000000..0f7ae007 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationState.kt @@ -0,0 +1,6 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration + +sealed interface HistoryRegistrationState { + data object Init : HistoryRegistrationState + data object Loading : HistoryRegistrationState +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationViewModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationViewModel.kt new file mode 100644 index 00000000..6d4a726c --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/HistoryRegistrationViewModel.kt @@ -0,0 +1,52 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration + +import ac.dnd.bookkeeping.android.presentation.common.base.BaseViewModel +import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.EventFlow +import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.MutableEventFlow +import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.asEventFlow +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject + +@HiltViewModel +class HistoryRegistrationViewModel @Inject constructor( + private val savedStateHandle: SavedStateHandle, +) : BaseViewModel() { + + private val _state: MutableStateFlow = + MutableStateFlow(HistoryRegistrationState.Init) + val state: StateFlow = _state.asStateFlow() + + private val _event: MutableEventFlow = MutableEventFlow() + val event: EventFlow = _event.asEventFlow() + + fun onIntent(intent: HistoryRegistrationIntent) { + when (intent) { + is HistoryRegistrationIntent.OnClickSubmit -> { + registration( + relationId = intent.relationId, + give = intent.give, + money = intent.money, + day = intent.day, + event = intent.event, + memo = intent.memo, + tags = intent.tags + ) + } + } + } + + private fun registration( + relationId: Long, + give: Boolean, + money: Long, + day: String, + event: String, + memo: String?, + tags: List? + ) { + } +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/calendar/HistoryCalendarScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/calendar/HistoryCalendarScreen.kt new file mode 100644 index 00000000..39d22403 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/calendar/HistoryCalendarScreen.kt @@ -0,0 +1,230 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration.calendar + +import ac.dnd.bookkeeping.android.presentation.R +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray000 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray600 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray700 +import ac.dnd.bookkeeping.android.presentation.common.theme.Gray800 +import ac.dnd.bookkeeping.android.presentation.common.theme.Headline2 +import ac.dnd.bookkeeping.android.presentation.common.theme.Headline3 +import ac.dnd.bookkeeping.android.presentation.common.theme.Space12 +import ac.dnd.bookkeeping.android.presentation.common.theme.Space20 +import ac.dnd.bookkeeping.android.presentation.common.theme.Space8 +import ac.dnd.bookkeeping.android.presentation.common.view.BottomSheetScreen +import ac.dnd.bookkeeping.android.presentation.common.view.calendar.CalendarComponent +import ac.dnd.bookkeeping.android.presentation.common.view.calendar.CalendarConfig +import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButton +import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButtonProperties +import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButtonSize +import ac.dnd.bookkeeping.android.presentation.common.view.confirm.ConfirmButtonType +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +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.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.holix.android.bottomsheetdialog.compose.BottomSheetBehaviorProperties +import com.holix.android.bottomsheetdialog.compose.BottomSheetDialogProperties + +@Composable +fun HistoryCalendarScreen( + calendarConfig: CalendarConfig, + selectedYear: Int, + selectedMonth: Int, + selectedDay: Int, + onClose: () -> Unit, + onConfirm: (year: Int, month: Int, day: Int) -> Unit +) { + var currentSelectYear by remember { mutableIntStateOf(selectedYear) } + var currentSelectMonth by remember { mutableIntStateOf(selectedMonth) } + var currentSelectDay by remember { mutableIntStateOf(selectedDay) } + + BottomSheetScreen( + onDismissRequest = onClose, + properties = BottomSheetDialogProperties( + behaviorProperties = BottomSheetBehaviorProperties( + state = BottomSheetBehaviorProperties.State.Expanded, + skipCollapsed = true + ) + ) + ) { + Column( + modifier = Modifier + .background(Color.Transparent) + .wrapContentHeight() + .padding(horizontal = Space20), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(123.dp) + .padding(vertical = Space20) + ) { + Box( + modifier = Modifier + .padding(Space8) + .align(Alignment.TopEnd) + ) { + Image( + painter = painterResource(R.drawable.ic_close), + contentDescription = null, + modifier = Modifier + .clickable { + onClose() + } + ) + } + Text( + text = "활동 날짜 선택", + style = Headline2.merge( + color = Gray800, + fontWeight = FontWeight.SemiBold + ), + modifier = Modifier + .align(Alignment.BottomStart) + .padding(vertical = Space8) + ) + } + Spacer(modifier = Modifier.height(10.dp)) + Box( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 2.5.dp) + ) { + Image( + painter = painterResource(R.drawable.ic_chevron_left), + colorFilter = ColorFilter.tint(Gray600), + contentDescription = null, + modifier = Modifier + .align(Alignment.CenterStart) + .clickable { + if (currentSelectMonth > 1) { + currentSelectMonth -= 1 + } else { + currentSelectMonth = 12 + currentSelectYear -= 1 + } + } + ) + Row( + modifier = Modifier + .wrapContentWidth() + .align(Alignment.Center), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "${currentSelectMonth}월", + style = Headline3.merge( + color = Gray700, + fontWeight = FontWeight.SemiBold + ) + ) + Spacer(modifier = Modifier.width(6.dp)) + Image( + painter = painterResource(R.drawable.ic_chevron_up), + contentDescription = null, + modifier = Modifier + .size(16.dp) + .rotate(180f) + ) + } + Image( + painter = painterResource(R.drawable.ic_chevron_right), + contentDescription = null, + colorFilter = ColorFilter.tint(Gray600), + modifier = Modifier + .align(Alignment.CenterEnd) + .clickable { + if (currentSelectMonth < 12) { + currentSelectMonth += 1 + } else { + currentSelectMonth = 1 + currentSelectYear += 1 + } + } + ) + } + + CalendarComponent( + modifier = Modifier.padding(vertical = 16.dp), + calendarConfig = calendarConfig, + selectedYear = currentSelectYear, + selectedMonth = currentSelectMonth, + selectedDay = currentSelectDay, + onDaySelect = { + currentSelectDay = it + }, + unClickableDays = setOf() + ) + Box( + modifier = Modifier + .fillMaxWidth() + .padding( + horizontal = Space20, + vertical = Space12 + ) + ) { + ConfirmButton( + modifier = Modifier.fillMaxWidth(), + onClick = { + onConfirm( + currentSelectYear, + currentSelectMonth, + currentSelectDay + ) + + }, + properties = ConfirmButtonProperties( + size = ConfirmButtonSize.Xlarge, + type = ConfirmButtonType.Primary + ) + ) { + Text( + text = "선택 완료", + style = Headline3.merge( + color = Gray000, + fontWeight = FontWeight.SemiBold + ) + ) + } + } + } + } +} + +@Preview(apiLevel = 33) +@Composable +fun HistoryCalendarScreenPreview() { + HistoryCalendarScreen( + calendarConfig = CalendarConfig(), + selectedYear = 2024, + selectedMonth = 1, + selectedDay = 20, + onClose = {}, + onConfirm = { _, _, _ -> } + ) +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/type/HistoryRegistrationEventType.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/type/HistoryRegistrationEventType.kt new file mode 100644 index 00000000..d682aca3 --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/type/HistoryRegistrationEventType.kt @@ -0,0 +1,22 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration.type + +enum class HistoryRegistrationEventType( + val id: Long, + val eventName: String +) { + MARRIAGE(0, "결혼"), + BIRTHDAY(1, "생일"), + CHILDBIRTH(2, "출산"), + FIRST_BIRTHDAY_PARTY(3, "돌잔치"), + OPENING_OF_BUSINESS(4, "개업"); + + companion object { + fun getEventName(id: Long): String { + return entries.find { it.id == id }?.eventName ?: "" + } + + fun getEventId(name: String): Long { + return entries.find { it.eventName == name }?.id ?: -1 + } + } +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/type/HistoryRegistrationTagType.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/type/HistoryRegistrationTagType.kt new file mode 100644 index 00000000..d24cfcbf --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/type/HistoryRegistrationTagType.kt @@ -0,0 +1,24 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration.type + +enum class HistoryRegistrationTagType( + val id: Long, + val tagName: String +) { + ATTEND(0, "참석"), + ACCOUNT(1, "계좌"), + GIFT_IN_RETURN(2, "답례품"), + CASH(3, "현금"), + NONAPPEARANCE(4, "불참"), + GIFT_CARD(5, "상품권"), + WREATH(6, "화환"); + + companion object { + fun getTagNameList(idList: List): List { + return entries.filter { + it.id in idList + }.map { + it.tagName + } + } + } +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/type/HistoryRegistrationType.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/type/HistoryRegistrationType.kt new file mode 100644 index 00000000..6366260e --- /dev/null +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/home/history/registration/type/HistoryRegistrationType.kt @@ -0,0 +1,17 @@ +package ac.dnd.bookkeeping.android.presentation.ui.main.home.history.registration.type + +enum class HistoryRegistrationType( + val typeName: String +) { + TAKE("받은 마음"), + GIVE("보낸 마음"); + + companion object { + fun getHistoryRegistration(type: HistoryRegistrationType): Boolean { + return when (type) { + TAKE -> false + GIVE -> true + } + } + } +} diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/RegistrationNavGraph.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/RegistrationNavGraph.kt index 3e3d9617..c11b196c 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/RegistrationNavGraph.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/RegistrationNavGraph.kt @@ -2,7 +2,7 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.registration import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState import ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.RegistrationMainConstant -import ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.registrationNamingDestination +import ac.dnd.bookkeeping.android.presentation.ui.main.registration.main.registrationMainDestination import androidx.navigation.NavGraphBuilder import androidx.navigation.navigation @@ -13,6 +13,6 @@ fun NavGraphBuilder.registrationNavGraph( startDestination = RegistrationMainConstant.ROUTE, route = RegistrationConstant.ROUTE ) { - registrationNamingDestination(appState) + registrationMainDestination(appState) } } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainDestination.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainDestination.kt index 0efb5594..c637f828 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainDestination.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/registration/main/RegistrationMainDestination.kt @@ -10,7 +10,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable -fun NavGraphBuilder.registrationNamingDestination( +fun NavGraphBuilder.registrationMainDestination( appState: ApplicationState ) { composable(