diff --git a/core/src/main/java/com/terning/core/designsystem/component/item/InternItemWithShadow.kt b/core/src/main/java/com/terning/core/designsystem/component/item/InternItemWithShadow.kt new file mode 100644 index 000000000..f16382734 --- /dev/null +++ b/core/src/main/java/com/terning/core/designsystem/component/item/InternItemWithShadow.kt @@ -0,0 +1,41 @@ +package com.terning.core.designsystem.component.item + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.terning.core.designsystem.theme.Grey200 +import com.terning.core.designsystem.theme.White +import com.terning.core.extension.customShadow + +@Composable +fun InternItemWithShadow( + imageUrl: String, + title: String, + dateDeadline: String, + workingPeriod: String, + isScraped: Boolean, +) { + Box( + modifier = Modifier + .customShadow( + color = Grey200, + shadowRadius = 10.dp, + shadowWidth = 2.dp, + ) + .background( + color = White, + shape = RoundedCornerShape(10.dp) + ) + ) { + InternItem( + imageUrl = imageUrl, + title = title, + dateDeadline = dateDeadline, + workingPeriod = workingPeriod, + isScraped = isScraped + ) + } +} \ No newline at end of file diff --git a/core/src/main/java/com/terning/core/designsystem/component/textfield/TerningBasicTextField.kt b/core/src/main/java/com/terning/core/designsystem/component/textfield/TerningBasicTextField.kt index e73c1a3d5..5fcea5394 100644 --- a/core/src/main/java/com/terning/core/designsystem/component/textfield/TerningBasicTextField.kt +++ b/core/src/main/java/com/terning/core/designsystem/component/textfield/TerningBasicTextField.kt @@ -136,7 +136,6 @@ fun TerningBasicTextField( Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp), - modifier = Modifier.padding(vertical = 8.dp) ) { helperIcon?.let { Icon( diff --git a/feature/src/main/java/com/terning/feature/home/home/HomeRoute.kt b/feature/src/main/java/com/terning/feature/home/home/HomeRoute.kt index e35492375..6d3559742 100644 --- a/feature/src/main/java/com/terning/feature/home/home/HomeRoute.kt +++ b/feature/src/main/java/com/terning/feature/home/home/HomeRoute.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -31,14 +30,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController import com.terning.core.designsystem.component.bottomsheet.SortingBottomSheet import com.terning.core.designsystem.component.button.SortingButton -import com.terning.core.designsystem.component.item.InternItem +import com.terning.core.designsystem.component.item.InternItemWithShadow import com.terning.core.designsystem.component.topappbar.LogoTopAppBar import com.terning.core.designsystem.theme.Black import com.terning.core.designsystem.theme.Grey150 -import com.terning.core.designsystem.theme.Grey200 import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.designsystem.theme.White -import com.terning.core.extension.customShadow import com.terning.feature.R import com.terning.feature.home.changefilter.navigation.navigateChangeFilter import com.terning.feature.home.home.component.HomeFilteringEmptyIntern @@ -46,7 +43,6 @@ import com.terning.feature.home.home.component.HomeFilteringScreen import com.terning.feature.home.home.component.HomeRecommendEmptyIntern import com.terning.feature.home.home.component.HomeTodayEmptyIntern import com.terning.feature.home.home.component.HomeTodayIntern -import com.terning.feature.home.home.model.RecommendInternData import com.terning.feature.home.home.model.UserNameState import com.terning.feature.home.home.model.UserScrapState @@ -148,7 +144,17 @@ fun HomeScreen( if (userNameState.internFilter != null && recommendInternData.isNotEmpty()) { items(recommendInternData.size) { index -> - ShowRecommendIntern(recommendInternData[index]) + Box( + modifier = Modifier.padding(horizontal = 24.dp) + ) { + InternItemWithShadow( + imageUrl = recommendInternData[index].imgUrl, + title = recommendInternData[index].title, + dateDeadline = recommendInternData[index].dDay.toString(), + workingPeriod = recommendInternData[index].workingPeriod.toString(), + isScraped = recommendInternData[index].isScrapped, + ) + } } } } @@ -243,29 +249,4 @@ private fun ShowInternFilter(userNameState: UserNameState, onChangeFilterClick: ) } } -} - -@Composable -private fun ShowRecommendIntern(recommendInternData: RecommendInternData) { - Box( - modifier = Modifier - .padding(horizontal = 24.dp) - .customShadow( - color = Grey200, - shadowRadius = 10.dp, - shadowWidth = 2.dp - ) - .background( - color = White, - shape = RoundedCornerShape(10.dp) - ) - ) { - InternItem( - imageUrl = recommendInternData.imgUrl, - title = recommendInternData.title, - dateDeadline = recommendInternData.dDay.toString(), - workingPeriod = recommendInternData.workingPeriod.toString(), - isScraped = recommendInternData.isScrapped, - ) - } } \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/home/home/HomeViewModel.kt b/feature/src/main/java/com/terning/feature/home/home/HomeViewModel.kt index 00dbb12f9..94e29b4f7 100644 --- a/feature/src/main/java/com/terning/feature/home/home/HomeViewModel.kt +++ b/feature/src/main/java/com/terning/feature/home/home/HomeViewModel.kt @@ -10,8 +10,8 @@ import com.terning.core.designsystem.theme.CalGreen2 import com.terning.core.designsystem.theme.CalOrange1 import com.terning.core.designsystem.theme.CalPink import com.terning.core.designsystem.theme.CalYellow +import com.terning.feature.home.home.model.InternData import com.terning.feature.home.home.model.InternFilterData -import com.terning.feature.home.home.model.RecommendInternData import com.terning.feature.home.home.model.ScrapData import com.terning.feature.home.home.model.UserNameState import com.terning.feature.home.home.model.UserScrapState @@ -24,7 +24,7 @@ import javax.inject.Inject class HomeViewModel @Inject constructor( ) : ViewModel() { - private val _userName by mutableStateOf( + private val _userName by mutableStateOf( UserNameState( userName = "남지우자랑스러운티엘이되", internFilter = InternFilterData( @@ -37,7 +37,7 @@ class HomeViewModel @Inject constructor( ) val userName get() = _userName - private val _scrapState = MutableStateFlow( + private val _scrapState = MutableStateFlow( UserScrapState( isScrapExist = true, scrapData = getScrapData() @@ -45,7 +45,7 @@ class HomeViewModel @Inject constructor( ) val scrapData get() = _scrapState.asStateFlow() - private val _recommendInternState = MutableStateFlow>( + private val _recommendInternState = MutableStateFlow( getRecommendData() ) val recommendInternData get() = _recommendInternState.asStateFlow() @@ -57,7 +57,6 @@ class HomeViewModel @Inject constructor( fun setWorkingPeriod(workingPeriod: Int) { userName.internFilter?.workingPeriod = workingPeriod } - } private fun getScrapData(): List = listOf( @@ -111,71 +110,71 @@ private fun getScrapData(): List = listOf( ), ) -private fun getRecommendData(): List = listOf( - RecommendInternData( +private fun getRecommendData(): List = listOf( + InternData( imgUrl = "https://reqres.in/img/faces/7-image.jpg", title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", dDay = 22, workingPeriod = 2, isScrapped = true, ), - RecommendInternData( + InternData( imgUrl = "https://reqres.in/img/faces/7-image.jpg", title = "ㅇㄻㅇㅁㄻㄹㅇㅁㅇㄹ", dDay = 9, workingPeriod = 6, isScrapped = false, ), - RecommendInternData( + InternData( imgUrl = "https://reqres.in/img/faces/7-image.jpg", title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", dDay = 2, workingPeriod = 4, isScrapped = true, ), - RecommendInternData( + InternData( imgUrl = "https://reqres.in/img/faces/7-image.jpg", title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", dDay = 2, workingPeriod = 4, isScrapped = false, ), - RecommendInternData( + InternData( imgUrl = "https://reqres.in/img/faces/7-image.jpg", title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", dDay = 2, workingPeriod = 4, isScrapped = true, ), - RecommendInternData( + InternData( imgUrl = "https://reqres.in/img/faces/7-image.jpg", title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", dDay = 2, workingPeriod = 4, isScrapped = true, ), - RecommendInternData( + InternData( imgUrl = "https://reqres.in/img/faces/7-image.jpg", title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", dDay = 2, workingPeriod = 4, isScrapped = false, ), - RecommendInternData( + InternData( imgUrl = "https://reqres.in/img/faces/7-image.jpg", title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", dDay = 2, workingPeriod = 4, isScrapped = true, ), - RecommendInternData( + InternData( imgUrl = "https://reqres.in/img/faces/7-image.jpg", title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", dDay = 2, workingPeriod = 4, isScrapped = false, ), - RecommendInternData( + InternData( imgUrl = "https://reqres.in/img/faces/7-image.jpg", title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", dDay = 2, diff --git a/feature/src/main/java/com/terning/feature/home/home/model/RecommendInternData.kt b/feature/src/main/java/com/terning/feature/home/home/model/InternData.kt similarity index 84% rename from feature/src/main/java/com/terning/feature/home/home/model/RecommendInternData.kt rename to feature/src/main/java/com/terning/feature/home/home/model/InternData.kt index 5b84c180a..4beafb192 100644 --- a/feature/src/main/java/com/terning/feature/home/home/model/RecommendInternData.kt +++ b/feature/src/main/java/com/terning/feature/home/home/model/InternData.kt @@ -1,6 +1,6 @@ package com.terning.feature.home.home.model -data class RecommendInternData( +data class InternData( val imgUrl: String, val title: String, val dDay: Int, diff --git a/feature/src/main/java/com/terning/feature/intern/component/InternCompanyInfo.kt b/feature/src/main/java/com/terning/feature/intern/component/InternCompanyInfo.kt index ed50ea608..4c5974242 100644 --- a/feature/src/main/java/com/terning/feature/intern/component/InternCompanyInfo.kt +++ b/feature/src/main/java/com/terning/feature/intern/component/InternCompanyInfo.kt @@ -54,7 +54,7 @@ fun InternCompanyInfo(modifier: Modifier) { ) { Image( painter = painterResource( - id = R.drawable.ic_nosearch + id = R.drawable.ic_empty_logo ), modifier = modifier.fillMaxWidth(), contentDescription = null, diff --git a/feature/src/main/java/com/terning/feature/main/MainNavigator.kt b/feature/src/main/java/com/terning/feature/main/MainNavigator.kt index f74336ff4..361fb6103 100644 --- a/feature/src/main/java/com/terning/feature/main/MainNavigator.kt +++ b/feature/src/main/java/com/terning/feature/main/MainNavigator.kt @@ -24,7 +24,7 @@ class MainNavigator( @Composable get() = navController .currentBackStackEntryAsState().value?.destination - val startDestination = SignIn + val startDestination = Search val currentTab: MainTab? @Composable get() = MainTab.find { tab -> diff --git a/feature/src/main/java/com/terning/feature/search/search/SearchRoute.kt b/feature/src/main/java/com/terning/feature/search/search/SearchRoute.kt index 67e792d95..17becd309 100644 --- a/feature/src/main/java/com/terning/feature/search/search/SearchRoute.kt +++ b/feature/src/main/java/com/terning/feature/search/search/SearchRoute.kt @@ -100,5 +100,4 @@ fun SearchScreen( SearchInternList(type = InternListType.SCRAP) } } - } diff --git a/feature/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt b/feature/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt index 4bcedab18..26d84657b 100644 --- a/feature/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt +++ b/feature/src/main/java/com/terning/feature/search/searchprocess/SearchProcessRoute.kt @@ -3,17 +3,24 @@ package com.terning.feature.search.searchprocess import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues 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.padding import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +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.FocusRequester @@ -28,6 +35,9 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController +import com.terning.core.designsystem.component.bottomsheet.SortingBottomSheet +import com.terning.core.designsystem.component.button.SortingButton +import com.terning.core.designsystem.component.item.InternItemWithShadow import com.terning.core.designsystem.component.textfield.SearchTextField import com.terning.core.designsystem.component.topappbar.BackButtonTopAppBar import com.terning.core.designsystem.theme.Grey400 @@ -43,18 +53,35 @@ private const val MAX_LINES = 1 fun SearchProcessRoute( navController: NavHostController, ) { + val currentSortBy: MutableState = remember { + mutableIntStateOf(0) + } SearchProcessScreen( navController = navController, + currentSortBy = currentSortBy ) } @Composable fun SearchProcessScreen( + currentSortBy: MutableState, modifier: Modifier = Modifier, navController: NavHostController, viewModel: SearchProcessViewModel = hiltViewModel(), ) { val state by viewModel.state.collectAsStateWithLifecycle() + var sheetState by remember { mutableStateOf(false) } + val internSearchResultData by viewModel.internSearchResultData.collectAsStateWithLifecycle() + + if (sheetState) { + SortingBottomSheet( + onDismiss = { + sheetState = false + }, + currentSortBy = currentSortBy.value, + newSortBy = currentSortBy + ) + } val focusRequester = remember { FocusRequester() } val focusManager = LocalFocusManager.current @@ -109,57 +136,90 @@ fun SearchProcessScreen( onDoneAction = { viewModel.updateQuery(state.text) viewModel.updateShowSearchResults(true) + viewModel.updateExistSearchResults(state.text) } ) if (state.showSearchResults) { Column( modifier = Modifier - .fillMaxWidth() - .padding(top = 87.dp), + .fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { - Image( - painter = painterResource( - id = R.drawable.ic_nosearch - ), - contentDescription = stringResource( - id = R.string.search_process_no_result_icon + if (state.existSearchResults) { + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.End, + ) { + SortingButton( + sortBy = currentSortBy.value, + onCLick = { sheetState = true }, + ) + } + LazyColumn( + contentPadding = PaddingValues( + top = 12.dp, + bottom = 20.dp, + ), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + items(internSearchResultData.size) { index -> + InternItemWithShadow( + imageUrl = internSearchResultData[index].imgUrl, + title = internSearchResultData[index].title, + dateDeadline = internSearchResultData[index].dDay.toString(), + workingPeriod = internSearchResultData[index].workingPeriod.toString(), + isScraped = internSearchResultData[index].isScrapped + ) + } + } + } else { + Spacer( + modifier = Modifier.padding(top = 87.dp) ) - ) - Row( - modifier = Modifier - .padding( - top = 16.dp, - bottom = 6.dp + Image( + painter = painterResource( + id = R.drawable.ic_empty_logo + ), + contentDescription = stringResource( + id = R.string.search_process_no_result_icon ) - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - Text( - text = state.query, - style = TerningTheme.typography.body1, - color = TerningMain, - maxLines = MAX_LINES, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.weight(1f, false) ) + Row( + modifier = Modifier + .padding( + top = 16.dp, + bottom = 6.dp + ) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Text( + text = state.query, + style = TerningTheme.typography.body1, + color = TerningMain, + maxLines = MAX_LINES, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f, false) + ) + Text( + text = stringResource(id = R.string.search_process_no_result_text_sub), + style = TerningTheme.typography.body1, + color = Grey400, + modifier = Modifier.wrapContentWidth() + ) + } Text( - text = stringResource(id = R.string.search_process_no_result_text_sub), + text = stringResource( + id = R.string.search_process_no_result_text_main + ), style = TerningTheme.typography.body1, color = Grey400, - modifier = Modifier.wrapContentWidth() ) } - Text( - text = stringResource( - id = R.string.search_process_no_result_text_main - ), - style = TerningTheme.typography.body1, - color = Grey400, - ) } } } @@ -171,7 +231,10 @@ fun SearchProcessScreen( fun SearchProcessScreenPreview() { TerningPointTheme { SearchProcessScreen( - navController = rememberNavController() + navController = rememberNavController(), + currentSortBy = remember { + mutableIntStateOf(0) + } ) } } diff --git a/feature/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt b/feature/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt index a88196b52..589f3106a 100644 --- a/feature/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt +++ b/feature/src/main/java/com/terning/feature/search/searchprocess/SearchProcessViewModel.kt @@ -1,10 +1,12 @@ package com.terning.feature.search.searchprocess import androidx.lifecycle.ViewModel +import com.terning.feature.home.home.model.InternData import com.terning.feature.search.searchprocess.models.SearchProcessState 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 @@ -14,6 +16,11 @@ class SearchProcessViewModel @Inject constructor() : ViewModel() { val state: StateFlow get() = _state + private val _internSearchResultState = MutableStateFlow( + getRecommendData() + ) + val internSearchResultData get() = _internSearchResultState.asStateFlow() + fun updateText(newText: String) { _state.value = _state.value.copy(text = newText) } @@ -24,5 +31,86 @@ class SearchProcessViewModel @Inject constructor() : ViewModel() { fun updateShowSearchResults(show: Boolean) { _state.value = _state.value.copy(showSearchResults = show) + _state.value = _state.value.copy(existSearchResults = true) + } + + + fun updateExistSearchResults(query: String) { + val exist = + _internSearchResultState.value.any { it.title.contains(query, ignoreCase = true) } + _state.value = _state.value.copy(existSearchResults = exist) } } + +private fun getRecommendData(): List = listOf( + InternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 22, + workingPeriod = 2, + isScrapped = true, + ), + InternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "ㅇㄻㅇㅁㄻㄹㅇㅁㅇㄹ", + dDay = 9, + workingPeriod = 6, + isScrapped = false, + ), + InternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = true, + ), + InternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = false, + ), + InternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = true, + ), + InternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = true, + ), + InternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = false, + ), + InternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = true, + ), + InternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = false, + ), + InternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = true, + ), +) \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/search/searchprocess/models/SearchProcessState.kt b/feature/src/main/java/com/terning/feature/search/searchprocess/models/SearchProcessState.kt index 2f55b7287..f00b0f1ca 100644 --- a/feature/src/main/java/com/terning/feature/search/searchprocess/models/SearchProcessState.kt +++ b/feature/src/main/java/com/terning/feature/search/searchprocess/models/SearchProcessState.kt @@ -1,8 +1,12 @@ package com.terning.feature.search.searchprocess.models +import com.terning.feature.home.home.model.InternData + data class SearchProcessState( val text: String = "", val query: String = "", val showSearchResults: Boolean = false, + val existSearchResults: Boolean = false, + val searchResults: List = emptyList(), ) \ No newline at end of file diff --git a/feature/src/main/res/drawable/ic_nosearch.xml b/feature/src/main/res/drawable/ic_nosearch.xml deleted file mode 100644 index c63bd8fb7..000000000 --- a/feature/src/main/res/drawable/ic_nosearch.xml +++ /dev/null @@ -1,10 +0,0 @@ - - -