diff --git a/core/src/main/java/com/terning/core/designsystem/component/item/ScrapBox.kt b/core/src/main/java/com/terning/core/designsystem/component/item/ScrapBox.kt index 5e2399b91..e57aa032a 100644 --- a/core/src/main/java/com/terning/core/designsystem/component/item/ScrapBox.kt +++ b/core/src/main/java/com/terning/core/designsystem/component/item/ScrapBox.kt @@ -1,15 +1,12 @@ package com.terning.core.designsystem.component.item -import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box 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.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -22,7 +19,6 @@ import com.terning.core.designsystem.theme.CalPink import com.terning.core.designsystem.theme.CalPurple import com.terning.core.designsystem.theme.Grey150 import com.terning.core.designsystem.theme.TerningPointTheme -import com.terning.core.designsystem.theme.TerningTheme import com.terning.core.designsystem.theme.White /** @@ -53,7 +49,11 @@ fun ScrapBox( .shadow( elevation = elevation, RoundedCornerShape(cornerRadius), - ).background(scrapColor) + ) + .background( + color = scrapColor, + shape = RoundedCornerShape(cornerRadius) + ) ) { Box( modifier = Modifier 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 63c015970..4c03dd063 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 @@ -10,7 +10,6 @@ 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.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape @@ -20,12 +19,15 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable 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.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.terning.core.designsystem.component.bottomsheet.SortingBottomSheet import com.terning.core.designsystem.component.button.SortingButton import com.terning.core.designsystem.component.item.InternItem @@ -37,15 +39,23 @@ 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.home.component.HomeFilteringEmptyIntern 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 + +const val NAME_START_LENGTH = 7 +const val NAME_END_LENGTH = 12 @Composable fun HomeRoute() { val currentSortBy: MutableState = remember { - mutableStateOf(0) + mutableIntStateOf(0) } - HomeScreen(currentSortBy) } @@ -53,8 +63,11 @@ fun HomeRoute() { @Composable fun HomeScreen( currentSortBy: MutableState, - modifier: Modifier = Modifier, + viewModel: HomeViewModel = hiltViewModel(), ) { + val userNameState = viewModel.userName + val userScrapState by viewModel.scrapData.collectAsStateWithLifecycle() + val recommendInternData by viewModel.recommendInternData.collectAsStateWithLifecycle() var sheetState by remember { mutableStateOf(false) } if (sheetState) { @@ -73,110 +86,170 @@ fun HomeScreen( LogoTopAppBar() } ) { paddingValues -> - LazyColumn( + Column( modifier = Modifier .fillMaxSize() .padding(top = paddingValues.calculateTopPadding()), - contentPadding = PaddingValues(2.dp), - verticalArrangement = Arrangement.spacedBy(12.dp) ) { - item { - Column( - modifier = Modifier - .padding(bottom = 16.dp) - ) { - Text( - text = stringResource( - id = R.string.home_today_title, "남지우" - ), + LazyColumn( + contentPadding = PaddingValues( + top = 2.dp, + bottom = 20.dp, + ), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + item { + Column( modifier = Modifier - .padding(top = 11.dp, bottom = 19.dp) - .padding(horizontal = 24.dp), - style = TerningTheme.typography.title1, - color = Black, - ) - HomeTodayIntern() + .padding(bottom = 16.dp) + ) { + ShowMainTitleWithName(userNameState) + ShowTodayIntern(userScrapState = userScrapState) + } } - } - stickyHeader { - Column( - modifier = Modifier - .background(White) - ) { - Text( - text = stringResource(id = R.string.home_recommend_sub_title), - style = TerningTheme.typography.detail2, - color = Black, - modifier = Modifier - .padding(top = 9.dp) - .padding(horizontal = 24.dp), - ) - - Text( - text = stringResource(id = R.string.home_recommend_main_title), - style = TerningTheme.typography.title1, - color = Black, - modifier = Modifier - .padding(top = 5.dp) - .padding(horizontal = 24.dp), - ) - - HomeFilteringScreen( - grade = 3, - period = 1, - startYear = 2024, - startMonth = 7, - ) - - HorizontalDivider( - thickness = 4.dp, - color = Grey150, - modifier = Modifier - .fillMaxWidth(), - ) - - Row( + stickyHeader { + Column( modifier = Modifier - .fillMaxWidth(), - horizontalArrangement = Arrangement.End, + .background(White) ) { - SortingButton( - sortBy = currentSortBy.value, - onCLick = { sheetState = true }, + ShowRecommendTitle() + ShowInternFilter(userNameState = userNameState) + + HorizontalDivider( + thickness = 4.dp, + color = Grey150, modifier = Modifier - .padding(vertical = 4.dp) + .fillMaxWidth(), ) - Spacer(modifier = Modifier.padding(9.dp)) + + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.End, + ) { + SortingButton( + sortBy = currentSortBy.value, + onCLick = { sheetState = true }, + modifier = Modifier + .padding(vertical = 4.dp) + ) + Spacer(modifier = Modifier.padding(9.dp)) + } + } + } + + if (userNameState.internFilter != null && recommendInternData.isNotEmpty()) { + items(recommendInternData.size) { index -> + ShowRecommendIntern(recommendInternData[index]) } } } - items(itemCount) { - Box( - modifier = modifier - .height(92.dp) + if (userNameState.internFilter == null) { + HomeFilteringEmptyIntern( + modifier = Modifier .padding(horizontal = 24.dp) - .customShadow( - color = Grey200, - shadowRadius = 10.dp, - shadowWidth = 2.dp - ) - .background( - color = White, - shape = RoundedCornerShape(10.dp) - ) - ) { - InternItem( - imageUrl = "https://reqres.in/img/faces/7-image.jpg", - title = "[Someone] 콘텐츠 마케터 대학생 인턴 채용", - dateDeadline = "2", - workingPeriod = "2", - isScraped = false, - ) - } + .fillMaxSize() + ) + } else if (recommendInternData.isEmpty()) { + HomeRecommendEmptyIntern() } } } } -private const val itemCount = 10 \ No newline at end of file +@Composable +private fun ShowMainTitleWithName(userNameState: UserNameState) { + Text( + text = stringResource( + id = R.string.home_today_title, + if (userNameState.userName.length in NAME_START_LENGTH..NAME_END_LENGTH) "\n" + userNameState.userName + else userNameState.userName + ), + modifier = Modifier + .padding(top = 11.dp, bottom = 19.dp) + .padding(horizontal = 24.dp), + style = TerningTheme.typography.title1, + color = Black, + ) +} + +@Composable +private fun ShowTodayIntern(userScrapState: UserScrapState) { + if (userScrapState.isScrapExist) { + if (userScrapState.scrapData == null) { + HomeTodayEmptyIntern(isButtonExist = true) + } else { + HomeTodayIntern(userScrapState.scrapData) + } + } else { + HomeTodayEmptyIntern(isButtonExist = false) + } +} + +@Composable +private fun ShowRecommendTitle() { + Text( + text = stringResource(id = R.string.home_recommend_sub_title), + style = TerningTheme.typography.detail2, + color = Black, + modifier = Modifier + .padding(top = 9.dp) + .padding(horizontal = 24.dp), + ) + + Text( + text = stringResource(id = R.string.home_recommend_main_title), + style = TerningTheme.typography.title1, + color = Black, + modifier = Modifier + .padding(top = 5.dp) + .padding(horizontal = 24.dp), + ) +} + +@Composable +private fun ShowInternFilter(userNameState: UserNameState) { + if (userNameState.internFilter == null) { + HomeFilteringScreen( + grade = R.string.home_recommend_no_filtering_hyphen, + period = R.string.home_recommend_no_filtering_hyphen, + startYear = R.string.home_recommend_no_filtering_hyphen, + startMonth = R.string.home_recommend_no_filtering_hyphen + ) + } else { + with(userNameState.internFilter) { + HomeFilteringScreen( + grade = grade, + period = workingPeriod, + startYear = startYear, + startMonth = startMonth, + ) + } + } +} + +@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 new file mode 100644 index 000000000..68fd0d2f1 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/home/home/HomeViewModel.kt @@ -0,0 +1,177 @@ +package com.terning.feature.home.home + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import com.terning.core.designsystem.theme.CalBlue1 +import com.terning.core.designsystem.theme.CalBlue2 +import com.terning.core.designsystem.theme.CalGreen1 +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.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 +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject + +@HiltViewModel +class HomeViewModel @Inject constructor( + +) : ViewModel() { + private val _userName by mutableStateOf( + UserNameState( + userName = "남지우자랑스러운티엘이되", + internFilter = InternFilterData( + grade = 4, + workingPeriod = 1, + startYear = 2024, + startMonth = 7, + ) + ) + ) + val userName get() = _userName + + private val _scrapState = MutableStateFlow( + UserScrapState( + isScrapExist = true, + scrapData = getScrapData() + ) + ) + val scrapData get() = _scrapState.asStateFlow() + + private val _recommendInternState = MutableStateFlow>( +// getRecommendData() + listOf() + ) + val recommendInternData get() = _recommendInternState.asStateFlow() +} + +private fun getScrapData(): List = listOf( + ScrapData( + internTitle = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + scrapColor = CalBlue1, + ), + ScrapData( + internTitle = "[유한킴벌리] 그린캠프 숲 활동가", + scrapColor = CalPink, + ), + ScrapData( + internTitle = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + scrapColor = CalYellow, + ), + ScrapData( + internTitle = "[유한킴벌리] 그린캠프 숲 활동가", + scrapColor = CalBlue2, + ), + ScrapData( + internTitle = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + scrapColor = CalGreen1, + ), + ScrapData( + internTitle = "[유한킴벌리] 그린캠프 숲 활동가", + scrapColor = CalOrange1, + ), + ScrapData( + internTitle = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + scrapColor = CalGreen2, + ), + ScrapData( + internTitle = "[유한킴벌리] 그린캠프 숲 활동가", + scrapColor = CalPink, + ), + ScrapData( + internTitle = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + scrapColor = CalBlue1, + ), + ScrapData( + internTitle = "[유한킴벌리] 그린캠프 숲 활동가", + scrapColor = CalPink, + ), + ScrapData( + internTitle = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + scrapColor = CalBlue1, + ), + ScrapData( + internTitle = "[유한킴벌리] 그린캠프 숲 활동가", + scrapColor = CalPink, + ), +) + +private fun getRecommendData(): List = listOf( + RecommendInternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 22, + workingPeriod = 2, + isScrapped = true, + ), + RecommendInternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "ㅇㄻㅇㅁㄻㄹㅇㅁㅇㄹ", + dDay = 9, + workingPeriod = 6, + isScrapped = false, + ), + RecommendInternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = true, + ), + RecommendInternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = false, + ), + RecommendInternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = true, + ), + RecommendInternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = true, + ), + RecommendInternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = false, + ), + RecommendInternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = true, + ), + RecommendInternData( + imgUrl = "https://reqres.in/img/faces/7-image.jpg", + title = "[유한킴벌리] 그린캠프 w.대학생 숲 활동가", + dDay = 2, + workingPeriod = 4, + isScrapped = false, + ), + RecommendInternData( + 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/home/home/component/HomeFilteringEmptyIntern.kt b/feature/src/main/java/com/terning/feature/home/home/component/HomeFilteringEmptyIntern.kt new file mode 100644 index 000000000..896715b29 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/home/home/component/HomeFilteringEmptyIntern.kt @@ -0,0 +1,43 @@ +package com.terning.feature.home.home.component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.terning.feature.R + +@Composable +fun HomeFilteringEmptyIntern( + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.Bottom + ) { + Box( + modifier = Modifier + .padding( + top = 16.dp, + bottom = 12.dp + ) + ) + Text( + text = stringResource(id = R.string.home_recommend_no_filtering), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 25.dp) + .align(Alignment.CenterHorizontally), + textAlign = TextAlign.Center, + ) + } +} \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/home/home/component/HomeTodayIntern.kt b/feature/src/main/java/com/terning/feature/home/home/component/HomeTodayIntern.kt index 35ad92071..e584b416b 100644 --- a/feature/src/main/java/com/terning/feature/home/home/component/HomeTodayIntern.kt +++ b/feature/src/main/java/com/terning/feature/home/home/component/HomeTodayIntern.kt @@ -7,20 +7,21 @@ import androidx.compose.foundation.lazy.LazyRow import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.terning.core.designsystem.theme.CalYellow +import com.terning.feature.home.home.model.ScrapData @Composable -fun HomeTodayIntern() { +fun HomeTodayIntern(internList: List) { LazyRow( horizontalArrangement = Arrangement.spacedBy(12.dp), contentPadding = PaddingValues(horizontal = 24.dp), modifier = Modifier .fillMaxWidth(), ) { - items(todayInternItemCount) { - HomeTodayInternItem("[유한킴벌리] 그린캠프 w.대학생 숲활동가 모집", CalYellow) + items(internList.size) { index -> + HomeTodayInternItem( + title = internList[index].internTitle, + scrapColor = internList[index].scrapColor, + ) } } } - -private const val todayInternItemCount = 5 \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/home/home/model/InternFilterData.kt b/feature/src/main/java/com/terning/feature/home/home/model/InternFilterData.kt new file mode 100644 index 000000000..b819d24fb --- /dev/null +++ b/feature/src/main/java/com/terning/feature/home/home/model/InternFilterData.kt @@ -0,0 +1,8 @@ +package com.terning.feature.home.home.model + +data class InternFilterData( + val grade: Int, + val workingPeriod: Int, + val startYear: Int, + val startMonth: Int, +) 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/RecommendInternData.kt new file mode 100644 index 000000000..5b84c180a --- /dev/null +++ b/feature/src/main/java/com/terning/feature/home/home/model/RecommendInternData.kt @@ -0,0 +1,9 @@ +package com.terning.feature.home.home.model + +data class RecommendInternData( + val imgUrl: String, + val title: String, + val dDay: Int, + val workingPeriod: Int, + val isScrapped: Boolean, +) diff --git a/feature/src/main/java/com/terning/feature/home/home/model/ScrapData.kt b/feature/src/main/java/com/terning/feature/home/home/model/ScrapData.kt new file mode 100644 index 000000000..21eee5bc4 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/home/home/model/ScrapData.kt @@ -0,0 +1,8 @@ +package com.terning.feature.home.home.model + +import androidx.compose.ui.graphics.Color + +data class ScrapData ( + val internTitle: String, + val scrapColor: Color, +) \ No newline at end of file diff --git a/feature/src/main/java/com/terning/feature/home/home/model/UserNameState.kt b/feature/src/main/java/com/terning/feature/home/home/model/UserNameState.kt new file mode 100644 index 000000000..1c93418ff --- /dev/null +++ b/feature/src/main/java/com/terning/feature/home/home/model/UserNameState.kt @@ -0,0 +1,6 @@ +package com.terning.feature.home.home.model + +data class UserNameState( + val userName: String, + val internFilter: InternFilterData?, +) diff --git a/feature/src/main/java/com/terning/feature/home/home/model/UserScrapState.kt b/feature/src/main/java/com/terning/feature/home/home/model/UserScrapState.kt new file mode 100644 index 000000000..9ba852dd1 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/home/home/model/UserScrapState.kt @@ -0,0 +1,6 @@ +package com.terning.feature.home.home.model + +data class UserScrapState( + val isScrapExist: Boolean, + val scrapData: List?, +) diff --git a/feature/src/main/res/values/strings.xml b/feature/src/main/res/values/strings.xml index 38941fb57..ab9e0fc09 100644 --- a/feature/src/main/res/values/strings.xml +++ b/feature/src/main/res/values/strings.xml @@ -80,6 +80,10 @@ 필터링 설정에 일치하는 인턴 공고가 없어요!\n딱 맞는 인턴 공고가 올라오면 바로 알려드릴게요 인턴 공고가 없어요! + + 지금 공고 필터링을 설정하고\n내 계획에 딱 맞는 대학생 인턴 공고를 추천받아보세요! + + - 재학 상태를 선택해주세요