Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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.theme.HustleSpacing
import com.cornellappdev.hustle.util.constants.CategoryType
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.categoryType }) { category ->
val categoryType = category.categoryType
HustleButton(
onClick = { onCategoryClick(categoryType) },
text = categoryType.typeName,
leadingIcon = {
Icon(
painter = painterResource(id = category.iconResId),
contentDescription = categoryType.typeName,
)
}
)
}
}
}

@Preview(showBackground = true)
@Composable
private fun CategoryButtonRowPreview() {
CategoryButtonRow(
onCategoryClick = {}
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
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.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
fun MainContent(
popularRightNowListings: List<Service>,
newOnHustleListings: List<Service>,
servicesNearYouListings: List<Service>,
availableThisWeekListings: List<Service>,
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)
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded padding value 32.dp should use HustleSpacing.large (which is 24.dp) or another appropriate spacing constant for consistency. This value appears on lines 58, 73, 88, and 103.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good thought, but would need to consult design

)
}
)

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() {
HustleTheme {
MainContent(
popularRightNowListings = TEST_SERVICES,
newOnHustleListings = TEST_SERVICES,
servicesNearYouListings = TEST_SERVICES,
availableThisWeekListings = TEST_SERVICES,
navigateToCategorySubpage = {},
navigateToServiceDetail = {},
onFavoriteClick = {},
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
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<String>,
recentlyViewedServices: List<Service>,
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<String>,
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() {
HustleTheme {
SearchContent(
recentSearches = TEST_RECENT_SEARCHES,
recentlyViewedServices = TEST_SERVICES,
onSearchSuggestionClick = {},
navigateToServiceDetail = {},
onFavoriteClick = {}
)
}
}
Loading
Loading