From 7ee227fc3fd27ab1b13450ed42eb1cd3d3eaf910 Mon Sep 17 00:00:00 2001 From: Andrew Cheung Date: Sun, 16 Nov 2025 03:03:56 -0500 Subject: [PATCH 1/3] Implement home and search screen ui base --- .../ui/components/home/CategoryButtonRow.kt | 52 ++++++ .../hustle/ui/components/home/MainContent.kt | 130 +++++++++++++++ .../ui/components/home/SearchContent.kt | 155 +++++++++++++++++ .../hustle/ui/components/home/SearchHeader.kt | 143 ++++++++++++++++ .../hustle/ui/navigation/Routes.kt | 22 +++ .../ui/navigation/navgraphs/HomeNavigation.kt | 11 +- .../hustle/ui/screens/home/HomeScreen.kt | 156 +++++++++++++++++- 7 files changed, 665 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/cornellappdev/hustle/ui/components/home/CategoryButtonRow.kt create mode 100644 app/src/main/java/com/cornellappdev/hustle/ui/components/home/MainContent.kt create mode 100644 app/src/main/java/com/cornellappdev/hustle/ui/components/home/SearchContent.kt create mode 100644 app/src/main/java/com/cornellappdev/hustle/ui/components/home/SearchHeader.kt diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/components/home/CategoryButtonRow.kt b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/CategoryButtonRow.kt new file mode 100644 index 0000000..4b856a8 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/CategoryButtonRow.kt @@ -0,0 +1,52 @@ +package com.cornellappdev.hustle.ui.components.home + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import com.cornellappdev.hustle.ui.components.general.HustleButton +import com.cornellappdev.hustle.ui.navigation.CategoryType +import com.cornellappdev.hustle.ui.theme.HustleSpacing +import com.cornellappdev.hustle.util.constants.SERVICE_CATEGORIES + +@Composable +fun CategoryButtonRow( + onCategoryClick: (CategoryType) -> Unit, + modifier: Modifier = Modifier, + contentPadding: PaddingValues = PaddingValues(horizontal = HustleSpacing.large) +) { + val lazyListState = rememberLazyListState() + LazyRow( + state = lazyListState, + modifier = modifier, + contentPadding = contentPadding, + horizontalArrangement = Arrangement.spacedBy(HustleSpacing.extraSmall) + ) { + items(SERVICE_CATEGORIES, key = { it.name }) { category -> + HustleButton( + onClick = { onCategoryClick(CategoryType.fromTypeName(category.name)) }, + text = category.name, + leadingIcon = { + Icon( + painter = painterResource(id = category.iconResId), + contentDescription = category.name, + ) + } + ) + } + } +} + +@Preview(showBackground = true) +@Composable +private fun CategoryButtonRowPreview() { + CategoryButtonRow( + onCategoryClick = {} + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/components/home/MainContent.kt b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/MainContent.kt new file mode 100644 index 0000000..96a8a04 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/MainContent.kt @@ -0,0 +1,130 @@ +package com.cornellappdev.hustle.ui.components.home + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.cornellappdev.hustle.data.model.services.Service +import com.cornellappdev.hustle.ui.components.general.ClickableSectionHeader +import com.cornellappdev.hustle.ui.components.general.service.ServiceHorizontalCarouselSection +import com.cornellappdev.hustle.ui.navigation.CategoryType +import com.cornellappdev.hustle.ui.theme.HustleSpacing +import com.cornellappdev.hustle.ui.theme.HustleTheme +import com.cornellappdev.hustle.util.constants.TEST_SERVICES + +@Composable +fun MainContent( + popularRightNowListings: List, + newOnHustleListings: List, + servicesNearYouListings: List, + availableThisWeekListings: List, + navigateToCategorySubpage: (CategoryType) -> Unit, + navigateToServiceDetail: (Int) -> Unit, + onFavoriteClick: (Int) -> Unit, + modifier: Modifier = Modifier +) { + LazyColumn( + modifier = modifier + .fillMaxSize() + .padding(top = HustleSpacing.small), + ) { + item { + CategoryButtonRow(onCategoryClick = navigateToCategorySubpage) + } + item { + Spacer(modifier = Modifier.height(HustleSpacing.medium)) + } + item { + Column( + verticalArrangement = Arrangement.spacedBy(HustleSpacing.large) + ) { + ServiceHorizontalCarouselSection( + serviceListings = popularRightNowListings, + onServiceClick = navigateToServiceDetail, + onFavoriteClick = onFavoriteClick, + header = { + ClickableSectionHeader( + title = CategoryType.POPULAR_RIGHT_NOW.typeName, + onClick = { + navigateToCategorySubpage(CategoryType.POPULAR_RIGHT_NOW) + }, + modifier = Modifier.padding(start = 32.dp) + ) + } + ) + + ServiceHorizontalCarouselSection( + serviceListings = newOnHustleListings, + onServiceClick = navigateToServiceDetail, + onFavoriteClick = onFavoriteClick, + header = { + ClickableSectionHeader( + title = CategoryType.NEW_ON_HUSTLE.typeName, + onClick = { + navigateToCategorySubpage(CategoryType.NEW_ON_HUSTLE) + }, + modifier = Modifier.padding(start = 32.dp) + ) + } + ) + + ServiceHorizontalCarouselSection( + serviceListings = servicesNearYouListings, + onServiceClick = navigateToServiceDetail, + onFavoriteClick = onFavoriteClick, + header = { + ClickableSectionHeader( + title = CategoryType.SERVICES_NEAR_YOU.typeName, + onClick = { + navigateToCategorySubpage(CategoryType.SERVICES_NEAR_YOU) + }, + modifier = Modifier.padding(start = 32.dp) + ) + } + ) + + ServiceHorizontalCarouselSection( + serviceListings = availableThisWeekListings, + onServiceClick = navigateToServiceDetail, + onFavoriteClick = onFavoriteClick, + header = { + ClickableSectionHeader( + title = CategoryType.AVAILABLE_THIS_WEEK.typeName, + onClick = { + navigateToCategorySubpage(CategoryType.AVAILABLE_THIS_WEEK) + }, + modifier = Modifier.padding(start = 32.dp) + ) + } + ) + } + } + } +} + +@Preview(showBackground = true) +@Composable +private fun MainContentPreview() { + val popularRightNowListings = TEST_SERVICES + val newOnHustleListings = TEST_SERVICES + val servicesNearYouListings = TEST_SERVICES + val availableThisWeekListings = TEST_SERVICES + HustleTheme { + MainContent( + popularRightNowListings = popularRightNowListings, + newOnHustleListings = newOnHustleListings, + servicesNearYouListings = servicesNearYouListings, + availableThisWeekListings = availableThisWeekListings, + navigateToCategorySubpage = {}, + navigateToServiceDetail = {}, + onFavoriteClick = {}, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/components/home/SearchContent.kt b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/SearchContent.kt new file mode 100644 index 0000000..da691f4 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/SearchContent.kt @@ -0,0 +1,155 @@ +package com.cornellappdev.hustle.ui.components.home + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +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.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.cornellappdev.hustle.R +import com.cornellappdev.hustle.data.model.services.Service +import com.cornellappdev.hustle.ui.components.general.service.ServiceHorizontalCarouselSection +import com.cornellappdev.hustle.ui.theme.HustleColors +import com.cornellappdev.hustle.ui.theme.HustleSpacing +import com.cornellappdev.hustle.ui.theme.HustleTheme +import com.cornellappdev.hustle.util.constants.TEST_RECENT_SEARCHES +import com.cornellappdev.hustle.util.constants.TEST_SERVICES + +@Composable +fun SearchContent( + recentSearches: List, + recentlyViewedServices: List, + onSearchSuggestionClick: (String) -> Unit, + navigateToServiceDetail: (Int) -> Unit, + onFavoriteClick: (Int) -> Unit, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier + .fillMaxSize() + .padding(top = 4.dp) + .verticalScroll(rememberScrollState()), + ) { + AnimatedVisibility( + visible = recentSearches.isNotEmpty(), + label = "Recent Searches Visibility", + enter = fadeIn(animationSpec = tween(300)), + exit = fadeOut(animationSpec = tween(300)) + ) { + RecentSearchesSection(recentSearches, onSearchSuggestionClick) + } + + Spacer(modifier = Modifier.height(44.dp)) + + AnimatedVisibility( + visible = recentlyViewedServices.isNotEmpty(), + label = "Recently Viewed Services Visibility", + enter = fadeIn(animationSpec = tween(300)), + exit = fadeOut(animationSpec = tween(300)) + ) { + ServiceHorizontalCarouselSection( + serviceListings = recentlyViewedServices, + onServiceClick = navigateToServiceDetail, + onFavoriteClick = onFavoriteClick, + header = { + Text( + text = "Recently viewed", + style = MaterialTheme.typography.headlineSmall, + modifier = Modifier.padding(start = HustleSpacing.large) + ) + } + ) + } + } +} + +@Composable +private fun RecentSearchesSection( + recentSearches: List, + onSearchSuggestionClick: (String) -> Unit +) { + Column( + modifier = Modifier.padding(horizontal = HustleSpacing.large) + ) { + Text( + text = "Recent", + style = MaterialTheme.typography.headlineSmall + ) + + recentSearches.forEach { recentSearch -> + RecentSearchItem( + recentSearch = recentSearch, + onSearchSuggestionClick = onSearchSuggestionClick + ) + } + } +} + +@Composable +private fun RecentSearchItem( + recentSearch: String, + onSearchSuggestionClick: (String) -> Unit +) { + Column( + verticalArrangement = Arrangement.spacedBy(HustleSpacing.small), + modifier = Modifier + .clickable( + onClick = { + onSearchSuggestionClick(recentSearch) + } + ) + .padding(top = HustleSpacing.medium) + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(6.dp), + verticalAlignment = Alignment.CenterVertically + + ) { + Icon( + painter = painterResource(R.drawable.ic_history), + contentDescription = "Recent Search Icon", + tint = Color.Unspecified + ) + Text( + text = recentSearch, + style = MaterialTheme.typography.labelLarge + ) + } + HorizontalDivider(color = HustleColors.iconInactive) + } +} + +@Preview(showBackground = true) +@Composable +private fun SearchContentPreview() { + val recentSearches = TEST_RECENT_SEARCHES + val recentlyViewedServiceListings = TEST_SERVICES + HustleTheme { + SearchContent( + recentSearches = recentSearches, + recentlyViewedServices = recentlyViewedServiceListings, + onSearchSuggestionClick = {}, + navigateToServiceDetail = {}, + onFavoriteClick = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/components/home/SearchHeader.kt b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/SearchHeader.kt new file mode 100644 index 0000000..8d72dd6 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/SearchHeader.kt @@ -0,0 +1,143 @@ +package com.cornellappdev.hustle.ui.components.home + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.shrinkHorizontally +import androidx.compose.foundation.layout.Arrangement +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.size +import androidx.compose.foundation.text.input.TextFieldState +import androidx.compose.foundation.text.input.rememberTextFieldState +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +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.graphics.graphicsLayer +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.cornellappdev.hustle.ui.components.general.BackButton +import com.cornellappdev.hustle.ui.components.general.HustleSearchBar +import com.cornellappdev.hustle.ui.theme.HustleColors +import com.cornellappdev.hustle.ui.theme.HustleSpacing + +@Composable +fun SearchHeader( + queryState: TextFieldState, + isSearchActive: Boolean, + onSearchActiveChange: (Boolean) -> Unit, + onSearch: () -> Unit, + modifier: Modifier = Modifier +) { + val focusManager = LocalFocusManager.current + val animationProgress by animateFloatAsState( + targetValue = if (isSearchActive) 1.0f else 0.0f, + animationSpec = tween(durationMillis = 300), + label = "SearchHeaderAnimation" + ) + var headerHeightPx by remember { mutableIntStateOf(0) } + var gapHeightPx by remember { mutableIntStateOf(0) } + + Column(modifier = modifier) { + Text( + text = "Hustle", + style = MaterialTheme.typography.headlineMedium.copy( + fontStyle = FontStyle.Italic, + fontWeight = FontWeight.Bold, + color = HustleColors.hustleGreen + ), + modifier = Modifier + .fillMaxWidth() + .onSizeChanged { headerHeightPx = it.height } + .graphicsLayer { + translationY = animationProgress * -headerHeightPx + alpha = 1.0f - animationProgress + } + ) + Spacer( + modifier = Modifier + .fillMaxWidth() + .height(HustleSpacing.extraSmall) + .onSizeChanged { gapHeightPx = it.height } + ) + SearchBarRow( + isSearchActive, + queryState, + onBackClick = { + onSearchActiveChange(false) + focusManager.clearFocus() + }, + onFocus = { onSearchActiveChange(true) }, + onSearch = onSearch, + modifier = Modifier + .fillMaxWidth() + .graphicsLayer { + val totalDisplacement = headerHeightPx + gapHeightPx + translationY = animationProgress * -totalDisplacement + } + ) + } +} + +@Composable +private fun SearchBarRow( + isSearchActive: Boolean, + queryState: TextFieldState, + onBackClick: () -> Unit, + onFocus: () -> Unit, + onSearch: () -> Unit, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(HustleSpacing.medium), + verticalAlignment = Alignment.CenterVertically + ) { + AnimatedVisibility( + visible = isSearchActive, + enter = fadeIn() + scaleIn(), + exit = fadeOut(animationSpec = tween(150)) + shrinkHorizontally( + shrinkTowards = Alignment.Start, + animationSpec = tween(150) + ) + ) { + BackButton(onClick = onBackClick, modifier = Modifier.size(12.dp)) + } + HustleSearchBar( + queryState = queryState, + isSearchActive = isSearchActive, + onFocus = onFocus, + onSearch = onSearch, + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun SearchHeaderPreview() { + var isSearchActive by remember { mutableStateOf(false) } + val queryState = rememberTextFieldState() + SearchHeader( + queryState = queryState, + isSearchActive = isSearchActive, + onSearchActiveChange = { isSearchActive = it }, + onSearch = {} + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/navigation/Routes.kt b/app/src/main/java/com/cornellappdev/hustle/ui/navigation/Routes.kt index 23c13bd..15d4dff 100644 --- a/app/src/main/java/com/cornellappdev/hustle/ui/navigation/Routes.kt +++ b/app/src/main/java/com/cornellappdev/hustle/ui/navigation/Routes.kt @@ -25,6 +25,28 @@ sealed interface HomeDestination : AppDestination { @Serializable data class ServiceDetail(val serviceId: Int) : HomeDestination + + @Serializable + data class CategoryService(val categoryType: CategoryType) : HomeDestination +} + +@Serializable +enum class CategoryType(val typeName: String) { + LESSONS("Lessons"), + PHOTO("Photo"), + BEAUTY("Beauty"), + PROFESSIONAL("Professional"), + POPULAR_RIGHT_NOW("Popular right now"), + NEW_ON_HUSTLE("New on Hustle"), + SERVICES_NEAR_YOU("Services near you"), + AVAILABLE_THIS_WEEK("Available this week"); + + companion object { + fun fromTypeName(typeName: String): CategoryType { + return entries.firstOrNull { it.typeName == typeName } + ?: throw IllegalArgumentException("No CategoryType with typeName $typeName found.") + } + } } sealed interface LearnDestination : AppDestination { diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/navigation/navgraphs/HomeNavigation.kt b/app/src/main/java/com/cornellappdev/hustle/ui/navigation/navgraphs/HomeNavigation.kt index c6fea98..4ee5415 100644 --- a/app/src/main/java/com/cornellappdev/hustle/ui/navigation/navgraphs/HomeNavigation.kt +++ b/app/src/main/java/com/cornellappdev/hustle/ui/navigation/navgraphs/HomeNavigation.kt @@ -12,11 +12,20 @@ import com.cornellappdev.hustle.ui.screens.home.HomeScreen fun NavGraphBuilder.homeNavGraph(navController: NavHostController) { navigation(startDestination = HomeDestination.Home) { composable { - HomeScreen() + HomeScreen( + navigateToServiceDetail = { serviceId -> + navController.navigate(HomeDestination.ServiceDetail(serviceId)) + }, + navigateToCategorySubpage = { categoryType -> + navController.navigate(HomeDestination.CategoryService(categoryType)) + } + ) } composable { backStackEntry -> val serviceId = backStackEntry.toRoute() } + + composable {} } } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/screens/home/HomeScreen.kt b/app/src/main/java/com/cornellappdev/hustle/ui/screens/home/HomeScreen.kt index 082f1f4..336736b 100644 --- a/app/src/main/java/com/cornellappdev/hustle/ui/screens/home/HomeScreen.kt +++ b/app/src/main/java/com/cornellappdev/hustle/ui/screens/home/HomeScreen.kt @@ -1,11 +1,161 @@ package com.cornellappdev.hustle.ui.screens.home -import androidx.compose.material3.Text +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.input.TextFieldState +import androidx.compose.foundation.text.input.rememberTextFieldState 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.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel +import com.cornellappdev.hustle.data.model.services.Service +import com.cornellappdev.hustle.ui.components.home.MainContent +import com.cornellappdev.hustle.ui.components.home.SearchContent +import com.cornellappdev.hustle.ui.components.home.SearchHeader +import com.cornellappdev.hustle.ui.navigation.CategoryType +import com.cornellappdev.hustle.ui.theme.HustleSpacing +import com.cornellappdev.hustle.ui.theme.HustleTheme import com.cornellappdev.hustle.ui.viewmodels.home.HomeScreenViewModel +import com.cornellappdev.hustle.util.constants.TEST_RECENT_SEARCHES +import com.cornellappdev.hustle.util.constants.TEST_SERVICES @Composable -fun HomeScreen(viewModel: HomeScreenViewModel = hiltViewModel()) { - Text("Home Screen") +fun HomeScreen( + navigateToCategorySubpage: (CategoryType) -> Unit, + navigateToServiceDetail: (Int) -> Unit, + viewModel: HomeScreenViewModel = hiltViewModel() +) { + // TODO: Add states to viewmodel and replace test data with real data from viewmodel + val queryState = rememberTextFieldState() + var isSearchActive by remember { mutableStateOf(false) } + val popularRightNowListings = TEST_SERVICES + val newOnHustleListings = TEST_SERVICES + val servicesNearYouListings = TEST_SERVICES + val availableThisWeekListings = TEST_SERVICES + val recentSearches = TEST_RECENT_SEARCHES + val recentlyViewedServiceListings = TEST_SERVICES + + HomeScreenContent( + queryState = queryState, + isSearchActive = isSearchActive, + onSearchActiveChange = { isActive -> isSearchActive = isActive }, + popularRightNowListings = popularRightNowListings, + newOnHustleListings = newOnHustleListings, + servicesNearYouListings = servicesNearYouListings, + availableThisWeekListings = availableThisWeekListings, + recentSearches = recentSearches, + recentlyViewedServices = recentlyViewedServiceListings, + onSearch = {}, + onSearchSuggestionClick = {}, + navigateToCategorySubpage = navigateToCategorySubpage, + navigateToServiceDetail = navigateToServiceDetail, + onFavoriteClick = {} + ) + +} + +@Composable +private fun HomeScreenContent( + queryState: TextFieldState, + isSearchActive: Boolean, + onSearchActiveChange: (Boolean) -> Unit, + popularRightNowListings: List, + newOnHustleListings: List, + servicesNearYouListings: List, + availableThisWeekListings: List, + recentSearches: List, + recentlyViewedServices: List, + onSearch: () -> Unit, + onSearchSuggestionClick: (String) -> Unit, + navigateToCategorySubpage: (CategoryType) -> Unit, + navigateToServiceDetail: (Int) -> Unit, + onFavoriteClick: (Int) -> Unit, +) { + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.White) + ) { + SearchHeader( + queryState = queryState, + isSearchActive = isSearchActive, + onSearchActiveChange = onSearchActiveChange, + onSearch = onSearch, + modifier = Modifier.padding(horizontal = HustleSpacing.large) + ) + AnimatedContent( + targetState = isSearchActive, + label = "HomeScreenContentAnimation", + transitionSpec = { + fadeIn(animationSpec = tween(300)) togetherWith fadeOut( + animationSpec = tween( + 300 + ) + ) + }, + ) { isSearchActive -> + if (!isSearchActive) { + MainContent( + popularRightNowListings = popularRightNowListings, + newOnHustleListings = newOnHustleListings, + servicesNearYouListings = servicesNearYouListings, + availableThisWeekListings = availableThisWeekListings, + navigateToCategorySubpage = navigateToCategorySubpage, + navigateToServiceDetail = navigateToServiceDetail, + onFavoriteClick = onFavoriteClick + ) + } else { + SearchContent( + recentSearches = recentSearches, + recentlyViewedServices = recentlyViewedServices, + onSearchSuggestionClick = onSearchSuggestionClick, + navigateToServiceDetail = navigateToServiceDetail, + onFavoriteClick = onFavoriteClick + ) + } + } + } +} + +@Preview(showBackground = true) +@Composable +private fun HomeScreenPreview() { + val queryState = rememberTextFieldState() + var isSearchActive by remember { mutableStateOf(false) } + val popularRightNowListings = TEST_SERVICES + val newOnHustleListings = TEST_SERVICES + val servicesNearYouListings = TEST_SERVICES + val availableThisWeekListings = TEST_SERVICES + val recentSearches = TEST_RECENT_SEARCHES + val recentlyViewedServiceListings = TEST_SERVICES + HustleTheme { + HomeScreenContent( + queryState = queryState, + isSearchActive = isSearchActive, + onSearchActiveChange = { isActive -> isSearchActive = isActive }, + popularRightNowListings = popularRightNowListings, + newOnHustleListings = newOnHustleListings, + servicesNearYouListings = servicesNearYouListings, + availableThisWeekListings = availableThisWeekListings, + recentSearches = recentSearches, + recentlyViewedServices = recentlyViewedServiceListings, + onSearch = {}, + onSearchSuggestionClick = {}, + navigateToCategorySubpage = {}, + navigateToServiceDetail = {}, + onFavoriteClick = {} + ) + } } \ No newline at end of file From f56cbe8d1f4223731bd4ecb6a127c1f73a33cbbc Mon Sep 17 00:00:00 2001 From: Andrew Cheung Date: Tue, 18 Nov 2025 23:22:37 -0500 Subject: [PATCH 2/3] Updated nav route name, removed unused route, and updated testing constants --- .../hustle/ui/navigation/HustleNavigation.kt | 1 - .../hustle/ui/navigation/Routes.kt | 2 +- .../ui/navigation/navgraphs/HomeNavigation.kt | 4 +- .../hustle/util/constants/TestingConstants.kt | 45 +++++++++---------- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/navigation/HustleNavigation.kt b/app/src/main/java/com/cornellappdev/hustle/ui/navigation/HustleNavigation.kt index 7ecd607..f99dc94 100644 --- a/app/src/main/java/com/cornellappdev/hustle/ui/navigation/HustleNavigation.kt +++ b/app/src/main/java/com/cornellappdev/hustle/ui/navigation/HustleNavigation.kt @@ -20,7 +20,6 @@ fun HustleNavigation( ) { val navController = rememberNavController() val startDestination = if (isSignedIn) HomeTab else Onboarding - val currentRoute = navController.currentBackStackEntry?.destination?.route LaunchedEffect(isSignedIn) { if (!isSignedIn && navController.currentDestination != Onboarding) { diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/navigation/Routes.kt b/app/src/main/java/com/cornellappdev/hustle/ui/navigation/Routes.kt index 15d4dff..e20f35d 100644 --- a/app/src/main/java/com/cornellappdev/hustle/ui/navigation/Routes.kt +++ b/app/src/main/java/com/cornellappdev/hustle/ui/navigation/Routes.kt @@ -27,7 +27,7 @@ sealed interface HomeDestination : AppDestination { data class ServiceDetail(val serviceId: Int) : HomeDestination @Serializable - data class CategoryService(val categoryType: CategoryType) : HomeDestination + data class CategoryServices(val categoryType: CategoryType) : HomeDestination } @Serializable diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/navigation/navgraphs/HomeNavigation.kt b/app/src/main/java/com/cornellappdev/hustle/ui/navigation/navgraphs/HomeNavigation.kt index 4ee5415..43ed19f 100644 --- a/app/src/main/java/com/cornellappdev/hustle/ui/navigation/navgraphs/HomeNavigation.kt +++ b/app/src/main/java/com/cornellappdev/hustle/ui/navigation/navgraphs/HomeNavigation.kt @@ -17,7 +17,7 @@ fun NavGraphBuilder.homeNavGraph(navController: NavHostController) { navController.navigate(HomeDestination.ServiceDetail(serviceId)) }, navigateToCategorySubpage = { categoryType -> - navController.navigate(HomeDestination.CategoryService(categoryType)) + navController.navigate(HomeDestination.CategoryServices(categoryType)) } ) } @@ -26,6 +26,6 @@ fun NavGraphBuilder.homeNavGraph(navController: NavHostController) { val serviceId = backStackEntry.toRoute() } - composable {} + composable {} } } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/hustle/util/constants/TestingConstants.kt b/app/src/main/java/com/cornellappdev/hustle/util/constants/TestingConstants.kt index 5ae0e66..4f7241f 100644 --- a/app/src/main/java/com/cornellappdev/hustle/util/constants/TestingConstants.kt +++ b/app/src/main/java/com/cornellappdev/hustle/util/constants/TestingConstants.kt @@ -35,57 +35,56 @@ val TEST_SERVICES = listOf( ), Service( id = 2, - name = "Dreamy fall grad photo session", - category = "Photo", - minimumPrice = 67.0, - rating = 4.1, + name = "Pretty Nail Painting Service", + category = "Beauty", + minimumPrice = 10.50, + rating = 4.5, isFavorited = false, user = User( firebaseUid = "", email = "", - displayName = "Jane Doe", + displayName = "Lauren Ah-Hot", photoUrl = "https://lh3.googleusercontent.com/a/ACg8ocKJrWoJxoOC0CoGv76ocYAULrRz9dAlfxMOiTb78E5dXH1VVo_j=s576-c-no" ), - displayImageUrl = "https://news.cornell.edu/sites/default/files/styles/full_size/public/06_2023_1114_sh_005-n_1.jpg?itok=E3ecxgYl", + displayImageUrl = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQhJqCvqqrdsOc9pS4HZiHvoA-gtow5VeUF0g&s", priceUnit = "/hour", ), Service( id = 3, - name = "Dreamy fall grad photo session", - category = "Photo", - minimumPrice = 67.0, - rating = 4.1, + name = "Cheap Haircuts for Males", + category = "Beauty", + minimumPrice = 20.0, + rating = 4.2, isFavorited = false, user = User( firebaseUid = "", email = "", - displayName = "Jane Doe", + displayName = "Joshua Dirga", photoUrl = "https://lh3.googleusercontent.com/a/ACg8ocKJrWoJxoOC0CoGv76ocYAULrRz9dAlfxMOiTb78E5dXH1VVo_j=s576-c-no" ), - displayImageUrl = "https://news.cornell.edu/sites/default/files/styles/full_size/public/06_2023_1114_sh_005-n_1.jpg?itok=E3ecxgYl", - priceUnit = "/hour", + displayImageUrl = "https://s3-media0.fl.yelpcdn.com/bphoto/1KwtwltxdEYVz4TIHAzaow/1000s.jpg" ), Service( id = 4, - name = "Dreamy fall grad photo session", - category = "Photo", - minimumPrice = 67.0, - rating = 4.1, + name = "Computer Science Tutoring", + category = "Lessons", + minimumPrice = 15.0, + rating = 3.5, isFavorited = false, user = User( firebaseUid = "", email = "", - displayName = "Jane Doe", + displayName = "Andrew Cheung", photoUrl = "https://lh3.googleusercontent.com/a/ACg8ocKJrWoJxoOC0CoGv76ocYAULrRz9dAlfxMOiTb78E5dXH1VVo_j=s576-c-no" ), - displayImageUrl = "https://news.cornell.edu/sites/default/files/styles/full_size/public/06_2023_1114_sh_005-n_1.jpg?itok=E3ecxgYl", + displayImageUrl = "https://www.engineering.cornell.edu/wp-content/uploads/2025/03/Duffield-Atrium-Students-Header-02.jpg", priceUnit = "/hour", ), Service( id = 5, - name = "Dreamy fall grad photo session", - category = "Photo", - minimumPrice = 67.0, + name = "Really Awesome Resume Review and Editing Session", + category = "Professional", + minimumPrice = 35.0, rating = 4.1, isFavorited = false, user = User( @@ -94,7 +93,7 @@ val TEST_SERVICES = listOf( displayName = "Jane Doe", photoUrl = "https://lh3.googleusercontent.com/a/ACg8ocKJrWoJxoOC0CoGv76ocYAULrRz9dAlfxMOiTb78E5dXH1VVo_j=s576-c-no" ), - displayImageUrl = "https://news.cornell.edu/sites/default/files/styles/full_size/public/06_2023_1114_sh_005-n_1.jpg?itok=E3ecxgYl", + displayImageUrl = "https://www.engineering.cornell.edu/wp-content/uploads/2024/10/AU-CornellEngineering-March082023-181.jpg", priceUnit = "/hour", ), From 93559c5ac6af724d55253a2725d4cbec43cf08dc Mon Sep 17 00:00:00 2001 From: Andrew Cheung Date: Wed, 19 Nov 2025 16:08:24 -0500 Subject: [PATCH 3/3] Removed typename function, refactored categorytype, created homescreenviewstate --- .../ui/components/home/CategoryButtonRow.kt | 11 +-- .../hustle/ui/components/home/MainContent.kt | 14 ++-- .../ui/components/home/SearchContent.kt | 6 +- .../hustle/ui/navigation/Routes.kt | 20 +---- .../hustle/ui/screens/home/HomeScreen.kt | 83 ++++++++++--------- .../hustle/util/constants/HustleConstants.kt | 23 +++-- 6 files changed, 74 insertions(+), 83 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/components/home/CategoryButtonRow.kt b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/CategoryButtonRow.kt index 4b856a8..11512ec 100644 --- a/app/src/main/java/com/cornellappdev/hustle/ui/components/home/CategoryButtonRow.kt +++ b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/CategoryButtonRow.kt @@ -11,8 +11,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import com.cornellappdev.hustle.ui.components.general.HustleButton -import com.cornellappdev.hustle.ui.navigation.CategoryType import com.cornellappdev.hustle.ui.theme.HustleSpacing +import com.cornellappdev.hustle.util.constants.CategoryType import com.cornellappdev.hustle.util.constants.SERVICE_CATEGORIES @Composable @@ -28,14 +28,15 @@ fun CategoryButtonRow( contentPadding = contentPadding, horizontalArrangement = Arrangement.spacedBy(HustleSpacing.extraSmall) ) { - items(SERVICE_CATEGORIES, key = { it.name }) { category -> + items(SERVICE_CATEGORIES, key = { it.categoryType }) { category -> + val categoryType = category.categoryType HustleButton( - onClick = { onCategoryClick(CategoryType.fromTypeName(category.name)) }, - text = category.name, + onClick = { onCategoryClick(categoryType) }, + text = categoryType.typeName, leadingIcon = { Icon( painter = painterResource(id = category.iconResId), - contentDescription = category.name, + contentDescription = categoryType.typeName, ) } ) diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/components/home/MainContent.kt b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/MainContent.kt index 96a8a04..e0c722e 100644 --- a/app/src/main/java/com/cornellappdev/hustle/ui/components/home/MainContent.kt +++ b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/MainContent.kt @@ -14,9 +14,9 @@ import androidx.compose.ui.unit.dp import com.cornellappdev.hustle.data.model.services.Service import com.cornellappdev.hustle.ui.components.general.ClickableSectionHeader import com.cornellappdev.hustle.ui.components.general.service.ServiceHorizontalCarouselSection -import com.cornellappdev.hustle.ui.navigation.CategoryType import com.cornellappdev.hustle.ui.theme.HustleSpacing import com.cornellappdev.hustle.ui.theme.HustleTheme +import com.cornellappdev.hustle.util.constants.CategoryType import com.cornellappdev.hustle.util.constants.TEST_SERVICES @Composable @@ -112,16 +112,12 @@ fun MainContent( @Preview(showBackground = true) @Composable private fun MainContentPreview() { - val popularRightNowListings = TEST_SERVICES - val newOnHustleListings = TEST_SERVICES - val servicesNearYouListings = TEST_SERVICES - val availableThisWeekListings = TEST_SERVICES HustleTheme { MainContent( - popularRightNowListings = popularRightNowListings, - newOnHustleListings = newOnHustleListings, - servicesNearYouListings = servicesNearYouListings, - availableThisWeekListings = availableThisWeekListings, + popularRightNowListings = TEST_SERVICES, + newOnHustleListings = TEST_SERVICES, + servicesNearYouListings = TEST_SERVICES, + availableThisWeekListings = TEST_SERVICES, navigateToCategorySubpage = {}, navigateToServiceDetail = {}, onFavoriteClick = {}, diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/components/home/SearchContent.kt b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/SearchContent.kt index da691f4..d93df02 100644 --- a/app/src/main/java/com/cornellappdev/hustle/ui/components/home/SearchContent.kt +++ b/app/src/main/java/com/cornellappdev/hustle/ui/components/home/SearchContent.kt @@ -141,12 +141,10 @@ private fun RecentSearchItem( @Preview(showBackground = true) @Composable private fun SearchContentPreview() { - val recentSearches = TEST_RECENT_SEARCHES - val recentlyViewedServiceListings = TEST_SERVICES HustleTheme { SearchContent( - recentSearches = recentSearches, - recentlyViewedServices = recentlyViewedServiceListings, + recentSearches = TEST_RECENT_SEARCHES, + recentlyViewedServices = TEST_SERVICES, onSearchSuggestionClick = {}, navigateToServiceDetail = {}, onFavoriteClick = {} diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/navigation/Routes.kt b/app/src/main/java/com/cornellappdev/hustle/ui/navigation/Routes.kt index e20f35d..9703423 100644 --- a/app/src/main/java/com/cornellappdev/hustle/ui/navigation/Routes.kt +++ b/app/src/main/java/com/cornellappdev/hustle/ui/navigation/Routes.kt @@ -1,5 +1,6 @@ package com.cornellappdev.hustle.ui.navigation +import com.cornellappdev.hustle.util.constants.CategoryType import kotlinx.serialization.Serializable sealed interface AppDestination @@ -30,25 +31,6 @@ sealed interface HomeDestination : AppDestination { data class CategoryServices(val categoryType: CategoryType) : HomeDestination } -@Serializable -enum class CategoryType(val typeName: String) { - LESSONS("Lessons"), - PHOTO("Photo"), - BEAUTY("Beauty"), - PROFESSIONAL("Professional"), - POPULAR_RIGHT_NOW("Popular right now"), - NEW_ON_HUSTLE("New on Hustle"), - SERVICES_NEAR_YOU("Services near you"), - AVAILABLE_THIS_WEEK("Available this week"); - - companion object { - fun fromTypeName(typeName: String): CategoryType { - return entries.firstOrNull { it.typeName == typeName } - ?: throw IllegalArgumentException("No CategoryType with typeName $typeName found.") - } - } -} - sealed interface LearnDestination : AppDestination { @Serializable data object Workshops : LearnDestination diff --git a/app/src/main/java/com/cornellappdev/hustle/ui/screens/home/HomeScreen.kt b/app/src/main/java/com/cornellappdev/hustle/ui/screens/home/HomeScreen.kt index 336736b..7414358 100644 --- a/app/src/main/java/com/cornellappdev/hustle/ui/screens/home/HomeScreen.kt +++ b/app/src/main/java/com/cornellappdev/hustle/ui/screens/home/HomeScreen.kt @@ -24,10 +24,10 @@ import com.cornellappdev.hustle.data.model.services.Service import com.cornellappdev.hustle.ui.components.home.MainContent import com.cornellappdev.hustle.ui.components.home.SearchContent import com.cornellappdev.hustle.ui.components.home.SearchHeader -import com.cornellappdev.hustle.ui.navigation.CategoryType import com.cornellappdev.hustle.ui.theme.HustleSpacing import com.cornellappdev.hustle.ui.theme.HustleTheme import com.cornellappdev.hustle.ui.viewmodels.home.HomeScreenViewModel +import com.cornellappdev.hustle.util.constants.CategoryType import com.cornellappdev.hustle.util.constants.TEST_RECENT_SEARCHES import com.cornellappdev.hustle.util.constants.TEST_SERVICES @@ -48,35 +48,40 @@ fun HomeScreen( val recentlyViewedServiceListings = TEST_SERVICES HomeScreenContent( - queryState = queryState, - isSearchActive = isSearchActive, + homeScreenViewState = HomeScreenViewState( + queryState = queryState, + isSearchActive = isSearchActive, + popularRightNowListings = popularRightNowListings, + newOnHustleListings = newOnHustleListings, + servicesNearYouListings = servicesNearYouListings, + availableThisWeekListings = availableThisWeekListings, + recentSearches = recentSearches, + recentlyViewedServices = recentlyViewedServiceListings + ), onSearchActiveChange = { isActive -> isSearchActive = isActive }, - popularRightNowListings = popularRightNowListings, - newOnHustleListings = newOnHustleListings, - servicesNearYouListings = servicesNearYouListings, - availableThisWeekListings = availableThisWeekListings, - recentSearches = recentSearches, - recentlyViewedServices = recentlyViewedServiceListings, onSearch = {}, onSearchSuggestionClick = {}, navigateToCategorySubpage = navigateToCategorySubpage, navigateToServiceDetail = navigateToServiceDetail, onFavoriteClick = {} ) - } +data class HomeScreenViewState( + val queryState: TextFieldState, + val isSearchActive: Boolean, + val popularRightNowListings: List, + val newOnHustleListings: List, + val servicesNearYouListings: List, + val availableThisWeekListings: List, + val recentSearches: List, + val recentlyViewedServices: List, +) + @Composable private fun HomeScreenContent( - queryState: TextFieldState, - isSearchActive: Boolean, + homeScreenViewState: HomeScreenViewState, onSearchActiveChange: (Boolean) -> Unit, - popularRightNowListings: List, - newOnHustleListings: List, - servicesNearYouListings: List, - availableThisWeekListings: List, - recentSearches: List, - recentlyViewedServices: List, onSearch: () -> Unit, onSearchSuggestionClick: (String) -> Unit, navigateToCategorySubpage: (CategoryType) -> Unit, @@ -89,14 +94,14 @@ private fun HomeScreenContent( .background(Color.White) ) { SearchHeader( - queryState = queryState, - isSearchActive = isSearchActive, + queryState = homeScreenViewState.queryState, + isSearchActive = homeScreenViewState.isSearchActive, onSearchActiveChange = onSearchActiveChange, onSearch = onSearch, modifier = Modifier.padding(horizontal = HustleSpacing.large) ) AnimatedContent( - targetState = isSearchActive, + targetState = homeScreenViewState.isSearchActive, label = "HomeScreenContentAnimation", transitionSpec = { fadeIn(animationSpec = tween(300)) togetherWith fadeOut( @@ -108,18 +113,18 @@ private fun HomeScreenContent( ) { isSearchActive -> if (!isSearchActive) { MainContent( - popularRightNowListings = popularRightNowListings, - newOnHustleListings = newOnHustleListings, - servicesNearYouListings = servicesNearYouListings, - availableThisWeekListings = availableThisWeekListings, + popularRightNowListings = homeScreenViewState.popularRightNowListings, + newOnHustleListings = homeScreenViewState.newOnHustleListings, + servicesNearYouListings = homeScreenViewState.servicesNearYouListings, + availableThisWeekListings = homeScreenViewState.availableThisWeekListings, navigateToCategorySubpage = navigateToCategorySubpage, navigateToServiceDetail = navigateToServiceDetail, onFavoriteClick = onFavoriteClick ) } else { SearchContent( - recentSearches = recentSearches, - recentlyViewedServices = recentlyViewedServices, + recentSearches = homeScreenViewState.recentSearches, + recentlyViewedServices = homeScreenViewState.recentlyViewedServices, onSearchSuggestionClick = onSearchSuggestionClick, navigateToServiceDetail = navigateToServiceDetail, onFavoriteClick = onFavoriteClick @@ -134,23 +139,19 @@ private fun HomeScreenContent( private fun HomeScreenPreview() { val queryState = rememberTextFieldState() var isSearchActive by remember { mutableStateOf(false) } - val popularRightNowListings = TEST_SERVICES - val newOnHustleListings = TEST_SERVICES - val servicesNearYouListings = TEST_SERVICES - val availableThisWeekListings = TEST_SERVICES - val recentSearches = TEST_RECENT_SEARCHES - val recentlyViewedServiceListings = TEST_SERVICES HustleTheme { HomeScreenContent( - queryState = queryState, - isSearchActive = isSearchActive, + homeScreenViewState = HomeScreenViewState( + queryState = queryState, + isSearchActive = isSearchActive, + popularRightNowListings = TEST_SERVICES, + newOnHustleListings = TEST_SERVICES, + servicesNearYouListings = TEST_SERVICES, + availableThisWeekListings = TEST_SERVICES, + recentSearches = TEST_RECENT_SEARCHES, + recentlyViewedServices = TEST_SERVICES + ), onSearchActiveChange = { isActive -> isSearchActive = isActive }, - popularRightNowListings = popularRightNowListings, - newOnHustleListings = newOnHustleListings, - servicesNearYouListings = servicesNearYouListings, - availableThisWeekListings = availableThisWeekListings, - recentSearches = recentSearches, - recentlyViewedServices = recentlyViewedServiceListings, onSearch = {}, onSearchSuggestionClick = {}, navigateToCategorySubpage = {}, diff --git a/app/src/main/java/com/cornellappdev/hustle/util/constants/HustleConstants.kt b/app/src/main/java/com/cornellappdev/hustle/util/constants/HustleConstants.kt index 84eda21..4bc2d17 100644 --- a/app/src/main/java/com/cornellappdev/hustle/util/constants/HustleConstants.kt +++ b/app/src/main/java/com/cornellappdev/hustle/util/constants/HustleConstants.kt @@ -2,15 +2,28 @@ package com.cornellappdev.hustle.util.constants import androidx.annotation.DrawableRes import com.cornellappdev.hustle.R +import kotlinx.serialization.Serializable +//TODO: Separate into different constant files or data package if necessary +@Serializable +enum class CategoryType(val typeName: String) { + LESSONS("Lessons"), + PHOTO("Photo"), + BEAUTY("Beauty"), + PROFESSIONAL("Professional"), + POPULAR_RIGHT_NOW("Popular right now"), + NEW_ON_HUSTLE("New on Hustle"), + SERVICES_NEAR_YOU("Services near you"), + AVAILABLE_THIS_WEEK("Available this week"); +} data class ServiceCategory( - val name: String, + val categoryType: CategoryType, @DrawableRes val iconResId: Int ) val SERVICE_CATEGORIES = listOf( - ServiceCategory("Lessons", R.drawable.ic_lessons), - ServiceCategory("Photo", R.drawable.ic_photo), - ServiceCategory("Beauty", R.drawable.ic_beauty), - ServiceCategory("Professional", R.drawable.ic_professional) + ServiceCategory(CategoryType.LESSONS, R.drawable.ic_lessons), + ServiceCategory(CategoryType.PHOTO, R.drawable.ic_photo), + ServiceCategory(CategoryType.BEAUTY, R.drawable.ic_beauty), + ServiceCategory(CategoryType.PROFESSIONAL, R.drawable.ic_professional) ) \ No newline at end of file