diff --git a/app/src/main/java/com/erica/gamsung/core/presentation/MainActivity.kt b/app/src/main/java/com/erica/gamsung/core/presentation/MainActivity.kt index da7719f..574759b 100644 --- a/app/src/main/java/com/erica/gamsung/core/presentation/MainActivity.kt +++ b/app/src/main/java/com/erica/gamsung/core/presentation/MainActivity.kt @@ -13,6 +13,7 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import com.erica.gamsung.menu.presentation.InputMenuScreen import com.erica.gamsung.ui.theme.GamsungTheme class MainActivity : ComponentActivity() { @@ -35,11 +36,12 @@ class MainActivity : ComponentActivity() { @Composable fun MainNavHost(navController: NavHostController) { - NavHost(navController = navController, startDestination = "main") { + NavHost(navController = navController, startDestination = Screen.MAIN.route) { composable(Screen.MAIN.route) { MainScreen(navController = navController) } composable(Screen.SETTING.route) { SettingScreen() } composable(Screen.PUBLISH_POSTING.route) { PublishPostingScreen() } composable(Screen.CHECK_POSTING.route) { CheckPostingScreen() } + composable(Screen.INPUT_MENU.route) { InputMenuScreen(navController = navController) } } } diff --git a/app/src/main/java/com/erica/gamsung/core/presentation/Screen.kt b/app/src/main/java/com/erica/gamsung/core/presentation/Screen.kt index f0ec2ef..992b1a4 100644 --- a/app/src/main/java/com/erica/gamsung/core/presentation/Screen.kt +++ b/app/src/main/java/com/erica/gamsung/core/presentation/Screen.kt @@ -7,4 +7,5 @@ enum class Screen( SETTING("setting"), PUBLISH_POSTING("publishPosting"), CHECK_POSTING("checkPosting"), + INPUT_MENU("inputMenu"), } diff --git a/app/src/main/java/com/erica/gamsung/core/presentation/component/GsTextBox.kt b/app/src/main/java/com/erica/gamsung/core/presentation/component/GsTextBox.kt index 974119a..66a1a82 100644 --- a/app/src/main/java/com/erica/gamsung/core/presentation/component/GsTextBox.kt +++ b/app/src/main/java/com/erica/gamsung/core/presentation/component/GsTextBox.kt @@ -10,24 +10,27 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text +import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview @@ -44,11 +47,11 @@ fun GsTextBox( modifier: Modifier, innerTextModifier: Modifier, ) { - val text by remember { mutableStateOf(TextFieldValue(hintText)) } + var text by remember { mutableStateOf(TextFieldValue(hintText)) } var expanded by remember { mutableStateOf(false) } var selectedOption by remember { mutableStateOf("") } Column { - TextTitle(title, isRequired, description) + TextTitle(title, isRequired, description, Modifier) if (options != null) { DropdownTextBox( text, @@ -66,9 +69,11 @@ fun GsTextBox( ) } else { InputTextBox( - text, - innerTextModifier, - modifier, + modifier = innerTextModifier, + hintText = text.text, + onValueChange = { text = TextFieldValue(it) }, + keyboardType = KeyboardType.Text, + isError = false, ) } } @@ -79,6 +84,7 @@ fun TextTitle( title: String, isRequired: Boolean, description: String?, + modifier: Modifier = Modifier, ) { Text( text = @@ -108,6 +114,8 @@ fun TextTitle( description?.let { append(description) } } }, + style = MaterialTheme.typography.titleSmall, + modifier = modifier, ) } @@ -168,51 +176,41 @@ private fun DropdownTextBox( @Composable fun InputTextBox( - hintText: TextFieldValue, - modifier: Modifier, - innerTextModifier: Modifier, + modifier: Modifier = Modifier, + hintText: String, + onValueChange: (String) -> Unit, + keyboardType: KeyboardType = KeyboardType.Text, + isError: Boolean = false, ) { - var text by remember { - mutableStateOf(hintText) - } - var isFocused by remember { - mutableStateOf(false) + var textState by remember { + mutableStateOf("") } - BasicTextField( - value = text, - onValueChange = { updatedText -> - text = updatedText + OutlinedTextField( + value = textState, + onValueChange = { + textState = it + onValueChange(it) }, - singleLine = true, - modifier = - modifier - .background(Color.Transparent) - .border( - 1.dp, - Color.LightGray, - RoundedCornerShape(percent = 15), - ).onFocusChanged { focusState -> - isFocused = focusState.isFocused - if (isFocused && text == hintText) { - text = TextFieldValue("") - } else if (!isFocused && text.text.isEmpty()) { - text = hintText - } - }, - decorationBox = { innerTextField -> - Row( - modifier = innerTextModifier, - verticalAlignment = Alignment.CenterVertically, - ) { - if (!isFocused && text == hintText) { - Text(hintText.text, color = Color.Gray) - Spacer(modifier = Modifier.weight(1f)) - } else { - innerTextField() - Spacer(modifier = Modifier.weight(1f)) - } - } + placeholder = { + Text( + text = hintText, + color = Color.Gray, + style = MaterialTheme.typography.bodyLarge, + ) }, + singleLine = true, + colors = + TextFieldDefaults.colors( + unfocusedContainerColor = Color.Transparent, + unfocusedIndicatorColor = Color.LightGray, + focusedContainerColor = Color.Transparent, + errorContainerColor = Color.Transparent, + ), + shape = RoundedCornerShape(percent = 15), + keyboardOptions = KeyboardOptions(keyboardType = keyboardType), + textStyle = MaterialTheme.typography.bodyLarge, + modifier = modifier, + isError = isError, ) } diff --git a/app/src/main/java/com/erica/gamsung/menu/presentation/InputMenuScreen.kt b/app/src/main/java/com/erica/gamsung/menu/presentation/InputMenuScreen.kt index 44c83cc..de8175c 100644 --- a/app/src/main/java/com/erica/gamsung/menu/presentation/InputMenuScreen.kt +++ b/app/src/main/java/com/erica/gamsung/menu/presentation/InputMenuScreen.kt @@ -1,5 +1,6 @@ package com.erica.gamsung.menu.presentation +import android.util.Log import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -13,7 +14,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AddCircleOutline @@ -24,12 +25,20 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import androidx.navigation.compose.rememberNavController +import com.erica.gamsung.core.presentation.Screen import com.erica.gamsung.core.presentation.component.GsButton import com.erica.gamsung.core.presentation.component.GsTopAppBar import com.erica.gamsung.core.presentation.component.InputTextBox @@ -37,7 +46,7 @@ import com.erica.gamsung.core.presentation.component.TextTitle import com.erica.gamsung.menu.domain.Menu @Composable -fun InputMenuScreen() { +fun InputMenuScreen(navController: NavHostController = rememberNavController()) { Scaffold( topBar = { GsTopAppBar(title = "메뉴 입력 (2/2)") }, ) { paddingValues -> @@ -48,13 +57,17 @@ fun InputMenuScreen() { .padding(paddingValues), horizontalAlignment = Alignment.CenterHorizontally, ) { + val menus = remember { mutableStateListOf() } + val isNameValid = remember { mutableStateOf(true) } + val isPriceValid = remember { mutableStateOf(true) } + Box( modifier = Modifier .fillMaxWidth() .weight(1f), ) { - InputMenuSection() + InputMenuSection(menus, isNameValid, isPriceValid) } Divider() GsButton( @@ -64,25 +77,30 @@ fun InputMenuScreen() { .fillMaxWidth() .height(70.dp) .padding(horizontal = 8.dp, vertical = 12.dp), - onClick = { TODO() }, + onClick = { + // TODO 서버로 메뉴 전송 + Log.d("InputMenuScreen", "서버로 전송할 메뉴 목록\n ${menus.toList().joinToString("\n")}") + + if (menus.isEmpty()) { + isNameValid.value = false + isPriceValid.value = false + } else { + navController.navigate(Screen.MAIN.route) + } + }, ) } } } -@Suppress("MagicNumber") // TODO 기능 구현 시 제거 @Composable -private fun InputMenuSection() { - // TODO 기능 구현 시 제거 - val menus = - listOf( - Menu("비빔밥", 12000), - Menu("떡볶이", 8000), - Menu("김치볶음밥", 9000), - Menu("갈비구이", 18000), - Menu("김치전", 13000), - Menu("해물파전", 15000), - ) +private fun InputMenuSection( + menus: SnapshotStateList, + isNameValid: MutableState, + isPriceValid: MutableState, +) { + val (name, setName) = remember { mutableStateOf("") } + val (price, setPrice) = remember { mutableStateOf("") } LazyColumn( modifier = @@ -93,16 +111,30 @@ private fun InputMenuSection() { horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Top, ) { - items(menus) { menu -> - CompletedMenuItem(menu) + itemsIndexed(menus) { index, menu -> + CompletedMenuItem(menu) { menus.removeAt(index) } } item { - InputMenuItem() + InputMenuItem( + nameChanged = { setName(it) }, + priceChanged = { setPrice(it) }, + isNameValid = isNameValid.value, + isPriceValid = isPriceValid.value, + ) } item { - IconButton(onClick = { /*TODO*/ }) { + IconButton(onClick = { + isNameValid.value = name.isNotBlank() + isPriceValid.value = price.isZeroOrPrimitiveInt() + + if (isNameValid.value && isPriceValid.value) { + menus.add(Menu(name, price.toInt())) + setName("") + setPrice("") + } + }) { Icon( imageVector = Icons.Default.AddCircleOutline, contentDescription = "메뉴 추가 아이콘", @@ -112,8 +144,16 @@ private fun InputMenuSection() { } } +private fun String.isZeroOrPrimitiveInt(): Boolean { + val int = this.toIntOrNull() ?: return false + return int >= 0 +} + @Composable -private fun CompletedMenuItem(menu: Menu) { +private fun CompletedMenuItem( + menu: Menu, + onClick: () -> Unit, +) { Row( modifier = Modifier @@ -133,7 +173,7 @@ private fun CompletedMenuItem(menu: Menu) { modifier = Modifier.weight(1f), ) IconButton( - onClick = { /*TODO*/ }, + onClick = onClick, modifier = Modifier .size(20.dp) @@ -164,38 +204,50 @@ private fun MenuItemContainer( title = title, isRequired = false, description = null, + modifier = Modifier, ) Text(text = content) } } @Composable -private fun InputMenuItem() { +private fun InputMenuItem( + nameChanged: (String) -> Unit, + priceChanged: (String) -> Unit, + isNameValid: Boolean = true, + isPriceValid: Boolean = true, +) { Row( modifier = Modifier .fillMaxWidth() - .height(70.dp) + .height(100.dp) .padding(6.dp) .border(1.dp, Color.Black, RoundedCornerShape(10.dp)), ) { TitleTextField( - title = "메뉴 이름", - hintText = "ex. 고등어 구이 정식", modifier = Modifier .fillMaxHeight() .padding(5.dp) .weight(1f), + title = "메뉴 이름", + hintText = "ex. 고등어 구이 정식", + onValueChange = nameChanged, + keyboardType = KeyboardType.Text, + isValid = isNameValid, ) TitleTextField( - title = "가격", - hintText = "ex. 15000", modifier = Modifier .fillMaxHeight() .padding(5.dp) .weight(1f), + title = "가격", + hintText = "ex. 15000", + onValueChange = priceChanged, + keyboardType = KeyboardType.Number, + isValid = isPriceValid, ) Spacer(modifier = Modifier.padding(10.dp)) } @@ -206,19 +258,24 @@ private fun TitleTextField( modifier: Modifier = Modifier, title: String, hintText: String, + onValueChange: (String) -> Unit, + keyboardType: KeyboardType = KeyboardType.Text, + isValid: Boolean = true, ) { Column( modifier = modifier, - verticalArrangement = Arrangement.SpaceEvenly, ) { - TextTitle(title = title, isRequired = true, description = null) + TextTitle( + title = title, + isRequired = true, + description = null, + ) InputTextBox( - hintText = TextFieldValue(hintText), - modifier = - Modifier - .weight(1f) - .padding(end = 10.dp), - innerTextModifier = Modifier.padding(start = 5.dp), + modifier = Modifier.padding(top = 5.dp, end = 10.dp), + hintText = hintText, + onValueChange = onValueChange, + keyboardType = keyboardType, + isError = !isValid, ) } }