From 6b634f40b786e29bed03f923eaf209f11dda7544 Mon Sep 17 00:00:00 2001 From: ankitsaraf Date: Fri, 19 Dec 2025 18:59:01 +0530 Subject: [PATCH] New screens and improvements --- .../com/example/livingai_lg/MainActivity.kt | 12 +- .../livingai_lg/ui/components/ActionPopup.kt | 109 +++++ .../ui/components/AddressSelectorOverlay.kt | 229 ++++++++++ .../ui/components/BuyAnimalCard.kt | 336 ++++++++------ .../ui/components/DropdownInput.kt | 18 +- .../livingai_lg/ui/components/FilterButton.kt | 4 +- .../ui/components/FilterOverlay.kt | 60 +++ .../ui/components/FloatingActionBar.kt | 60 ++- .../ui/components/NotificationsOverlay.kt | 30 ++ .../livingai_lg/ui/components/OptionCard.kt | 28 +- .../livingai_lg/ui/components/Rating.kt | 57 +++ .../livingai_lg/ui/components/SortButton.kt | 14 +- .../livingai_lg/ui/components/SortOverlay.kt | 74 +++ .../ui/components/UserLocationHeader.kt | 109 ++--- .../CreateProfileScreen.kt | 4 +- .../ui/{login => login_legacy}/LoginScreen.kt | 3 +- .../ui/{login => login_legacy}/OtpScreen.kt | 6 +- .../{login => login_legacy}/SignInScreen.kt | 3 +- .../{login => login_legacy}/SignUpScreen.kt | 3 +- .../{login => login_legacy}/SuccessScreen.kt | 4 +- .../example/livingai_lg/ui/models/Animal.kt | 37 +- .../ui/models/BottomNavItemData.kt | 10 +- .../livingai_lg/ui/models/Notifications.kt | 37 ++ .../livingai_lg/ui/models/ProfileType.kt | 11 +- .../com/example/livingai_lg/ui/models/User.kt | 3 + .../ui/navigation/AppNavigation.kt | 59 ++- .../livingai_lg/ui/navigation/AuthNavGraph.kt | 17 +- .../livingai_lg/ui/navigation/MainNavGraph.kt | 127 ++++-- .../ui/screens/AnimalProfileScreen.kt | 140 ++++-- .../livingai_lg/ui/screens/BuyScreen.kt | 100 ++++- .../ui/screens/ChooseServiceScreen.kt | 98 +--- .../ui/screens/CreateProfileScreen.kt | 10 +- .../livingai_lg/ui/screens/FilterScreen.kt | 179 ++------ .../ui/screens/NewListingScreen.kt | 3 +- .../ui/screens/NotificationsScreen.kt | 165 +++++++ .../livingai_lg/ui/screens/PostSaleSurvey.kt | 12 - .../ui/screens/SavedListingsScreen.kt | 266 +++++++++++ .../livingai_lg/ui/screens/SortScreen.kt | 26 +- .../ui/screens/auth/SignInScreen.kt | 2 +- .../ui/screens/auth/SignUpScreen.kt | 2 +- .../ui/screens/chat/CallsScreen.kt | 327 ++++++++++++++ .../livingai_lg/ui/screens/chat/ChatScreen.kt | 365 +++++++++++++++ .../ui/screens/chat/ChatsScreen.kt | 333 ++++++++++++++ .../ui/screens/chat/ContactsScreen.kt | 422 ++++++++++++++++++ .../livingai_lg/ui/theme/AppTypography.kt | 12 + 45 files changed, 3277 insertions(+), 649 deletions(-) create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/ActionPopup.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/AddressSelectorOverlay.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/FilterOverlay.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/NotificationsOverlay.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/Rating.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/SortOverlay.kt rename app/src/main/java/com/example/livingai_lg/ui/{login => login_legacy}/CreateProfileScreen.kt (97%) rename app/src/main/java/com/example/livingai_lg/ui/{login => login_legacy}/LoginScreen.kt (98%) rename app/src/main/java/com/example/livingai_lg/ui/{login => login_legacy}/OtpScreen.kt (96%) rename app/src/main/java/com/example/livingai_lg/ui/{login => login_legacy}/SignInScreen.kt (98%) rename app/src/main/java/com/example/livingai_lg/ui/{login => login_legacy}/SignUpScreen.kt (99%) rename app/src/main/java/com/example/livingai_lg/ui/{login => login_legacy}/SuccessScreen.kt (95%) create mode 100644 app/src/main/java/com/example/livingai_lg/ui/models/Notifications.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/NotificationsScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/SavedListingsScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/chat/CallsScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/chat/ChatScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/chat/ChatsScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/chat/ContactsScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/theme/AppTypography.kt diff --git a/app/src/main/java/com/example/livingai_lg/MainActivity.kt b/app/src/main/java/com/example/livingai_lg/MainActivity.kt index 6281511..52878ed 100644 --- a/app/src/main/java/com/example/livingai_lg/MainActivity.kt +++ b/app/src/main/java/com/example/livingai_lg/MainActivity.kt @@ -4,14 +4,9 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavType @@ -19,17 +14,16 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument -import com.example.livingai_lg.ui.AuthState import com.example.livingai_lg.ui.MainViewModel import com.example.livingai_lg.ui.MainViewModelFactory -import com.example.livingai_lg.ui.login.* +import com.example.livingai_lg.ui.login_legacy.* import com.example.livingai_lg.ui.navigation.AppNavigation import com.example.livingai_lg.ui.theme.FarmMarketplaceTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - enableEdgeToEdge() + //enableEdgeToEdge() setContent { FarmMarketplaceTheme { val mainViewModel: MainViewModel = viewModel(factory = MainViewModelFactory(LocalContext.current)) @@ -54,6 +48,8 @@ class MainActivity : ComponentActivity() { } } + +// TODO: remove the old code after testing new stuff @Composable fun AuthNavigation() { val navController = rememberNavController() diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/ActionPopup.kt b/app/src/main/java/com/example/livingai_lg/ui/components/ActionPopup.kt new file mode 100644 index 0000000..6596d4f --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/ActionPopup.kt @@ -0,0 +1,109 @@ +package com.example.livingai_lg.ui.components + + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import kotlinx.coroutines.delay + +@Composable +fun ActionPopup( + visible: Boolean, + text: String, + icon: ImageVector, + modifier: Modifier = Modifier, + backgroundColor: Color = Color.Black, + contentColor: Color = Color.White, + autoDismissMillis: Long = 5000L, + onClick: (() -> Unit)? = null, + onDismiss: () -> Unit +) { + // Auto dismiss + LaunchedEffect(visible) { + if (visible) { + delay(autoDismissMillis) + onDismiss() + } + } + + AnimatedVisibility( + visible = visible, + enter = fadeIn() + slideInVertically { it / 2 }, + exit = fadeOut() + slideOutVertically { it / 2 }, + modifier = modifier + ) { + Row( + modifier = Modifier + .shadow(12.dp, RoundedCornerShape(50)) + .background(backgroundColor, RoundedCornerShape(50)) + .clickable( + enabled = onClick != null, + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { + onClick?.invoke() + } + .padding(horizontal = 16.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = contentColor, + modifier = Modifier.size(18.dp) + ) + + Text( + text = text, + color = contentColor, + fontSize = 14.sp, + fontWeight = FontWeight.Medium + ) + + Spacer(modifier = Modifier.width(4.dp)) + + Icon( + imageVector = Icons.Default.Close, + contentDescription = "Close", + tint = contentColor.copy(alpha = 0.8f), + modifier = Modifier + .size(16.dp) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { + onDismiss() + } + ) + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/AddressSelectorOverlay.kt b/app/src/main/java/com/example/livingai_lg/ui/components/AddressSelectorOverlay.kt new file mode 100644 index 0000000..397217c --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/AddressSelectorOverlay.kt @@ -0,0 +1,229 @@ +package com.example.livingai_lg.ui.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +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.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.LibraryAdd +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material.icons.filled.MyLocation +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedTextField +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.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.example.livingai_lg.ui.models.UserAddress + +@Composable +fun AddressSelectorOverlay( + visible: Boolean, + addresses: List, + selectedAddressId: String?, + onSelect: (String) -> Unit, + onClose: () -> Unit +) { + AnimatedVisibility( + visible = visible, + enter = slideInVertically( + initialOffsetY = { -it } + ) + fadeIn(), + exit = slideOutVertically( + targetOffsetY = { -it } + ) + fadeOut() + ) { + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF4F4F4)) + ) { + + Column { + AddressSelectorHeader(onClose) + AddressSearchBar() + AddressActionsRow() + SavedAddressesList( + addresses = addresses, + selectedAddressId = selectedAddressId, + onSelect = onSelect + ) + } + } + } +} + + +@Composable +fun AddressSelectorHeader(onClose: () -> Unit) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + IconButton(onClick = onClose) { + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") + } + Text( + text = "Select Your Location", + fontSize = 20.sp, + fontWeight = FontWeight.Medium + ) + } +} + +@Composable +fun AddressSearchBar() { + OutlinedTextField( + value = "", + onValueChange = {}, + placeholder = { Text("Search an area or address") }, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + trailingIcon = { + Icon(Icons.Default.Search, contentDescription = null) + }, + enabled = false // 👈 explicitly disabled for now + ) +} + +@Composable +fun AddressActionsRow() { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + AddressActionCard(Icons.Default.MyLocation,"Use Current Location") + AddressActionCard(Icons.Default.LibraryAdd,"Add New Address") + } +} + +@Composable +fun SavedAddressesList( + addresses: List, + selectedAddressId: String?, + onSelect: (String) -> Unit +) { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = "SAVED ADDRESSES", + fontSize = 12.sp, + color = Color.Gray + ) + + Spacer(Modifier.height(8.dp)) + + addresses.forEach { address -> + AddressItem( + address = address, + selected = address.id == selectedAddressId, + onClick = { onSelect(address.id) } + ) + Spacer(Modifier.height(8.dp)) + } + } +} + +@Composable +fun AddressItem( + address: UserAddress, + selected: Boolean, + onClick: () -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.White, RoundedCornerShape(12.dp)) + .clickable(onClick = onClick) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon(Icons.Default.Home, contentDescription = null) + + Spacer(Modifier.width(12.dp)) + + Column(modifier = Modifier.weight(1f)) { + Row(verticalAlignment = Alignment.CenterVertically) { + Text(address.name, fontWeight = FontWeight.Medium) + if (selected) { + Spacer(Modifier.width(8.dp)) + Text( + "SELECTED", + fontSize = 10.sp, + color = Color.Green + ) + } + } + Text(address.address, fontSize = 12.sp, color = Color.Gray) + } + + Icon(Icons.Default.MoreVert, contentDescription = null) + } +} + +@Composable +fun AddressActionCard( + icon: ImageVector, + label: String, + modifier: Modifier = Modifier, + onClick: () -> Unit = {} +) { + Column( + modifier = modifier + .size(110.dp) + .background(Color.White, RoundedCornerShape(16.dp)) + .border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(16.dp)) + .clickable { onClick() } + .padding(12.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Icon( + imageVector = icon, + contentDescription = label, + tint = Color.Black, + modifier = Modifier.size(28.dp) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = label, + fontSize = 13.sp, + fontWeight = FontWeight.Medium, + textAlign = TextAlign.Center + ) + } +} + diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/BuyAnimalCard.kt b/app/src/main/java/com/example/livingai_lg/ui/components/BuyAnimalCard.kt index 6d70097..e3c7a0d 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/components/BuyAnimalCard.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/components/BuyAnimalCard.kt @@ -6,15 +6,22 @@ import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.LocationOn +import androidx.compose.material.icons.outlined.Visibility import androidx.compose.material3.Icon +import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -26,6 +33,7 @@ import com.example.livingai_lg.ui.utils.formatDistance import com.example.livingai_lg.ui.utils.formatPrice import com.example.livingai_lg.ui.utils.formatViews import com.example.livingai_lg.R +import com.example.livingai_lg.ui.theme.AppTypography @Composable fun BuyAnimalCard( @@ -34,6 +42,7 @@ fun BuyAnimalCard( onSavedChange: (Boolean) -> Unit, onProductClick: () -> Unit, onSellerClick:(sellerId: String)-> Unit, + onBookmarkClick: () -> Unit ) { Box( modifier = Modifier @@ -46,163 +55,204 @@ fun BuyAnimalCard( Color(0xFF000000).copy(alpha = 0.1f), RoundedCornerShape(14.dp) ) - .clickable( - indication = LocalIndication.current, - interactionSource = remember { MutableInteractionSource() }, - onClick = onProductClick - ) + ) { Column { - // Image - Box( - modifier = Modifier - .fillMaxWidth() - .height(257.dp) - ) { - ImageCarousel( - imageUrls = product.imageUrl ?: emptyList(), - modifier = Modifier.fillMaxSize() - ) - - // Views - Row( - modifier = Modifier - .align(Alignment.TopStart) - .padding(8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - painter = painterResource(R.drawable.ic_view), - contentDescription = null, - tint = Color.White, - modifier = Modifier.size(16.dp) - ) - Spacer(Modifier.width(4.dp)) - Text(formatViews(product.views), fontSize = 10.sp, color = Color.White) - } - - // Distance - Row( - modifier = Modifier - .align(Alignment.BottomStart) - .padding(8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - painter = painterResource(R.drawable.ic_location), - contentDescription = null, - tint = Color.White, - modifier = Modifier.size(12.dp) - ) - Spacer(Modifier.width(4.dp)) - Text(formatDistance( product.distance), fontSize = 16.sp, color = Color.White) - } - } - - // Content Column( - modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Column { - Text( - product.name?: "", - fontSize = 16.sp, - fontWeight = FontWeight.Medium - ) - Text( - formatPrice(product.price), - fontSize = 16.sp, - fontWeight = FontWeight.Medium - ) - } - - Column(horizontalAlignment = Alignment.End,modifier = Modifier.clickable( - indication = LocalIndication.current, - interactionSource = remember { MutableInteractionSource() } - ) { - onSellerClick(product.sellerId?:"") - }) { - Text("Sold By: ${product.sellerName}", fontSize = 13.sp) - Text(product.sellerType?: "???", fontSize = 13.sp) - } - } - - // Rating - Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - painter = painterResource(R.drawable.ic_star), - contentDescription = null, - tint = Color(0xFFDE9A07), - modifier = Modifier.size(8.dp) - ) - Spacer(Modifier.width(4.dp)) - Text( - "${product.rating} (${product.ratingCount} Ratings)", - fontSize = 8.sp - ) - } - - // Badges - Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) { - - Box( - modifier = Modifier - .background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp)) - .padding(horizontal = 8.dp, vertical = 6.dp) - ) { - val scoreString = "AI Score: ${product.aiScore?: 0}" - Text(scoreString, fontSize = 12.sp) - } - Box( - modifier = Modifier - .background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp)) - .padding(horizontal = 8.dp, vertical = 6.dp) - ) { - Text("placeholder", fontSize = 12.sp) - } - if(product.milkCapacity != null) { - Box( - modifier = Modifier - .background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp)) - .padding(horizontal = 8.dp, vertical = 6.dp) - ) { - Text("Milk Capacity: ${product.milkCapacity}L", fontSize = 12.sp) - } - } - } - - // Description - Text( - product.description?: "", - fontSize = 14.sp, - color = Color(0xFF717182), - lineHeight = 20.sp + modifier = Modifier.clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() }, + onClick = onProductClick ) + ) { - // Actions + // Image + Box( + modifier = Modifier + .fillMaxWidth() + .height(257.dp) + ) { + ImageCarousel( + imageUrls = product.imageUrl ?: emptyList(), + modifier = Modifier.fillMaxSize() + ) + + // Views + Row( + modifier = Modifier + .align(Alignment.TopStart) + .padding(6.dp) + .shadow( + elevation = 6.dp, + shape = RoundedCornerShape(50), + ambientColor = Color.Black.copy(alpha = 0.4f), + spotColor = Color.Black.copy(alpha = 0.4f) + ) + .background( + color = Color.Black.copy(alpha = 0.35f), // 👈 light but effective + shape = RoundedCornerShape(50) + ) + .padding(horizontal = 6.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Outlined.Visibility, + contentDescription = null, + tint = Color.White, + modifier = Modifier.size(16.dp).shadow( + elevation = 6.dp, + shape = CircleShape, + clip = false + ) + ) + Spacer(Modifier.width(4.dp)) + Text(formatViews(product.views), fontSize = AppTypography.Caption, color = Color.White,style = LocalTextStyle.current.copy( + )) + } + + // Distance + Row( + modifier = Modifier + .align(Alignment.BottomStart) + .padding(6.dp) + .shadow( + elevation = 6.dp, + shape = RoundedCornerShape(50), + ambientColor = Color.Black.copy(alpha = 0.4f), + spotColor = Color.Black.copy(alpha = 0.4f) + ) + .background( + color = Color.Black.copy(alpha = 0.35f), // 👈 light but effective + shape = RoundedCornerShape(50) + ) + .padding(horizontal = 6.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Outlined.LocationOn, + contentDescription = null, + tint = Color.White, + modifier = Modifier.size(12.dp) + ) + Spacer(Modifier.width(4.dp)) + Text( + formatDistance(product.distance), + fontSize = AppTypography.Body, + color = Color.White, + ) + } + } + + // Content + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Column { + Text( + product.name ?: "", + fontSize = AppTypography.Body, + fontWeight = FontWeight.Medium + ) + Text( + formatPrice(product.price), + fontSize = AppTypography.Body, + fontWeight = FontWeight.Medium + ) + } + + Column( + horizontalAlignment = Alignment.End, modifier = Modifier.clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { + onSellerClick(product.sellerId ?: "") + }) { + Text("Sold By: ${product.sellerName}", fontSize = AppTypography.BodySmall) + Text(product.sellerType ?: "???", fontSize = AppTypography.BodySmall) + } + } + + // Rating + Row(verticalAlignment = Alignment.CenterVertically) { + RatingStars(product.rating?:0f, starSize = 12.dp) + Spacer(Modifier.width(4.dp)) + Text( + "${product.rating} (${product.ratingCount} Ratings)", + fontSize = AppTypography.Caption + ) + } + + // Badges + Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) { + + Box( + modifier = Modifier + .background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp)) + .padding(horizontal = 8.dp, vertical = 6.dp) + ) { + Row { + Text("AI Score: ", fontSize = AppTypography.Caption) + val scoreString = "${product.aiScore ?: 0}" + Text(scoreString, fontSize = AppTypography.Caption, fontWeight = FontWeight.Bold) + + } + } + + if (product.milkCapacity != null) { + Box( + modifier = Modifier + .background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp)) + .padding(horizontal = 8.dp, vertical = 6.dp) + ) { + Row { + Text( + "Milk Capacity:", + fontSize = AppTypography.Caption + ) + Text( + "${product.milkCapacity}L", + fontSize = AppTypography.Caption, + fontWeight = FontWeight.Bold + ) + } + } + } + } + + // Description + Text( + product.description ?: "", + fontSize = 14.sp, + color = Color(0xFF717182), + lineHeight = 20.sp + ) + + // Actions // Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) { // ActionButton(R.drawable.ic_chat, "Chat") // ActionButton(R.drawable.ic_phone, "Call") // ActionButton(R.drawable.ic_location, "Location") // ActionButton(R.drawable.ic_bookmark_plus, "Bookmark") // } - FloatingActionBar( - modifier = Modifier - .padding(bottom = 12.dp) - .zIndex(10f), // 👈 ensure it floats above everything - onChatClick = { /* TODO */ }, - onCallClick = { /* TODO */ }, - onLocationClick = { /* TODO */ }, - onBookmarkClick = { /* TODO */ } - ) + } } + FloatingActionBar( + modifier = Modifier + .padding(bottom = 12.dp) + .zIndex(10f), // 👈 ensure it floats above everything + showContainer = false, + onChatClick = { /* TODO */ }, + onCallClick = { /* TODO */ }, + onLocationClick = { /* TODO */ }, + onBookmarkClick = onBookmarkClick + ) } } } diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/DropdownInput.kt b/app/src/main/java/com/example/livingai_lg/ui/components/DropdownInput.kt index 3620a62..8b98f62 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/components/DropdownInput.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/components/DropdownInput.kt @@ -7,6 +7,8 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -16,6 +18,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.example.livingai_lg.ui.theme.AppTypography import com.example.livingai_lg.ui.theme.FarmTextDark @OptIn(ExperimentalMaterial3Api::class) @@ -38,7 +41,7 @@ fun DropdownInput( if (label != null) { Text( text = label, - fontSize = 16.sp, + fontSize = AppTypography.Body, fontWeight = FontWeight.Medium, color = FarmTextDark ) @@ -78,15 +81,16 @@ fun DropdownInput( // Custom placeholder support Text( text = selected.ifEmpty { placeholder }, - fontSize = 16.sp, + fontSize = AppTypography.Body, color = if (selected.isEmpty()) Color(0xFF99A1AF) else FarmTextDark ) - Text( - text = "▼", - fontSize = 12.sp, - color = FarmTextDark + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = "Dropdown", + tint = FarmTextDark ) + } } @@ -99,7 +103,7 @@ fun DropdownInput( options.forEach { item -> DropdownMenuItem( text = { - Text(item, fontSize = 16.sp, color = FarmTextDark) + Text(item, fontSize = AppTypography.Body, color = FarmTextDark) }, onClick = { onSelect(item) diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/FilterButton.kt b/app/src/main/java/com/example/livingai_lg/ui/components/FilterButton.kt index 5e07ebe..76697bf 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/components/FilterButton.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/components/FilterButton.kt @@ -26,7 +26,7 @@ import com.example.livingai_lg.R @Composable fun FilterButton( - onFilterClick: () -> Unit + onClick: () -> Unit ) { Row( modifier = Modifier @@ -37,7 +37,7 @@ fun FilterButton( .clickable( indication = LocalIndication.current, interactionSource = remember { MutableInteractionSource() }, - onClick = onFilterClick, + onClick = onClick, ), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/FilterOverlay.kt b/app/src/main/java/com/example/livingai_lg/ui/components/FilterOverlay.kt new file mode 100644 index 0000000..ba065c9 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/FilterOverlay.kt @@ -0,0 +1,60 @@ +package com.example.livingai_lg.ui.screens + +import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp + +@Composable +fun FilterOverlay( + visible: Boolean, + onDismiss: () -> Unit, + onSubmitClick: () -> Unit = {}, +) { + if (!visible) return + + BackHandler { onDismiss() } + + Box( + modifier = Modifier + .fillMaxSize() + ) { + // Dimmed background + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.Black.copy(alpha = 0.35f)) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { onDismiss() } + ) + AnimatedVisibility( + visible = visible, + enter = slideInHorizontally { fullWidth -> fullWidth }, + exit = slideOutHorizontally { fullWidth -> fullWidth }, + modifier = Modifier.fillMaxHeight() + ) { + FilterScreen( + onBackClick = onDismiss, + onCancelClick = onDismiss, + onSubmitClick = { + onSubmitClick() + onDismiss() + } + ) + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/FloatingActionBar.kt b/app/src/main/java/com/example/livingai_lg/ui/components/FloatingActionBar.kt index 0f094cd..7138ae7 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/components/FloatingActionBar.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/components/FloatingActionBar.kt @@ -1,14 +1,26 @@ package com.example.livingai_lg.ui.components +import android.media.Image +import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.Chat +import androidx.compose.material.icons.outlined.BookmarkAdd +import androidx.compose.material.icons.outlined.Chat +import androidx.compose.material.icons.outlined.LocationOn +import androidx.compose.material.icons.outlined.Phone import androidx.compose.material3.Icon import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.example.livingai_lg.R @@ -16,35 +28,44 @@ import com.example.livingai_lg.R @Composable fun FloatingActionBar( modifier: Modifier = Modifier, + showContainer: Boolean = true, onChatClick: () -> Unit = {}, onCallClick: () -> Unit = {}, onLocationClick: () -> Unit = {}, onBookmarkClick: () -> Unit = {} ) { + val containerModifier = + if (showContainer) { + Modifier + .shadow( + elevation = 12.dp, + shape = RoundedCornerShape(50), + clip = false + ) + .background( + color = Color.White, + shape = RoundedCornerShape(50) + ) + .padding(horizontal = 12.dp, vertical = 12.dp) + } else { + Modifier // no background, no shadow + } + Box( modifier = modifier .fillMaxWidth() //.padding(horizontal = 24.dp) - .shadow( - elevation = 12.dp, - shape = RoundedCornerShape(50), // pill shape - clip = false - ) - .background( - color = Color.White, - shape = RoundedCornerShape(50) - ) - .padding(vertical = 12.dp) + .then(containerModifier) ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically ) { - FloatingActionIcon(R.drawable.ic_chat, onChatClick) - FloatingActionIcon(R.drawable.ic_phone, onCallClick) - FloatingActionIcon(R.drawable.ic_location, onLocationClick) - FloatingActionIcon(R.drawable.ic_bookmark_plus, onBookmarkClick) + FloatingActionIcon(Icons.AutoMirrored.Outlined.Chat, onChatClick) + FloatingActionIcon(Icons.Outlined.Phone, onCallClick) + FloatingActionIcon(Icons.Outlined.LocationOn, onLocationClick) + FloatingActionIcon(Icons.Outlined.BookmarkAdd, onBookmarkClick) } } } @@ -52,7 +73,7 @@ fun FloatingActionBar( @Composable private fun FloatingActionIcon( - iconRes: Int, + icon: ImageVector, onClick: () -> Unit ) { Box( @@ -63,11 +84,16 @@ private fun FloatingActionIcon( shape = RoundedCornerShape(24.dp), clip = false ) - .background(Color.White, RoundedCornerShape(24.dp)), + .background(Color.White, RoundedCornerShape(24.dp)) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() }, + onClick = onClick + ), contentAlignment = Alignment.Center ) { Icon( - painter = painterResource(iconRes), + imageVector = icon, contentDescription = null, tint = Color(0xFF0A0A0A), modifier = Modifier.size(22.dp) diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/NotificationsOverlay.kt b/app/src/main/java/com/example/livingai_lg/ui/components/NotificationsOverlay.kt new file mode 100644 index 0000000..d65736b --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/NotificationsOverlay.kt @@ -0,0 +1,30 @@ +package com.example.livingai_lg.ui.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.runtime.Composable +import com.example.livingai_lg.ui.models.AppNotification +import com.example.livingai_lg.ui.screens.NotificationsScreen + +@Composable +fun NotificationsOverlay( + visible: Boolean, + notifications: List, + onClose: () -> Unit, + onDismiss: (String) -> Unit, + onNotificationClick: (String) -> Unit = {} +) { + AnimatedVisibility( + visible = visible, + enter = slideInVertically { -it }, + exit = slideOutVertically { -it } + ) { + NotificationsScreen( + notifications = notifications, + onBackClick = onClose, + onDismiss = onDismiss, + onNotificationClick = onNotificationClick + ) + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/OptionCard.kt b/app/src/main/java/com/example/livingai_lg/ui/components/OptionCard.kt index 2e5d4d5..c50c0b6 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/components/OptionCard.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/components/OptionCard.kt @@ -16,16 +16,19 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.example.livingai_lg.ui.theme.AppTypography @Composable fun OptionCard( label: String, - icon: Int, + icon: Any, iconBackgroundColor: Color, + iconTint: Color = Color.Unspecified, onClick: () -> Unit ) { Box( @@ -54,17 +57,28 @@ fun OptionCard( .background(iconBackgroundColor, RoundedCornerShape(14.dp)), contentAlignment = Alignment.Center ) { - Icon( - painter = painterResource(id = icon), - contentDescription = label, - tint = Color.Unspecified - ) + when (icon) { + is ImageVector -> { + Icon( + imageVector = icon, + contentDescription = label, + tint = iconTint + ) + } + is Int -> { + Icon( + painter = painterResource(icon), + contentDescription = label, + tint = Color.Unspecified + ) + } + } } // Label Text( text = label, - fontSize = 16.sp, + fontSize = AppTypography.Body, fontWeight = FontWeight.Normal, color = Color(0xFF0A0A0A), modifier = Modifier.weight(1f) diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/Rating.kt b/app/src/main/java/com/example/livingai_lg/ui/components/Rating.kt new file mode 100644 index 0000000..1c6e758 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/Rating.kt @@ -0,0 +1,57 @@ +package com.example.livingai_lg.ui.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.StarHalf +import androidx.compose.material.icons.filled.Star +import androidx.compose.material3.Icon +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.unit.Dp +import androidx.compose.ui.unit.dp + +@Composable +fun RatingStars( + rating: Float, + modifier: Modifier = Modifier, + starSize: Dp = 14.dp, + color: Color = Color(0xFFDE9A07) +) { + val fullStars = rating.toInt() + val remainder = rating - fullStars + + val showHalfStar = remainder in 0.25f..0.74f + val extraFullStar = remainder >= 0.75f + + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(2.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // Full stars + repeat( + if (extraFullStar) fullStars + 1 else fullStars + ) { + Icon( + imageVector = Icons.Default.Star, + contentDescription = null, + tint = color, + modifier = Modifier.size(starSize) + ) + } + + // Half star (only if not rounded up) + if (showHalfStar) { + Icon( + imageVector = Icons.AutoMirrored.Filled.StarHalf, + contentDescription = null, + tint = color, + modifier = Modifier.size(starSize) + ) + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/SortButton.kt b/app/src/main/java/com/example/livingai_lg/ui/components/SortButton.kt index 02a68e9..34d7424 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/components/SortButton.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/components/SortButton.kt @@ -25,17 +25,23 @@ import androidx.compose.ui.unit.sp import com.example.livingai_lg.R @Composable -fun SortButton(onSortClick: () -> Unit) { +fun SortButton( + onClick: () -> Unit +) { Row( modifier = Modifier .height(36.dp) - .border(1.078.dp, Color(0xFF000000).copy(alpha = 0.1f), RoundedCornerShape(8.dp)) + .border( + 1.078.dp, + Color(0xFF000000).copy(alpha = 0.1f), + RoundedCornerShape(8.dp) + ) .background(Color.White, RoundedCornerShape(8.dp)) .padding(horizontal = 8.dp) .clickable( indication = LocalIndication.current, interactionSource = remember { MutableInteractionSource() }, - onClick = onSortClick, + onClick = onClick ), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) @@ -53,4 +59,4 @@ fun SortButton(onSortClick: () -> Unit) { color = Color(0xFF0A0A0A) ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/SortOverlay.kt b/app/src/main/java/com/example/livingai_lg/ui/components/SortOverlay.kt new file mode 100644 index 0000000..d9d952e --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/SortOverlay.kt @@ -0,0 +1,74 @@ +package com.example.livingai_lg.ui.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import com.example.livingai_lg.ui.models.SortField +import com.example.livingai_lg.ui.screens.SortScreen + +@Composable +fun SortOverlay( + visible: Boolean, + onApplyClick: (selected: List) -> Unit, + onDismiss: () -> Unit, +) { + if (!visible) return + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.Black.copy(alpha = 0.35f)) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { onDismiss() } // tap outside closes + ) { + AnimatedVisibility( + visible = visible, + enter = slideInHorizontally( + initialOffsetX = { -it } + ), + exit = slideOutHorizontally( + targetOffsetX = { -it } + ) + ) { + Box( + modifier = Modifier + .fillMaxHeight() + .fillMaxWidth(0.85f) // 👈 DOES NOT cover full screen + .background(Color(0xFFF7F4EE)) + ) { + // Prevent click-through + Box( + modifier = Modifier + .fillMaxSize() + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) {} + ) { + SortScreen( + onApplyClick = { selected -> + onApplyClick(selected) + onDismiss() + // TODO: apply sort + }, + onCancelClick = onDismiss, + onBackClick = onDismiss + ) + } + } + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/UserLocationHeader.kt b/app/src/main/java/com/example/livingai_lg/ui/components/UserLocationHeader.kt index d19b3f5..27fe06e 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/components/UserLocationHeader.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/components/UserLocationHeader.kt @@ -31,23 +31,15 @@ import com.example.livingai_lg.R @Composable fun UserLocationHeader( user: UserProfile, + selectedAddressId: String, modifier: Modifier = Modifier, - onAddressSelected: (UserAddress) -> Unit = {}, - onAddNewClick: () -> Unit = {} // future navigation hook + onOpenAddressOverlay: () -> Unit ) { - var expanded by remember { mutableStateOf(false) } - - var selectedAddress by remember { - mutableStateOf( - user.addresses.firstOrNull { it.isPrimary } - ?: user.addresses.first() - ) - } - Row( modifier = modifier.wrapContentWidth(), verticalAlignment = Alignment.CenterVertically ) { + val selectedAddress = user.addresses.find { it.id == selectedAddressId } ?: user.addresses.first() // Profile image Box( @@ -75,79 +67,38 @@ fun UserLocationHeader( Spacer(modifier = Modifier.width(12.dp)) - // Anchor ONLY the text section - Box { - Column( - modifier = Modifier - .clickable( - indication = LocalIndication.current, - interactionSource = remember { MutableInteractionSource() } - ) { expanded = true } + // Address + arrow (click opens overlay) + Column( + modifier = Modifier.clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } ) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = selectedAddress.name, - fontSize = 16.sp, - fontWeight = FontWeight.SemiBold, - color = Color.Black - ) - Icon( - imageVector = Icons.Default.KeyboardArrowDown, - contentDescription = null, - tint = Color.Black, - modifier = Modifier.size(18.dp) - ) - } - + onOpenAddressOverlay() + } + ) { + Row(verticalAlignment = Alignment.CenterVertically) { Text( - text = selectedAddress.address, - fontSize = 13.sp, - color = Color.Black.copy(alpha = 0.7f), - maxLines = 1, - overflow = TextOverflow.Ellipsis + text = selectedAddress.name, + fontSize = 16.sp, + fontWeight = FontWeight.SemiBold, + color = Color.Black + ) + Icon( + imageVector = Icons.Default.KeyboardArrowDown, + contentDescription = null, + tint = Color.Black, + modifier = Modifier.size(18.dp) ) } - // Dropdown appears BELOW name/address - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false } - ) { - user.addresses.forEach { address -> - DropdownMenuItem( - text = { - Text( - text = address.name, - fontWeight = if (address == selectedAddress) - FontWeight.SemiBold - else FontWeight.Normal - ) - }, - onClick = { - selectedAddress = address - expanded = false - onAddressSelected(address) - } - ) - } - - Divider() - - // Add New option - DropdownMenuItem( - text = { - Text( - text = "Add New +", - fontWeight = FontWeight.Medium, - color = Color(0xFF007BFF) - ) - }, - onClick = { - expanded = false - onAddNewClick() - } - ) - } + Text( + text = selectedAddress.address, + fontSize = 13.sp, + color = Color.Black.copy(alpha = 0.7f), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) } } } + diff --git a/app/src/main/java/com/example/livingai_lg/ui/login/CreateProfileScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/login_legacy/CreateProfileScreen.kt similarity index 97% rename from app/src/main/java/com/example/livingai_lg/ui/login/CreateProfileScreen.kt rename to app/src/main/java/com/example/livingai_lg/ui/login_legacy/CreateProfileScreen.kt index 53e2a83..00756d1 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/login/CreateProfileScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/login_legacy/CreateProfileScreen.kt @@ -1,6 +1,5 @@ -package com.example.livingai_lg.ui.login +package com.example.livingai_lg.ui.login_legacy -import android.app.Application import android.widget.Toast import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -14,7 +13,6 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource diff --git a/app/src/main/java/com/example/livingai_lg/ui/login/LoginScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/login_legacy/LoginScreen.kt similarity index 98% rename from app/src/main/java/com/example/livingai_lg/ui/login/LoginScreen.kt rename to app/src/main/java/com/example/livingai_lg/ui/login_legacy/LoginScreen.kt index c04ba19..0610de9 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/login/LoginScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/login_legacy/LoginScreen.kt @@ -1,4 +1,4 @@ -package com.example.livingai_lg.ui.login +package com.example.livingai_lg.ui.login_legacy import android.widget.Toast import androidx.compose.foundation.background @@ -11,7 +11,6 @@ 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.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight diff --git a/app/src/main/java/com/example/livingai_lg/ui/login/OtpScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/login_legacy/OtpScreen.kt similarity index 96% rename from app/src/main/java/com/example/livingai_lg/ui/login/OtpScreen.kt rename to app/src/main/java/com/example/livingai_lg/ui/login_legacy/OtpScreen.kt index 16e3154..a80b4ea 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/login/OtpScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/login_legacy/OtpScreen.kt @@ -1,8 +1,7 @@ -package com.example.livingai_lg.ui.login +package com.example.livingai_lg.ui.login_legacy import android.widget.Toast -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.* @@ -10,7 +9,6 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight diff --git a/app/src/main/java/com/example/livingai_lg/ui/login/SignInScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/login_legacy/SignInScreen.kt similarity index 98% rename from app/src/main/java/com/example/livingai_lg/ui/login/SignInScreen.kt rename to app/src/main/java/com/example/livingai_lg/ui/login_legacy/SignInScreen.kt index 32d0cbb..3e9b26b 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/login/SignInScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/login_legacy/SignInScreen.kt @@ -1,4 +1,4 @@ -package com.example.livingai_lg.ui.login +package com.example.livingai_lg.ui.login_legacy import android.widget.Toast import androidx.compose.foundation.background @@ -14,7 +14,6 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight diff --git a/app/src/main/java/com/example/livingai_lg/ui/login/SignUpScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/login_legacy/SignUpScreen.kt similarity index 99% rename from app/src/main/java/com/example/livingai_lg/ui/login/SignUpScreen.kt rename to app/src/main/java/com/example/livingai_lg/ui/login_legacy/SignUpScreen.kt index da8f0e6..89e3fe1 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/login/SignUpScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/login_legacy/SignUpScreen.kt @@ -1,4 +1,4 @@ -package com.example.livingai_lg.ui.login +package com.example.livingai_lg.ui.login_legacy import android.widget.Toast import androidx.compose.foundation.background @@ -15,7 +15,6 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight diff --git a/app/src/main/java/com/example/livingai_lg/ui/login/SuccessScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/login_legacy/SuccessScreen.kt similarity index 95% rename from app/src/main/java/com/example/livingai_lg/ui/login/SuccessScreen.kt rename to app/src/main/java/com/example/livingai_lg/ui/login_legacy/SuccessScreen.kt index a5e23b2..364d4a0 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/login/SuccessScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/login_legacy/SuccessScreen.kt @@ -1,12 +1,10 @@ -package com.example.livingai_lg.ui.login +package com.example.livingai_lg.ui.login_legacy -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/Animal.kt b/app/src/main/java/com/example/livingai_lg/ui/models/Animal.kt index 1861b93..b6d6df8 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/models/Animal.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/models/Animal.kt @@ -25,31 +25,42 @@ data class Animal( val sampleAnimals = listOf( Animal( id = "1", - name = "Golden Retriever", - price = 80000, - imageUrl = listOf("https://api.builder.io/api/v1/image/assets/TEMP/8b3d82a183db9ded7741b4b8bf0cd81fb2733b89?width=686"), - distance = 2500L,// miles away - views = 94,//Views + name = "Rita", + age = 45, + breed = "Gir", + location = "Punjab", + distance = 12000, + imageUrl = listOf("https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2F4.bp.blogspot.com%2F_tecSnxaePMo%2FTLLVknW8dOI%2FAAAAAAAAACo%2F_kd1ZNBXU1o%2Fs1600%2FGIR%2CGujrat.jpg&f=1&nofb=1&ipt=da6ba1d040c396b64d3f08cc99998f66200dcd6c001e4a56def143ab3d1a87ea","https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fcpimg.tistatic.com%2F4478702%2Fb%2F4%2Fgir-cow.jpg&f=1&nofb=1&ipt=19bf391461480585c786d01433d863a383c60048ac2ce063ce91f173e215205d"), + views = 9001, + aiScore = 0.80f, + price = 120000, + isFairPrice = true, + rating = 4.7f, + ratingCount = 2076, sellerId = "1", sellerName = "Seller 1", - sellerType = "Wholeseller", - rating = 4.5f, - ratingCount = 2076, - description = "Friendly and energetic companion looking for an active family.", + description = "Premium Gir dairy cow in excellent health condition. High-yielding milk producer with consistent output and gentle temperament, making her ideal for commercial dairy operations or family farms.", + displayLocation = "Mediatek Pashu Mela, Marathalli, Bangalore (13 km)", + milkCapacity = 3.2f ), Animal( id = "2", - name = "Mudkip", - price = 999999999, - imageUrl = listOf("https://img.pokemondb.net/artwork/large/mudkip.jpg", "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fcdna.artstation.com%2Fp%2Fassets%2Fimages%2Fimages%2F012%2F524%2F022%2Flarge%2Fanna-ben-david-poster-size-mudkip.jpg%3F1535218851&f=1&nofb=1&ipt=aac92abbad440468461cbec5d9208d4258e5f4ed72e18a9ae17cfd6e0da1ce9f"), + name = "Sahi", + price = 95000, + age = 35, + breed = "Sahiwal", + location = "Punjab", + isFairPrice = true, + imageUrl = listOf("https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fcdnbbsr.s3waas.gov.in%2Fs3a5a61717dddc3501cfdf7a4e22d7dbaa%2Fuploads%2F2020%2F09%2F2020091812-1024x680.jpg&f=1&nofb=1&ipt=bb426406b3747e54151e4812472e203f33922fa3b4e11c4feef9aa59a5733146"), distance = 0L, - views = 100000, + views = 100, sellerId = "1", sellerName = "Seller ???", sellerType = "Wholeseller", rating = 5f, ratingCount = 2076, description = "Friendly and energetic companion looking for an active family.", + milkCapacity = 2.5f ), Animal( id = "3", diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/BottomNavItemData.kt b/app/src/main/java/com/example/livingai_lg/ui/models/BottomNavItemData.kt index d6501d0..e3a838b 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/models/BottomNavItemData.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/models/BottomNavItemData.kt @@ -13,15 +13,13 @@ val mainBottomNavItems = listOf( BottomNavItemData("Home", R.drawable.ic_home , AppScreen.BUY_ANIMALS), BottomNavItemData("Sell", R.drawable.ic_tag, AppScreen.CREATE_ANIMAL_LISTING), // TODO: - BottomNavItemData("Chats", R.drawable.ic_chat, AppScreen.CREATE_PROFILE), + BottomNavItemData("Chats", R.drawable.ic_chat, AppScreen.CHATS), BottomNavItemData("Services", R.drawable.ic_config, AppScreen.CREATE_PROFILE), BottomNavItemData("Mandi", R.drawable.ic_market, AppScreen.CREATE_PROFILE) ) val chatBottomNavItems = listOf( - BottomNavItemData("Home", R.drawable.ic_home ,"home"), - BottomNavItemData("Sell", R.drawable.ic_tag, "sell"), + BottomNavItemData("Contacts", R.drawable.ic_home ,"home"), + BottomNavItemData("Calls", R.drawable.ic_tag, "sell"), BottomNavItemData("Chats", R.drawable.ic_chat, "chats"), - BottomNavItemData("Services", R.drawable.ic_config, "services"), - BottomNavItemData("Mandi", R.drawable.ic_market, "mandi") -) + ) diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/Notifications.kt b/app/src/main/java/com/example/livingai_lg/ui/models/Notifications.kt new file mode 100644 index 0000000..fe2c9ce --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/models/Notifications.kt @@ -0,0 +1,37 @@ +package com.example.livingai_lg.ui.models + +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Bookmark +import androidx.compose.material.icons.filled.Chat +import androidx.compose.material.icons.filled.Notifications +import androidx.compose.material.icons.filled.Star + +data class AppNotification( + val id: String, + val title: String, + val icon: ImageVector +) + +val sampleNotifications = listOf( + AppNotification( + id = "1", + title = "Animal saved successfully", + icon = Icons.Default.Bookmark + ), + AppNotification( + id = "2", + title = "New message from Seller", + icon = Icons.Default.Chat + ), + AppNotification( + id = "3", + title = "Price dropped on an animal you viewed", + icon = Icons.Default.Notifications + ), + AppNotification( + id = "4", + title = "You received a new rating", + icon = Icons.Default.Star + ) +) diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/ProfileType.kt b/app/src/main/java/com/example/livingai_lg/ui/models/ProfileType.kt index 309245f..4ef9f87 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/models/ProfileType.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/models/ProfileType.kt @@ -1,13 +1,16 @@ package com.example.livingai_lg.ui.models import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource import com.example.livingai_lg.R data class ProfileType( val id: String, val title: String, - val icon: Int, - val backgroundColor: Color + val icon: Any, + val iconTint: Color = Color.Unspecified, + val backgroundColor: Color, ) val profileTypes = listOf( @@ -15,24 +18,28 @@ val profileTypes = listOf( id = "buyer_seller", title = "I'm a Buyer/Seller", icon = R.drawable.ic_shop, + iconTint = Color.White, backgroundColor = Color(0xFF9D4EDD) ), ProfileType( id = "wholesale_trader", title = "I'm a Wholesale Trader", icon = R.drawable.ic_bag, + iconTint = Color.White, backgroundColor = Color(0xFF3A86FF) ), ProfileType( id = "service_provider", title = "I'm a Service Provider", icon = R.drawable.ic_spanner, + iconTint = Color.White, backgroundColor = Color(0xFFFF5722) ), ProfileType( id = "mandi_host", title = "I'm a Mandi Host", icon = R.drawable.ic_shop2, + iconTint = Color.White, backgroundColor = Color(0xFF4CAF50) ) ) diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/User.kt b/app/src/main/java/com/example/livingai_lg/ui/models/User.kt index 4af6432..5d1131c 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/models/User.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/models/User.kt @@ -1,6 +1,7 @@ package com.example.livingai_lg.ui.models data class UserAddress( + val id: String, val name: String, val address: String, val isPrimary: Boolean = false @@ -18,11 +19,13 @@ val userProfile = profileImageUrl = "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fstatic.vecteezy.com%2Fsystem%2Fresources%2Fpreviews%2F014%2F016%2F808%2Fnon_2x%2Findian-farmer-face-vector.jpg&f=1&nofb=1&ipt=c352fec591428aebefe6cd263d2958765e85d4da69cce3c46b725ba2ff7d3448", addresses = listOf( UserAddress( + id = "1", name = "Home", address = "205 1st floor 7th cross 27th main, PW", isPrimary = true ), UserAddress( + id = "2", name = "Farm", address = "2nd block, MG Farms" ) diff --git a/app/src/main/java/com/example/livingai_lg/ui/navigation/AppNavigation.kt b/app/src/main/java/com/example/livingai_lg/ui/navigation/AppNavigation.kt index 140f78d..20b7b92 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/navigation/AppNavigation.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/navigation/AppNavigation.kt @@ -4,6 +4,9 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.Composable +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.navigation.NavController @@ -28,7 +31,8 @@ import com.example.livingai_lg.ui.screens.auth.LandingScreen import com.example.livingai_lg.ui.screens.auth.OtpScreen import com.example.livingai_lg.ui.screens.auth.SignInScreen import com.example.livingai_lg.ui.screens.auth.SignUpScreen - +import com.example.livingai_lg.ui.navigation.authNavGraph +import com.example.livingai_lg.ui.navigation.mainNavGraph object AppScreen { const val LANDING = "landing" @@ -55,6 +59,19 @@ object AppScreen { const val POST_SALE_SURVEY = "post_sale_survey" + const val SAVED_LISTINGS = "saved_listings" + + const val CONTACTS = "contacts" + + const val CALLS = "calls" + + const val CHAT = "chat" + + const val CHATS = "chats" + + fun chats(contact: String) = + "$CHAT/$contact" + fun otp(phone: String, name: String) = "$OTP/$phone/$name" @@ -74,23 +91,55 @@ object AppScreen { fun saleArchive(saleId: String) = "$SALE_ARCHIVE/$saleId" + } +object Graph { + const val AUTH = "auth" + const val MAIN = "auth" + + fun auth(route: String)= + "$AUTH/$route" + + fun main(route: String)= + "$MAIN/$route" +} @Composable fun AppNavigation( authState: AuthState ) { + val navController = rememberNavController() + var isLoggedIn = false; + when (authState) { - is AuthState.Unauthenticated -> {AuthNavGraph()} - is AuthState.Authenticated -> {MainNavGraph()} + is AuthState.Unauthenticated -> {isLoggedIn = false; } + is AuthState.Authenticated -> {isLoggedIn = true;} is AuthState.Unknown -> { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } -// AuthState.Loading -> SplashScreen() } -// val navController = rememberNavController() + + NavHost( + navController = navController, + startDestination = if (isLoggedIn) Graph.MAIN else Graph.AUTH + ) { + authNavGraph(navController) + mainNavGraph(navController) + } +// MainNavGraph(navController) +// AuthNavGraph(navController) +// when (authState) { +// is AuthState.Unauthenticated -> {AuthNavGraph()} +// is AuthState.Authenticated -> {MainNavGraph()} +// is AuthState.Unknown -> { +// Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { +// CircularProgressIndicator() +// } +// } +// AuthState.Loading -> SplashScreen() +// } // val onNavClick: (String) -> Unit = { route -> // val currentRoute = diff --git a/app/src/main/java/com/example/livingai_lg/ui/navigation/AuthNavGraph.kt b/app/src/main/java/com/example/livingai_lg/ui/navigation/AuthNavGraph.kt index 092b125..3ca3016 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/navigation/AuthNavGraph.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/navigation/AuthNavGraph.kt @@ -1,10 +1,13 @@ package com.example.livingai_lg.ui.navigation import androidx.compose.runtime.Composable +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable +import androidx.navigation.compose.navigation import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import com.example.livingai_lg.ui.screens.SaleArchiveScreen @@ -13,19 +16,16 @@ import com.example.livingai_lg.ui.screens.auth.OtpScreen import com.example.livingai_lg.ui.screens.auth.SignInScreen import com.example.livingai_lg.ui.screens.auth.SignUpScreen -@Composable -fun AuthNavGraph( - navController: NavHostController = rememberNavController() -) { - NavHost( - navController = navController, +fun NavGraphBuilder.authNavGraph(navController: NavController) { + navigation( + route = Graph.AUTH, startDestination = AppScreen.LANDING ) { composable(AppScreen.LANDING) { LandingScreen( onSignUpClick = { navController.navigate(AppScreen.SIGN_UP) }, onSignInClick = { navController.navigate(AppScreen.SIGN_IN) }, - onGuestClick = { navController.navigate(AppScreen.CREATE_PROFILE) } + onGuestClick = { navController.navigate(Graph.main(AppScreen.createProfile("guest"))) } ) } @@ -63,7 +63,8 @@ fun AuthNavGraph( OtpScreen( phoneNumber = backStackEntry.arguments?.getString("phoneNumber") ?: "", name = backStackEntry.arguments?.getString("name") ?: "", - onSuccess = { navController.navigate(AppScreen.chooseService("1"))} + onCreateProfile = {name -> navController.navigate(Graph.main(AppScreen.createProfile(name)))}, + onSuccess = { navController.navigate(Graph.auth(AppScreen.chooseService("1")))} ) } } diff --git a/app/src/main/java/com/example/livingai_lg/ui/navigation/MainNavGraph.kt b/app/src/main/java/com/example/livingai_lg/ui/navigation/MainNavGraph.kt index d5dcfb6..ecb63ca 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/navigation/MainNavGraph.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/navigation/MainNavGraph.kt @@ -1,13 +1,16 @@ package com.example.livingai_lg.ui.navigation -import androidx.compose.runtime.Composable -import androidx.navigation.NavHostController +import android.util.Log +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import androidx.navigation.compose.rememberNavController +import androidx.navigation.compose.navigation import androidx.navigation.navArgument -import com.example.livingai_lg.ui.login.SuccessScreen +import com.example.farmmarketplace.ui.screens.CallsScreen +import com.example.farmmarketplace.ui.screens.ContactsScreen +import com.example.livingai_lg.ui.models.profileTypes import com.example.livingai_lg.ui.screens.AnimalProfileScreen import com.example.livingai_lg.ui.screens.BuyScreen import com.example.livingai_lg.ui.screens.ChooseServiceScreen @@ -16,32 +19,39 @@ import com.example.livingai_lg.ui.screens.FilterScreen import com.example.livingai_lg.ui.screens.NewListingScreen import com.example.livingai_lg.ui.screens.PostSaleSurveyScreen import com.example.livingai_lg.ui.screens.SaleArchiveScreen +import com.example.livingai_lg.ui.screens.SavedListingsScreen import com.example.livingai_lg.ui.screens.SellerProfileScreen import com.example.livingai_lg.ui.screens.SortScreen +import com.example.livingai_lg.ui.screens.chat.ChatScreen +import com.example.livingai_lg.ui.screens.chat.ChatsScreen -@Composable -fun MainNavGraph( - navController: NavHostController = rememberNavController() -) { +fun NavGraphBuilder.mainNavGraph(navController: NavController) { val onNavClick: (String) -> Unit = { route -> val currentRoute = navController.currentBackStackEntry?.destination?.route + Log.d("Current Route:"," $currentRoute $route") if (currentRoute != route) { navController.navigate(route) { launchSingleTop = true - restoreState = true - popUpTo(navController.graph.startDestinationId) { - saveState = true - } + //restoreState = true +// popUpTo(navController.graph.startDestinationId) { +// saveState = true +// } } } } - NavHost( - navController = navController, - startDestination = AppScreen.BUY_ANIMALS - ) { + navigation( + route = Graph.MAIN, + startDestination = AppScreen.createProfile("guest") + ){ + + +// NavHost( +// navController = navController, +// startDestination = AppScreen.createProfile("guest") +// ) { composable( "${AppScreen.CREATE_PROFILE}/{name}", arguments = listOf(navArgument("name") { type = NavType.StringType }) @@ -55,12 +65,6 @@ fun MainNavGraph( ) } - - composable(AppScreen.CHOOSE_SERVICE) { - ChooseServiceScreen ( - onServiceSelected = { navController.navigate(AppScreen.LANDING) }, - ) - } composable( "${AppScreen.CHOOSE_SERVICE}/{profileId}", arguments = listOf(navArgument("profileId") { type = NavType.StringType }) @@ -82,8 +86,6 @@ fun MainNavGraph( ) }, onNavClick = onNavClick, - onFilterClick = {navController.navigate(AppScreen.BUY_ANIMALS_FILTERS)}, - onSortClick = {navController.navigate(AppScreen.BUY_ANIMALS_SORT)}, onSellerClick = { sellerId -> navController.navigate( AppScreen.sellerProfile(sellerId) @@ -92,20 +94,17 @@ fun MainNavGraph( ) } + composable(AppScreen.BUY_ANIMALS_FILTERS) { FilterScreen( - onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)}, onBackClick = { navController.popBackStack() }, - onSkipClick = { - navController.popBackStack() - }, + onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)}, onCancelClick = { navController.popBackStack() }, - - ) + ) } composable(AppScreen.BUY_ANIMALS_SORT) { @@ -114,9 +113,6 @@ fun MainNavGraph( onBackClick = { navController.popBackStack() }, - onSkipClick = { - navController.popBackStack() - }, onCancelClick = { navController.popBackStack() }, @@ -124,13 +120,25 @@ fun MainNavGraph( ) } + composable(AppScreen.SAVED_LISTINGS) { + SavedListingsScreen( + onNavClick = onNavClick, + onBackClick = { navController.popBackStack() }) + } + + composable(AppScreen.CREATE_ANIMAL_LISTING) { NewListingScreen ( onSaveClick = {navController.navigate( AppScreen.postSaleSurvey("2") )}, onBackClick = { - navController.popBackStack() + navController.navigate(AppScreen.BUY_ANIMALS){ + popUpTo(AppScreen.BUY_ANIMALS){ + inclusive = true + } + } + } ) } @@ -175,7 +183,7 @@ fun MainNavGraph( PostSaleSurveyScreen ( animalId = animalId, onBackClick = { - navController.popBackStack() + navController.navigate(AppScreen.CREATE_ANIMAL_LISTING) }, onSubmit = {navController.navigate( AppScreen.saleArchive("2") @@ -200,6 +208,7 @@ fun MainNavGraph( onBackClick = { navController.popBackStack() }, + onNavClick = onNavClick, onSellerClick = { sellerId -> navController.navigate( AppScreen.sellerProfile(sellerId) @@ -226,5 +235,51 @@ fun MainNavGraph( navController.popBackStack() } ) - }} + } + + composable(AppScreen.CONTACTS) { + ContactsScreen( + onBackClick = {navController.navigate(AppScreen.BUY_ANIMALS)},//{navController.popBackStack()}, + onTabClick = onNavClick, + ) + } + + composable(AppScreen.CALLS) { + CallsScreen( + onBackClick = {navController.navigate(AppScreen.BUY_ANIMALS)},//{navController.popBackStack()}, + onTabClick = onNavClick, + ) + } + + composable(AppScreen.CHATS) { + ChatsScreen( + onBackClick = {navController.navigate(AppScreen.BUY_ANIMALS)},//{navController.popBackStack()}, + onTabClick = onNavClick, + onChatItemClick = {navController.navigate(AppScreen.chats("2"))} + ) + } + + composable( + route = "${AppScreen.CHAT}/{contact}", + arguments = listOf( + navArgument("contact") { type = NavType.StringType } + ) + ) { backStackEntry -> + + val sellerId = backStackEntry + .arguments + ?.getString("contact") + ?: return@composable + + ChatScreen( + sellerId, + onBackClick = { + navController.navigate(AppScreen.CHATS) + //navController.popBackStack() + } + ) + } + } + + } diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/AnimalProfileScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/AnimalProfileScreen.kt index 13b84a7..892d625 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/screens/AnimalProfileScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/AnimalProfileScreen.kt @@ -1,5 +1,6 @@ package com.example.livingai_lg.ui.screens +import androidx.compose.foundation.Image import com.example.livingai_lg.ui.components.ImageCarousel import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.background @@ -9,11 +10,21 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.StarHalf +import androidx.compose.material.icons.filled.Bookmark +import androidx.compose.material.icons.filled.Star +import androidx.compose.material.icons.filled.StarHalf import androidx.compose.material3.Icon +import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow @@ -26,9 +37,12 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.zIndex import com.example.livingai_lg.ui.models.Animal import com.example.livingai_lg.ui.utils.formatPrice @@ -38,6 +52,11 @@ import com.example.livingai_lg.ui.components.FloatingActionBar import com.example.livingai_lg.ui.models.sampleAnimals import com.example.livingai_lg.ui.utils.formatAge import com.example.livingai_lg.R +import com.example.livingai_lg.ui.components.ActionPopup +import com.example.livingai_lg.ui.components.RatingStars +import com.example.livingai_lg.ui.navigation.AppScreen +import com.example.livingai_lg.ui.theme.AppTypography +import com.example.livingai_lg.ui.utils.formatDistance @Composable @@ -45,12 +64,15 @@ fun AnimalProfileScreen( animalId: String, onBackClick: () -> Unit = {}, onSellerClick: (sellerId: String) -> Unit = {}, + onNavClick: (route: String) -> Unit = {} ) { + var showSavedPopup by remember { mutableStateOf(false) } val animal = sampleAnimals.find { animal -> animal.id == animalId } ?: Animal(id = "null") Box( modifier = Modifier .fillMaxSize() .background(Color(0xFFF7F4EE)) + .padding(12.dp) ) { Column( modifier = Modifier @@ -62,7 +84,7 @@ fun AnimalProfileScreen( Box( modifier = Modifier .fillMaxWidth() - .height(500.dp) + .height(350.dp) .shadow(4.dp) ) { // Main image @@ -92,19 +114,31 @@ fun AnimalProfileScreen( Row( modifier = Modifier .align(Alignment.TopStart) - .padding(start = 16.dp, top = 16.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), + .padding(start = 5.dp, top = 5.dp) + .shadow( + elevation = 6.dp, + shape = RoundedCornerShape(50), + ambientColor = Color.Black.copy(alpha = 0.4f), + spotColor = Color.Black.copy(alpha = 0.4f) + ) + .background( + color = Color.Black.copy(alpha = 0.35f), // 👈 light but effective + shape = RoundedCornerShape(50) + ) + .padding(horizontal = 6.dp, vertical = 3.dp), + horizontalArrangement = Arrangement.spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically ) { Icon( painter = painterResource(R.drawable.ic_view), contentDescription = "Views", tint = Color.White, - modifier = Modifier.size(24.dp) + modifier = Modifier.size(16.dp) ) + Text( text = formatViews(animal.views), - fontSize = 12.sp, + fontSize = AppTypography.BodySmall, fontWeight = FontWeight.Normal, color = Color.White ) @@ -120,25 +154,41 @@ fun AnimalProfileScreen( ) { Text( text = "${animal.name}, ${formatAge(animal.age)}", - fontSize = 30.sp, + fontSize = AppTypography.Title, fontWeight = FontWeight.Normal, - color = Color.White + color = Color.White, + style = LocalTextStyle.current.copy( + shadow = Shadow( + color = Color.Black.copy(alpha = 0.75f), + offset = Offset(0f, 2f), + blurRadius = 6f + ) + ) ) Text( - text = "${animal.breed} • ${animal.location}, ${animal.distance}".uppercase(), - fontSize = 12.sp, + text = "${animal.breed} • ${animal.location}, ${formatDistance(animal.distance)}".uppercase(), + fontSize = AppTypography.Body, fontWeight = FontWeight.Normal, color = Color.White.copy(alpha = 0.7f), - letterSpacing = 1.2.sp + letterSpacing = 1.2.sp, + style = LocalTextStyle.current.copy( + shadow = Shadow( + color = Color.Black.copy(alpha = 0.6f), + offset = Offset(0f, 1.5f), + blurRadius = 4f + ) + ) ) + Spacer(modifier = Modifier.height(4.dp)) + } // AI Score badge (bottom left) Row( modifier = Modifier .align(Alignment.BottomStart) - .padding(start = 0.dp, bottom = 5.dp) - .height(48.dp) + .padding(start = 5.dp, bottom = 5.dp) + .height(60.dp) .border(2.dp, Color(0xFF717182), CircleShape) .padding(horizontal = 12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), @@ -147,7 +197,7 @@ fun AnimalProfileScreen( AIScoreCircle(score = animal.aiScore ?: 0f) Text( text = "AI Score", - fontSize = 18.sp, + fontSize = AppTypography.Body, fontWeight = FontWeight.Normal, color = Color.White ) @@ -159,11 +209,12 @@ fun AnimalProfileScreen( append("At Display: ") append(animal.displayLocation) }, - fontSize = 8.sp, + fontSize = AppTypography.Caption, fontWeight = FontWeight.Normal, - color = Color.White, + color = Color.White.copy(alpha = 0.7f), lineHeight = 13.sp, textDecoration = TextDecoration.Underline, + textAlign = TextAlign.Right, modifier = Modifier .align(Alignment.BottomEnd) .padding(end = 16.dp, bottom = 8.dp) @@ -190,8 +241,8 @@ fun AnimalProfileScreen( ) { Text( text = formatPrice(animal.price), - fontSize = 30.sp, - fontWeight = FontWeight.Medium, + fontSize = AppTypography.Title, + fontWeight = FontWeight.Bold, color = Color(0xFF0A0A0A) ) @@ -204,12 +255,12 @@ fun AnimalProfileScreen( Icon( painter = painterResource(R.drawable.ic_thumbs_up), contentDescription = "Fair Price", - tint = Color(0xFF0A0A0A), + tint = Color(0xFF00C950), modifier = Modifier.size(15.dp) ) Text( text = "Fair Price", - fontSize = 12.sp, + fontSize = AppTypography.Caption, fontWeight = FontWeight.Normal, color = Color(0xFF00C950) ) @@ -219,7 +270,7 @@ fun AnimalProfileScreen( Column( horizontalAlignment = Alignment.End, - verticalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(4.dp), modifier = Modifier.clickable( indication = LocalIndication.current, interactionSource = remember { MutableInteractionSource() } @@ -229,7 +280,7 @@ fun AnimalProfileScreen( { Text( text = "Sold By: ${animal.sellerName}", - fontSize = 16.sp, + fontSize = AppTypography.Body, fontWeight = FontWeight.Normal, color = Color(0xFF0A0A0A) ) @@ -239,19 +290,13 @@ fun AnimalProfileScreen( horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically ) { - repeat(5) { index -> - Icon( - painter = painterResource(R.drawable.ic_star), - contentDescription = null, - tint = if (index < (animal.rating ?: 0).toInt()) Color( - 0xFFDE9A07 - ) else Color(0xFFDE9A07), - modifier = Modifier.size(12.dp) - ) - } + RatingStars( + rating = animal.rating ?: 0f, + starSize = 12.dp + ) Text( text = "${animal.rating} (${animal.ratingCount} Ratings)", - fontSize = 10.sp, + fontSize = AppTypography.Caption, fontWeight = FontWeight.Normal, color = Color(0xFF0A0A0A) ) @@ -267,13 +312,13 @@ fun AnimalProfileScreen( ) { Text( text = "About", - fontSize = 16.sp, + fontSize = AppTypography.Body, fontWeight = FontWeight.Medium, color = Color(0xFF09090B).copy(alpha = 0.5f) ) Text( text = animal.description ?: "", - fontSize = 14.sp, + fontSize = AppTypography.BodySmall, fontWeight = FontWeight.Normal, color = Color(0xFF09090B), lineHeight = 24.sp @@ -297,24 +342,43 @@ fun AnimalProfileScreen( // Ad space banner AdSpaceBanner() - Spacer(modifier = Modifier.height(32.dp)) + Spacer(modifier = Modifier.height(64.dp)) } } FloatingActionBar( modifier = Modifier .align(Alignment.BottomCenter) - .padding(bottom = 24.dp) - .offset(y = (-20).dp) + .padding(bottom = 10.dp) + .offset(y = (-10).dp) .zIndex(10f), // 👈 ensure it floats above everything onChatClick = { /* TODO */ }, onCallClick = { /* TODO */ }, onLocationClick = { /* TODO */ }, - onBookmarkClick = { /* TODO */ } + onBookmarkClick = { showSavedPopup = true } ) + ActionPopup( + visible = showSavedPopup, + text = "Saved", + icon = Icons.Default.Bookmark, + backgroundColor = Color.Black, + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(end = 16.dp, bottom = 96.dp), + onClick = { + onNavClick(AppScreen.SAVED_LISTINGS) + // Navigate to saved items + }, + onDismiss = { + showSavedPopup = false + } + ) } + } +fun Modifier.Companion.align(bottomEnd: Alignment) {} + @Composable fun AIScoreCircle(score: Float) { Box( diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/BuyScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/BuyScreen.kt index d66fff0..db23513 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/screens/BuyScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/BuyScreen.kt @@ -1,12 +1,20 @@ package com.example.livingai_lg.ui.screens +import androidx.compose.foundation.Indication +import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Bookmark import androidx.compose.material3.Icon import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -25,6 +33,12 @@ import com.example.livingai_lg.ui.models.mainBottomNavItems import com.example.livingai_lg.ui.models.sampleAnimals import com.example.livingai_lg.ui.models.userProfile import com.example.livingai_lg.R +import com.example.livingai_lg.ui.components.ActionPopup +import com.example.livingai_lg.ui.components.AddressSelectorOverlay +import com.example.livingai_lg.ui.components.NotificationsOverlay +import com.example.livingai_lg.ui.components.SortOverlay +import com.example.livingai_lg.ui.models.sampleNotifications +import com.example.livingai_lg.ui.navigation.AppScreen @Composable fun BuyScreen( @@ -37,10 +51,13 @@ fun BuyScreen( ) { val selectedAnimalType = remember { mutableStateOf(null) } val isSaved = remember { mutableStateOf(false) } - - - - + var showAddressSelector by remember { mutableStateOf(false) } + var selectedAddressId by remember { mutableStateOf(userProfile.addresses.find { address -> address.isPrimary }?.id) } + val showFilterOverlay = remember { mutableStateOf(false) } + val showSortOverlay = remember { mutableStateOf(false) } + var showSavedPopup by remember { mutableStateOf(false) } + var showNotifications by remember { mutableStateOf(false) } + val sampleNotifications = sampleNotifications Box( modifier = Modifier @@ -74,10 +91,8 @@ fun BuyScreen( UserLocationHeader( user = userProfile, - onAddressSelected = { - // optional: reload listings, persist selection, etc. - }, - onAddNewClick = {} + onOpenAddressOverlay = { showAddressSelector = true }, + selectedAddressId = selectedAddressId?:"1", ) // Right-side actions (notifications, etc.) @@ -86,6 +101,10 @@ fun BuyScreen( contentDescription = "Notifications", tint = Color.Black, modifier = Modifier.size(24.dp) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ){ showNotifications = true } ) } // Row( @@ -129,8 +148,10 @@ fun BuyScreen( .padding(horizontal = 22.dp), horizontalArrangement = Arrangement.SpaceBetween ) { - SortButton(onSortClick) - FilterButton(onFilterClick) + SortButton( + onClick = { showSortOverlay.value = true } + ) + FilterButton(onClick = { showFilterOverlay.value = true }) } sampleAnimals.forEach { animal -> @@ -142,7 +163,8 @@ fun BuyScreen( isSaved = isSaved.value, onSavedChange = { isSaved.value = it }, onProductClick = { onProductClick(animal.id)}, - onSellerClick = onSellerClick + onSellerClick = onSellerClick, + onBookmarkClick = { showSavedPopup = true } ) Spacer(modifier = Modifier.height(16.dp)) @@ -160,6 +182,62 @@ fun BuyScreen( } } + AddressSelectorOverlay( + visible = showAddressSelector, + addresses = userProfile.addresses, + selectedAddressId = selectedAddressId, + onSelect = { addressId -> + selectedAddressId = addressId + showAddressSelector = false + }, + onClose = { showAddressSelector = false } + ) + + SortOverlay( + visible = showSortOverlay.value, + onApplyClick = { selected -> + // TODO: apply sort + }, + onDismiss = { showSortOverlay.value = false } + ) + + FilterOverlay( + visible = showFilterOverlay.value, + onDismiss = { showFilterOverlay.value = false }, + onSubmitClick = { + // apply filters + } + ) + + ActionPopup( + visible = showSavedPopup, + text = "Saved", + icon = Icons.Default.Bookmark, + backgroundColor = Color.Black, + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(end = 16.dp, bottom = 96.dp), + onClick = { + onNavClick(AppScreen.SAVED_LISTINGS) + // Navigate to saved items + }, + onDismiss = { + showSavedPopup = false + } + ) + + + NotificationsOverlay( + visible = showNotifications, + notifications = sampleNotifications, + onClose = { showNotifications = false }, + onDismiss = { id -> + // remove notification from list + }, + onNotificationClick = { id -> + // optional navigation + } + ) } } diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/ChooseServiceScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/ChooseServiceScreen.kt index 62cb862..146940d 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/screens/ChooseServiceScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/ChooseServiceScreen.kt @@ -27,10 +27,13 @@ import androidx.compose.ui.unit.sp import com.example.livingai_lg.ui.components.OptionCard import com.example.livingai_lg.ui.components.backgrounds.StoreBackground import com.example.livingai_lg.R +import com.example.livingai_lg.ui.theme.AppTypography + data class ServiceType( val id: String, val title: String, - val icon: Int, + val icon: Any, + val iconTint: Color, val backgroundColor: Color ) @@ -46,30 +49,35 @@ fun ChooseServiceScreen( id = "transport", title = "Transport", icon = R.drawable.ic_shop, + iconTint = Color.White, backgroundColor = Color(0xFF9D4EDD) ), ServiceType( id = "vet", title = "Vet", icon = R.drawable.ic_bag, + iconTint = Color.White, backgroundColor = Color(0xFF3A86FF) ), ServiceType( id = "feed_supplier", title = "Feed Supplier", icon = R.drawable.ic_spanner, + iconTint = Color.White, backgroundColor = Color(0xFFFF5722) ), ServiceType( id = "medicine_supplier", title = "Medicine Supplier", icon = R.drawable.ic_shop2, + iconTint = Color.White, backgroundColor = Color(0xFF4CAF50) ), ServiceType( id = "other", title = "Other", icon = R.drawable.ic_other, + iconTint = Color.White, backgroundColor = Color(0xFFD4A942) ) ) @@ -101,7 +109,7 @@ fun ChooseServiceScreen( ) { Text( text = "Choose Service", - fontSize = 36.sp, + fontSize = AppTypography.Display, fontWeight = FontWeight.Medium, color = Color(0xFF0A0A0A) ) @@ -109,8 +117,8 @@ fun ChooseServiceScreen( Spacer(modifier = Modifier.height(8.dp)) Text( - text = "Choose Service**", - fontSize = 16.sp, + text = "Choose a Service", + fontSize = AppTypography.Body, fontWeight = FontWeight.Medium, color = Color(0xFF0A0A0A).copy(alpha = 0.8f) ) @@ -129,6 +137,7 @@ fun ChooseServiceScreen( OptionCard( label = service.title, icon = service.icon, + iconTint = service.iconTint, iconBackgroundColor = service.backgroundColor, onClick = { selectedService.value = service.id @@ -144,84 +153,3 @@ fun ChooseServiceScreen( } } -@Composable -fun ServiceTypeCard( - service: ServiceType, - isSelected: Boolean, - onClick: () -> Unit -) { - Box( - modifier = Modifier - .fillMaxWidth() - .height(90.dp) - .shadow(2.dp, RoundedCornerShape(16.dp)) - .background(Color.White, RoundedCornerShape(16.dp)) - .border(1.dp, Color(0xFFF3F4F6), RoundedCornerShape(16.dp)) - .clickable( - indication = LocalIndication.current, - interactionSource = remember { MutableInteractionSource() }, - onClick = onClick - ) - .padding(12.dp) - ) { - Row( - modifier = Modifier - .fillMaxSize(), - horizontalArrangement = Arrangement.spacedBy(16.dp), - verticalAlignment = Alignment.CenterVertically - ) { - // Icon container - Box( - modifier = Modifier - .size(56.dp) - .background(service.backgroundColor, RoundedCornerShape(14.dp)), - contentAlignment = Alignment.Center - ) { - when (service.id) { - "transport" -> - Icon( - painter = painterResource(id = R.drawable.ic_shop), - contentDescription = "Transport", - ) - - "vet" -> Icon( - painter = painterResource(id = R.drawable.ic_bag), - contentDescription = "Vet", - ) - "feed_supplier" -> Icon( - painter = painterResource(id = R.drawable.ic_spanner), - contentDescription = "Feed Supplier", - ) - "medicine_supplier" -> Icon( - painter = painterResource(id = R.drawable.ic_shop2), - contentDescription = "Medicine Supplier", - ) - "other" -> Icon( - painter = painterResource(id = R.drawable.ic_other), - contentDescription = "Other", - ) - } - } - - // Title - Text( - text = service.title, - fontSize = 16.sp, - fontWeight = FontWeight.Normal, - color = Color(0xFF0A0A0A), - modifier = Modifier.weight(1f) - ) - - // Radio button - RadioButton( - selected = isSelected, - onClick = onClick, - colors = RadioButtonDefaults.colors( - selectedColor = Color(0xFF6B7280), - unselectedColor = Color(0xFF6B7280) - ) - ) - } - } -} - diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/CreateProfileScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/CreateProfileScreen.kt index 25511bc..7bc88ac 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/screens/CreateProfileScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/CreateProfileScreen.kt @@ -5,6 +5,8 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Notifications import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf @@ -24,6 +26,7 @@ import com.example.livingai_lg.ui.components.OptionCard import com.example.livingai_lg.ui.components.backgrounds.StoreBackground import com.example.livingai_lg.ui.models.profileTypes +import com.example.livingai_lg.ui.theme.AppTypography import kotlinx.coroutines.launch @@ -75,7 +78,7 @@ fun CreateProfileScreen( ) { Text( text = "Create Profile", - fontSize = 36.sp, + fontSize = AppTypography.Display, fontWeight = FontWeight.Medium, color = Color(0xFF0A0A0A) ) @@ -84,7 +87,7 @@ fun CreateProfileScreen( Text( text = "Choose Profile Type", - fontSize = 16.sp, + fontSize = AppTypography.Body, fontWeight = FontWeight.Medium, color = Color(0xFF0A0A0A).copy(alpha = 0.8f) ) @@ -105,7 +108,8 @@ fun CreateProfileScreen( icon = profile.icon, iconBackgroundColor = profile.backgroundColor, onClick = { - updateProfile(profile.id) + onProfileSelected(profile.id) + //updateProfile(profile.id) } ) } diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/FilterScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/FilterScreen.kt index 5f4c9d6..0cf3291 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/screens/FilterScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/FilterScreen.kt @@ -1,5 +1,9 @@ package com.example.livingai_lg.ui.screens +import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.background @@ -24,14 +28,14 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.example.livingai_lg.ui.components.DropdownInput import com.example.livingai_lg.ui.components.RangeFilter +import com.example.livingai_lg.ui.theme.AppTypography @OptIn(ExperimentalMaterial3Api::class) @Composable fun FilterScreen( onBackClick: () -> Unit = {}, - onSkipClick: () -> Unit = {}, onSubmitClick: () -> Unit = {}, - onCancelClick: () -> Unit = {} + onCancelClick: () -> Unit = {}, ) { var selectedAnimal by remember { mutableStateOf("") } var selectedBreed by remember { mutableStateOf("") } @@ -104,16 +108,6 @@ fun FilterScreen( color = Color.Black ) } - - TextButton(onClick = onSkipClick) { - Text( - text = "Skip", - fontSize = 24.sp, - fontWeight = FontWeight.Normal, - color = Color(0xFF4A5565), - textDecoration = TextDecoration.Underline - ) - } } } @@ -122,7 +116,7 @@ fun FilterScreen( modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) - .padding(horizontal = 16.dp) + .padding(horizontal = 32.dp) .padding(bottom = 24.dp), verticalArrangement = Arrangement.spacedBy(24.dp) ) { @@ -130,117 +124,43 @@ fun FilterScreen( Column( verticalArrangement = Arrangement.spacedBy(8.dp) ) { - Text( - text = "Animal", - fontSize = 16.sp, - fontWeight = FontWeight.Normal, - color = Color.Black - ) - - ExposedDropdownMenuBox( + DropdownInput( + label = "Animal", + selected = selectedAnimal, + options = listOf("Cow", "Buffalo", "Goat", "Sheep"), expanded = animalExpanded, - onExpandedChange = { animalExpanded = it } - ) { - OutlinedTextField( - value = selectedAnimal, - onValueChange = {}, - readOnly = true, - placeholder = { - Text( - "Select Animal", - color = Color(0xFF717182), - fontSize = 16.sp - ) - }, - modifier = Modifier - .fillMaxWidth() - .menuAnchor(), - shape = RoundedCornerShape(8.dp), - colors = OutlinedTextFieldDefaults.colors( - unfocusedContainerColor = Color.White, - focusedContainerColor = Color.White, - unfocusedBorderColor = Color(0x1A000000), - focusedBorderColor = Color(0x1A000000) - ) - ) - ExposedDropdownMenu( - expanded = animalExpanded, - onDismissRequest = { animalExpanded = false } - ) { - listOf("Cow", "Buffalo", "Goat", "Sheep").forEach { animal -> - DropdownMenuItem( - text = { Text(animal) }, - onClick = { - selectedAnimal = animal - animalExpanded = false - } - ) - } - } - } + onExpandedChange = { animalExpanded = it }, + onSelect = { item -> + selectedAnimal = item + animalExpanded = false + }, + placeholder = "Select Animal", // <--- half width + ) } // Breed Section Column( verticalArrangement = Arrangement.spacedBy(8.dp) ) { - Text( - text = "Breed", - fontSize = 16.sp, - fontWeight = FontWeight.Normal, - color = Color.Black - ) - - ExposedDropdownMenuBox( + DropdownInput( + label = "Breed", + selected = selectedBreed, + options = listOf("Holstein", "Jersey", "Gir", "Sahiwal"), expanded = breedExpanded, - onExpandedChange = { breedExpanded = it } - ) { - OutlinedTextField( - value = selectedBreed, - onValueChange = {}, - readOnly = true, - placeholder = { - Text( - "Select Breed", - color = Color(0xFF717182), - fontSize = 16.sp - ) - }, - modifier = Modifier - .fillMaxWidth() - .menuAnchor(), - shape = RoundedCornerShape(8.dp), - colors = OutlinedTextFieldDefaults.colors( - unfocusedContainerColor = Color.White, - focusedContainerColor = Color.White, - unfocusedBorderColor = Color(0x1A000000), - focusedBorderColor = Color(0x1A000000) - ) - ) - ExposedDropdownMenu( - expanded = breedExpanded, - onDismissRequest = { breedExpanded = false } - ) { - listOf("Holstein", "Jersey", "Gir", "Sahiwal").forEach { breed -> - DropdownMenuItem( - text = { Text(breed) }, - onClick = { - selectedBreed = breed - breedExpanded = false - } - ) - } - } - } + onExpandedChange = { breedExpanded = it }, + onSelect = { item -> + selectedBreed = item + breedExpanded = false + }, + placeholder = "Select Breed", // <--- half width + ) } // Price and Age Row - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(16.dp) // 👈 reduce spacing - ) { + Column( - modifier = Modifier.weight(1f) + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { RangeFilter( modifier = Modifier.fillMaxWidth(), // 👈 important @@ -257,7 +177,8 @@ fun FilterScreen( } Column( - modifier = Modifier.weight(1f) + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { RangeFilter( modifier = Modifier.fillMaxWidth(), @@ -273,17 +194,13 @@ fun FilterScreen( } ) } - } + // Distance and Gender Row - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(64.dp) - ) { + // Distance Section Column( - modifier = Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(3.dp) + verticalArrangement = Arrangement.spacedBy(8.dp) ) { DropdownInput( label = "Distance", @@ -301,8 +218,7 @@ fun FilterScreen( // Gender Section Column( - modifier = Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(3.dp) + verticalArrangement = Arrangement.spacedBy(8.dp) ) { DropdownInput( label = "Gender", @@ -319,7 +235,7 @@ fun FilterScreen( } - } + // Pregnancy Status Section Column( @@ -327,7 +243,7 @@ fun FilterScreen( ) { Text( text = "Pregnancy Status", - fontSize = 16.sp, + fontSize = AppTypography.Body, fontWeight = FontWeight.SemiBold, color = Color(0xFF364153) ) @@ -352,12 +268,9 @@ fun FilterScreen( } } - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(16.dp) // 👈 reduce spacing - ) { + Column( - modifier = Modifier.weight(1f) + verticalArrangement = Arrangement.spacedBy(8.dp) ) { RangeFilter( modifier = Modifier.fillMaxWidth(), // 👈 important @@ -374,7 +287,7 @@ fun FilterScreen( } Column( - modifier = Modifier.weight(1f) + verticalArrangement = Arrangement.spacedBy(8.dp) ) { RangeFilter( modifier = Modifier.fillMaxWidth(), @@ -390,7 +303,7 @@ fun FilterScreen( } ) } - } + // Calving Number Section Column( @@ -469,7 +382,7 @@ fun FilterScreen( ) { Text( text = "Submit", - fontSize = 16.sp, + fontSize = AppTypography.Body, fontWeight = FontWeight.Normal ) } @@ -492,7 +405,7 @@ fun FilterScreen( ) { Text( text = "Cancel", - fontSize = 16.sp, + fontSize = AppTypography.Body, fontWeight = FontWeight.Normal ) } diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/NewListingScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/NewListingScreen.kt index 4792fd5..dc5ac7f 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/screens/NewListingScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/NewListingScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.example.livingai_lg.ui.components.MediaPickerCard import com.example.livingai_lg.ui.models.NewListingFormState +import com.example.livingai_lg.ui.theme.AppTypography @OptIn(ExperimentalMaterial3Api::class) @@ -56,7 +57,7 @@ fun NewListingScreen( title = { Text( text = "New Listing", - fontSize = 24.sp, + fontSize = AppTypography.Display, fontWeight = FontWeight.Medium, color = Color(0xFF0A0A0A) ) diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/NotificationsScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/NotificationsScreen.kt new file mode 100644 index 0000000..f3d11d2 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/NotificationsScreen.kt @@ -0,0 +1,165 @@ +package com.example.livingai_lg.ui.screens + +import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +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.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Notifications +import androidx.compose.material3.Divider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.example.livingai_lg.ui.models.AppNotification + +@Composable +fun NotificationsScreen( + notifications: List, + onBackClick: () -> Unit, + onDismiss: (String) -> Unit, + onNotificationClick: (String) -> Unit = {}, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier + .fillMaxSize() + .background(Color(0xFFF7F4EE)) + ) { + + // Header + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + IconButton(onClick = onBackClick) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back", + tint = Color.Black + ) + } + + Spacer(modifier = Modifier.width(8.dp)) + + Text( + text = "Notifications", + fontSize = 24.sp, + fontWeight = FontWeight.Normal, + color = Color.Black + ) + } + + Divider(color = Color(0xFFE5E7EB)) + + if (notifications.isEmpty()) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text( + text = "No notifications", + fontSize = 14.sp, + color = Color(0xFF6B7280) + ) + } + } else { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + items( + items = notifications, + key = { it.id } + ) { notification -> + NotificationItem( + notification = notification, + onDismiss = onDismiss, + onClick = onNotificationClick + ) + } + } + } + } +} + +@Composable +fun NotificationItem( + notification: AppNotification, + onDismiss: (String) -> Unit, + onClick: (String) -> Unit = {} +) { + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.White, RoundedCornerShape(14.dp)) + .border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(14.dp)) + .clickable { onClick(notification.id) } + .padding(horizontal = 16.dp, vertical = 14.dp), + verticalAlignment = Alignment.CenterVertically + ) { + + Icon( + imageVector = notification.icon, + contentDescription = null, + tint = Color(0xFF0A0A0A), + modifier = Modifier.size(22.dp) + ) + + Spacer(modifier = Modifier.width(12.dp)) + + Text( + text = notification.title, + fontSize = 14.sp, + color = Color(0xFF1F2937), + modifier = Modifier.weight(1f) + ) + + IconButton( + onClick = { onDismiss(notification.id) }, + modifier = Modifier.size(20.dp) + ) { + Icon( + imageVector = Icons.Default.Close, + contentDescription = "Dismiss", + tint = Color(0xFF6B7280), + modifier = Modifier.size(16.dp) + ) + } + } +} + diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/PostSaleSurvey.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/PostSaleSurvey.kt index ee28d9e..d86eba1 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/screens/PostSaleSurvey.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/PostSaleSurvey.kt @@ -94,18 +94,6 @@ fun PostSaleSurveyScreen( interactionSource = remember { MutableInteractionSource() } ) { onBackClick() } ) - - Text( - text = "Skip", - fontSize = 24.sp, - fontWeight = FontWeight.Medium, - color = Color(0xFF4A5565), - textDecoration = TextDecoration.Underline, - modifier = Modifier.clickable( - indication = LocalIndication.current, - interactionSource = remember { MutableInteractionSource() } - ) { onSkipClick() } - ) } Spacer(modifier = Modifier.height(26.dp)) diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/SavedListingsScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/SavedListingsScreen.kt new file mode 100644 index 0000000..96b9116 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/SavedListingsScreen.kt @@ -0,0 +1,266 @@ +package com.example.livingai_lg.ui.screens + +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil.compose.AsyncImage +import com.example.livingai_lg.ui.components.AdSpaceBanner +import com.example.livingai_lg.ui.layout.BottomNavScaffold +import com.example.livingai_lg.ui.models.mainBottomNavItems +import com.example.livingai_lg.ui.theme.AppTypography + +data class SavedListing( + val id: String, + val name: String, + val age: String, + val breed: String, + val price: String, + val views: String, + val distance: String, + val imageUrl: String +) + +enum class BottomNavTab { + HOME, + SELL, + CHATS, + SERVICES, + MANDI +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SavedListingsScreen( + onListingClick: (String) -> Unit = {}, + onNavClick: (route: String) -> Unit = {}, + onBackClick: () -> Unit = {} +) { + val savedListings = listOf( + SavedListing( + id = "1", + name = "Eauri", + age = "Age: 3 yrs", + breed = "Holstein", + price = "₹45,000", + views = "124 views", + distance = "Distance: 12 km", + imageUrl = "https://api.builder.io/api/v1/image/assets/TEMP/db216f496f6d5fd681db9afc8006eb8da3164c17?width=192" + ), + SavedListing( + id = "2", + name = "Fauri", + age = "Age: 2 yrs", + breed = "Jersey", + price = "₹38,000", + views = "89 views", + distance = "Distance: 17 km", + imageUrl = "https://api.builder.io/api/v1/image/assets/TEMP/a49aa5540b0cb6bf76ba3aa99cb8d4f94835e8ee?width=192" + ), + SavedListing( + id = "3", + name = "Gauri", + age = "Age: 4 yrs", + breed = "Gir", + price = "₹52,000", + views = "156 views", + distance = "Distance: 20 km", + imageUrl = "https://api.builder.io/api/v1/image/assets/TEMP/26fd38c965180ef066862fb1d0aa6df040a0d389?width=192" + ), + SavedListing( + id = "4", + name = "Hauri", + age = "Age: 1.5 yrs", + breed = "Sahiwal", + price = "₹28,000", + views = "67 views", + distance = "Distance: 6 km", + imageUrl = "https://api.builder.io/api/v1/image/assets/TEMP/b8edfd48d908a8e0eeeee55745ab409e454be25f?width=192" + ) + ) + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF7F4EE)) + ) { + BottomNavScaffold( + items = mainBottomNavItems, + currentItem = "Home", + onBottomNavItemClick = onNavClick, + ) { paddingValues -> + + + Column( + modifier = Modifier + .fillMaxSize() + .padding(bottom = 72.dp) + ) { + TopAppBar( + title = { + Text( + text = "Saved Listings", + fontSize = AppTypography.Display, + fontWeight = FontWeight.Medium, + color = Color(0xFF0A0A0A) + ) + }, + navigationIcon = { + IconButton(onClick = onBackClick) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = "Back", + tint = Color(0xFF0A0A0A) + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = Color(0xFFF7F4EE) + ) + ) + + LazyColumn( + modifier = Modifier.fillMaxWidth(), + contentPadding = PaddingValues(horizontal = 15.dp), + verticalArrangement = Arrangement.spacedBy(14.dp) + ) { + item { + AdSpaceBanner() + } + + items(savedListings) { listing -> + ListingCard( + listing = listing, + onClick = { onListingClick(listing.id) } + ) + } + } + } + } + } +} + +@Composable +fun ListingCard( + listing: SavedListing, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier + .fillMaxWidth() + .height(138.dp) + .border( + width = 1.078.dp, + color = Color(0xFFF3F4F6).copy(alpha = 0.5f), + shape = RoundedCornerShape(24.dp) + ) + .background(Color(0xFFFCFBF8), RoundedCornerShape(24.dp)) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() }, + onClick = onClick + ) + .padding(horizontal = 16.dp, vertical = 21.dp), + horizontalArrangement = Arrangement.spacedBy(20.dp), + verticalAlignment = Alignment.CenterVertically + ) { + AsyncImage( + model = listing.imageUrl, + contentDescription = listing.name, + modifier = Modifier + .size(96.dp) + .clip(CircleShape), + contentScale = ContentScale.Crop + ) + + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = listing.name, + fontSize = 16.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF1E2939), + lineHeight = 24.sp + ) + + Text( + text = listing.age, + fontSize = 14.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF4A5565), + lineHeight = 20.sp + ) + + Text( + text = listing.breed, + fontSize = 14.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF4A5565), + lineHeight = 20.sp + ) + } + + Column( + horizontalAlignment = Alignment.End, + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = listing.price, + fontSize = 16.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF1E2939), + lineHeight = 24.sp, + textAlign = TextAlign.End + ) + + Text( + text = listing.views, + fontSize = 12.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF6A7282), + lineHeight = 16.sp, + textAlign = TextAlign.End + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = listing.distance, + fontSize = 12.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF4A5565), + lineHeight = 20.sp + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/SortScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/SortScreen.kt index 57043e2..23db146 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/screens/SortScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/SortScreen.kt @@ -1,19 +1,29 @@ package com.example.livingai_lg.ui.screens +import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight 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.widthIn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults @@ -39,12 +49,10 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.example.livingai_lg.ui.components.SortItem - @OptIn(ExperimentalMaterial3Api::class) @Composable fun SortScreen( onBackClick: () -> Unit = {}, - onSkipClick: () -> Unit = {}, onApplyClick: (List) -> Unit = {}, onCancelClick: () -> Unit = {} ) { @@ -103,18 +111,14 @@ fun SortScreen( ) { Row(verticalAlignment = Alignment.CenterVertically) { IconButton(onClick = onBackClick) { - Icon(Icons.Default.ArrowBack, null) + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = "Back", + tint = Color(0xFF0A0A0A) + ) } Text("Sort By", fontSize = 32.sp) } - - TextButton(onClick = onSkipClick) { - Text( - "Skip", - textDecoration = TextDecoration.Underline, - fontSize = 20.sp - ) - } } Column( diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignInScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignInScreen.kt index bfa0e4d..91270d6 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignInScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignInScreen.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.launch @Composable fun SignInScreen( onSignUpClick: () -> Unit = {}, - onSignInClick: (phone: String, name: String) -> Unit = {}, + onSignInClick: (phone: String, name: String) -> Unit = {_,_->}, ) { val phoneNumber = remember { mutableStateOf("") } val isValid = phoneNumber.value.length == 10 diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignUpScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignUpScreen.kt index e45d56a..ea4dd49 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignUpScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignUpScreen.kt @@ -52,7 +52,7 @@ private fun String.isValidPhoneNumber(): Boolean { @Composable fun SignUpScreen( onSignInClick: () -> Unit = {}, - onSignUpClick: (phone: String, name: String) -> Unit = {} + onSignUpClick: (phone: String, name: String) -> Unit = {_,_->} ) { var formData by remember { mutableStateOf(SignUpFormData()) } diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/chat/CallsScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/chat/CallsScreen.kt new file mode 100644 index 0000000..a2465e0 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/chat/CallsScreen.kt @@ -0,0 +1,327 @@ +package com.example.farmmarketplace.ui.screens + +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.CallMade +import androidx.compose.material.icons.automirrored.filled.CallMissed +import androidx.compose.material.icons.automirrored.filled.CallReceived +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +data class CallRecord( + val id: String, + val name: String, + val initials: String, + val callType: CallType, + val duration: String, + val timestamp: String, + val isVideoCall: Boolean = false +) + +enum class CallType { + INCOMING, + OUTGOING, + MISSED +} + +@Composable +fun CallsScreen( + onBackClick: () -> Unit = {}, + onCallClick: () -> Unit = {}, + onMenuClick: () -> Unit = {}, + onCallItemClick: (String) -> Unit = {}, + onTabClick: (route: String) -> Unit = {} +) { + val callHistory = listOf( + CallRecord( + id = "1", + name = "Farmer Kumar", + initials = "FK", + callType = CallType.INCOMING, + duration = "5m 23s", + timestamp = "Today, 2:30 PM" + ), + CallRecord( + id = "2", + name = "Seller Raj", + initials = "SR", + callType = CallType.OUTGOING, + duration = "2m 10s", + timestamp = "Today, 11:45 AM" + ), + CallRecord( + id = "3", + name = "Buyer Priya", + initials = "BP", + callType = CallType.MISSED, + duration = "", + timestamp = "Yesterday, 8:15 PM" + ), + CallRecord( + id = "4", + name = "Seller 1", + initials = "S1", + callType = CallType.OUTGOING, + duration = "8m 45s", + timestamp = "Yesterday, 5:30 PM", + isVideoCall = true + ), + CallRecord( + id = "5", + name = "Veterinarian", + initials = "V", + callType = CallType.INCOMING, + duration = "12m 30s", + timestamp = "2 days ago" + ) + ) + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFFCFBF8)) + ) { + Column( + modifier = Modifier.fillMaxSize() + ) { + CallsHeader( + onBackClick = onBackClick, + onCallClick = onCallClick, + onMenuClick = onMenuClick + ) + + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + .padding(horizontal = 16.dp, vertical = 16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + items(callHistory) { call -> + CallHistoryItem( + call = call, + onClick = { onCallItemClick(call.id) } + ) + } + } + + ContactsBottomNav( + currentTab = ContactsTab.CALLS, + onTabClick = onTabClick + ) + } + } +} + +@Composable +fun CallsHeader( + onBackClick: () -> Unit, + onCallClick: () -> Unit, + onMenuClick: () -> Unit, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier + .fillMaxWidth() + .height(65.dp) + .border( + width = 1.078.dp, + color = Color(0xFF000000).copy(alpha = 0.1f) + ) + .background(Color(0xFFFCFBF8)) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 14.dp, vertical = 10.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back", + tint = Color(0xFF0A0A0A), + modifier = Modifier + .size(26.dp) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onBackClick() } + ) + + Text( + text = "Calls", + fontSize = 24.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF0A0A0A), + lineHeight = 42.sp + ) + } + + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Phone, + contentDescription = "New Call", + tint = Color(0xFF0A0A0A), + modifier = Modifier + .size(20.dp) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onCallClick() } + ) + + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = "Menu", + tint = Color(0xFF0A0A0A), + modifier = Modifier + .size(20.dp) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onMenuClick() } + ) + } + } + } +} + +@Composable +fun CallHistoryItem( + call: CallRecord, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier + .fillMaxWidth() + .height(76.dp) + .border( + width = 1.078.dp, + color = Color(0xFF000000).copy(alpha = 0.1f), + shape = RoundedCornerShape(12.dp) + ) + .background(Color(0xFFFCFBF8), RoundedCornerShape(12.dp)) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onClick() } + .padding(horizontal = 16.dp, vertical = 14.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.weight(1f) + ) { + Box( + modifier = Modifier + .size(48.dp) + .background(Color(0xFFE5E7EB), CircleShape), + contentAlignment = Alignment.Center + ) { + Text( + text = call.initials, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF0A0A0A), + lineHeight = 21.sp + ) + } + + Column( + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = call.name, + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF1E2939), + lineHeight = 20.sp, + letterSpacing = (-0.312).sp + ) + + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = when (call.callType) { + CallType.INCOMING -> Icons.AutoMirrored.Filled.CallReceived + CallType.OUTGOING -> Icons.AutoMirrored.Filled.CallMade + CallType.MISSED -> Icons.AutoMirrored.Filled.CallMissed + }, + contentDescription = call.callType.name, + tint = when (call.callType) { + CallType.INCOMING -> Color(0xFF00A63E) + CallType.OUTGOING -> Color(0xFF155DFC) + CallType.MISSED -> Color(0xFFE7000B) + }, + modifier = Modifier.size(20.dp) + ) + + Text( + text = when (call.callType) { + CallType.INCOMING -> "Incoming • ${call.duration}" + CallType.OUTGOING -> "Outgoing • ${call.duration}" + CallType.MISSED -> "Missed" + }, + fontSize = 12.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF717182), + lineHeight = 16.sp + ) + } + } + } + + Column( + horizontalAlignment = Alignment.End, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = call.timestamp, + fontSize = 12.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF717182), + lineHeight = 16.sp + ) + + Icon( + imageVector = if (call.isVideoCall) Icons.Default.Videocam else Icons.Default.Phone, + contentDescription = if (call.isVideoCall) "Video Call" else "Voice Call", + tint = Color(0xFF030213), + modifier = Modifier.size(24.dp) + ) + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/chat/ChatScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/chat/ChatScreen.kt new file mode 100644 index 0000000..c6744d7 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/chat/ChatScreen.kt @@ -0,0 +1,365 @@ +package com.example.livingai_lg.ui.screens.chat + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.Send +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Attachment +import androidx.compose.material.icons.filled.KeyboardVoice +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material.icons.filled.Phone +import androidx.compose.material.icons.filled.Send +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.StrokeJoin +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +data class ChatMessage( + val text: String, + val timestamp: String, + val isSentByCurrentUser: Boolean +) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ChatScreen( + sellerName: String = "Seller 1", + sellerStatus: String = "Online", + onBackClick: () -> Unit = {}, + onPhoneClick: () -> Unit = {}, + onMenuClick: () -> Unit = {} +) { + var messageText by remember { mutableStateOf("") } + + val messages = remember { + listOf( + ChatMessage( + text = "Hello! Yes, the cow is still available. Would you like to visit?", + timestamp = "10:30 AM", + isSentByCurrentUser = false + ), + ChatMessage( + text = "Hey is she still available", + timestamp = "10:28 AM", + isSentByCurrentUser = true + ), + ChatMessage( + text = "She's a healthy Gir cow, 2 years old, great milk yield.", + timestamp = "10:31 AM", + isSentByCurrentUser = false + ), + ChatMessage( + text = "What's the price?", + timestamp = "10:32 AM", + isSentByCurrentUser = true + ), + ChatMessage( + text = "₹45,000. Negotiable if you visit today.", + timestamp = "10:33 AM", + isSentByCurrentUser = false + ) + ) + } + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFF8F8F8)) + + ) { + Column( + modifier = Modifier.fillMaxSize() + ) { + ChatHeader( + sellerName = sellerName, + sellerStatus = sellerStatus, + onBackClick = onBackClick, + onPhoneClick = onPhoneClick, + onMenuClick = onMenuClick + ) + + LazyColumn( + modifier = Modifier + .weight(1f) + .fillMaxWidth() + .padding(horizontal = 16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + contentPadding = PaddingValues(vertical = 16.dp) + ) { + items(messages) { message -> + MessageBubble(message = message) + } + } + + MessageInputBar( + messageText = messageText, + onMessageChange = { messageText = it }, + onSendClick = { + messageText = "" + } + ) + } + } +} + +@Composable +fun ChatHeader( + sellerName: String, + sellerStatus: String, + onBackClick: () -> Unit, + onPhoneClick: () -> Unit, + onMenuClick: () -> Unit +) { + Surface( + modifier = Modifier + .fillMaxWidth() + .height(65.dp), + color = Color.White, + shadowElevation = 1.dp + ) { + Row( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.weight(1f) + ) { + IconButton(onClick = onBackClick) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back", + tint = Color(0xFF0A0A0A), + modifier = Modifier.size(20.dp) + ) + } + + Box( + modifier = Modifier + .size(40.dp) + .background(Color(0xFFF0F0F0), shape = CircleShape), + contentAlignment = Alignment.Center + ) { + Text( + text = "S1", + fontSize = 14.sp, + color = Color(0xFF0A0A0A) + ) + } + + Spacer(modifier = Modifier.width(12.dp)) + + Column { + Text( + text = sellerName, + fontSize = 16.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF1E2939) + ) + Text( + text = sellerStatus, + fontSize = 12.sp, + color = Color(0xFF4A5565) + ) + } + } + + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + IconButton(onClick = onPhoneClick) { + Icon( + imageVector = Icons.Default.Phone, + contentDescription = "Phone", + tint = Color(0xFF0A0A0A), + modifier = Modifier.size(20.dp) + ) + } + + IconButton(onClick = onMenuClick) { + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = "Menu", + tint = Color(0xFF0A0A0A), + modifier = Modifier.size(20.dp) + ) + } + } + } + } +} + +@Composable +fun MessageBubble(message: ChatMessage) { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = if (message.isSentByCurrentUser) Alignment.End else Alignment.Start + ) { + Surface( + shape = if (message.isSentByCurrentUser) { + RoundedCornerShape(topStart = 16.dp, topEnd = 4.dp, bottomStart = 16.dp, bottomEnd = 16.dp) + } else { + RoundedCornerShape(topStart = 4.dp, topEnd = 16.dp, bottomStart = 16.dp, bottomEnd = 16.dp) + }, + color = if (message.isSentByCurrentUser) Color(0xFF0A0A0A) else Color.White, + modifier = Modifier + .widthIn(max = 271.dp) + .then( + if (!message.isSentByCurrentUser) { + Modifier.border( + width = 1.dp, + color = Color(0x1A000000), + shape = RoundedCornerShape(topStart = 4.dp, topEnd = 16.dp, bottomStart = 16.dp, bottomEnd = 16.dp) + ) + } else Modifier + ) + ) { + Column( + modifier = Modifier.padding(12.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = message.text, + fontSize = 14.sp, + lineHeight = 20.sp, + color = if (message.isSentByCurrentUser) Color.White else Color(0xFF0A0A0A) + ) + Text( + text = message.timestamp, + fontSize = 12.sp, + lineHeight = 16.sp, + color = if (message.isSentByCurrentUser) Color(0x99FFFFFF) else Color(0xFF717182) + ) + } + } + } +} + +@Composable +fun MessageInputBar( + messageText: String, + onMessageChange: (String) -> Unit, + onSendClick: () -> Unit +) { + val isMultiline = messageText.contains("\n") || messageText.length > 40 + + Surface( + modifier = Modifier.fillMaxWidth().imePadding(), + color = Color.White, + shadowElevation = 1.dp + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp, vertical = 8.dp), + verticalAlignment = Alignment.Bottom, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + + // TEXT INPUT + OutlinedTextField( + value = messageText, + onValueChange = onMessageChange, + modifier = Modifier + .weight(1f) + .heightIn( + min = if (isMultiline) 44.dp else 36.dp, + max = 120.dp + ), + placeholder = { + Text( + text = "Type a message", + fontSize = 14.sp, + color = Color(0xFF717182) + ) + }, + textStyle = LocalTextStyle.current.copy( + fontSize = 14.sp, + color = Color.Black // ✅ typed text black + ), + colors = OutlinedTextFieldDefaults.colors( + unfocusedBorderColor = Color(0x1A000000), + focusedBorderColor = Color(0x33000000), + unfocusedContainerColor = Color.White, + focusedContainerColor = Color.White, + cursorColor = Color.Black + ), + shape = RoundedCornerShape(20.dp), + singleLine = false, + maxLines = 4 + ) + + // ATTACHMENT ICON + IconButton( + onClick = { }, + modifier = Modifier.size(36.dp) + ) { + Icon( + imageVector = Icons.Default.Attachment, + contentDescription = "Attachment", + tint = Color(0xFF717182), + modifier = Modifier.size(20.dp) + ) + } + + // VOICE BUTTON + Box( + modifier = Modifier + .size(40.dp) + .background(Color(0xFF0A0A0A), CircleShape), + contentAlignment = Alignment.Center + ) { + IconButton( + onClick = { }, + modifier = Modifier.size(20.dp) + ) { + Icon( + imageVector = Icons.Default.KeyboardVoice, + contentDescription = "Voice", + tint = Color.White, + modifier = Modifier.size(18.dp) + ) + } + } + + // SEND BUTTON + Box( + modifier = Modifier + .size(40.dp) + .background(Color(0xFF0A0A0A), CircleShape), + contentAlignment = Alignment.Center + ) { + IconButton( + onClick = onSendClick, + modifier = Modifier.size(20.dp) + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.Send, + contentDescription = "Send", + tint = Color.White, + modifier = Modifier.size(18.dp) + ) + } + } + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/chat/ChatsScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/chat/ChatsScreen.kt new file mode 100644 index 0000000..c1e71f9 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/chat/ChatsScreen.kt @@ -0,0 +1,333 @@ +package com.example.livingai_lg.ui.screens.chat + +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.example.farmmarketplace.ui.screens.ContactsBottomNav +import com.example.farmmarketplace.ui.screens.ContactsTab + +data class ChatPreview( + val id: String, + val name: String, + val initials: String, + val lastMessage: String, + val timestamp: String, + val isOnline: Boolean = false, + val unreadCount: Int = 0 +) + +@Composable +fun ChatsScreen( + onBackClick: () -> Unit = {}, + onNewChatClick: () -> Unit = {}, + onMenuClick: () -> Unit = {}, + onChatItemClick: (String) -> Unit = {}, + onTabClick: (route: String) -> Unit = {} +) { + val chatList = listOf( + ChatPreview( + id = "1", + name = "Farmer Kumar", + initials = "FK", + lastMessage = "The cows are healthy and ready for viewing", + timestamp = "Today, 2:30 PM", + isOnline = true, + unreadCount = 2 + ), + ChatPreview( + id = "2", + name = "Seller Raj", + initials = "SR", + lastMessage = "You: Can you send more photos?", + timestamp = "Today, 11:45 AM", + isOnline = true, + unreadCount = 0 + ), + ChatPreview( + id = "3", + name = "Buyer Priya", + initials = "BP", + lastMessage = "What's the best time to visit?", + timestamp = "Yesterday, 8:15 PM", + isOnline = false, + unreadCount = 1 + ), + ChatPreview( + id = "4", + name = "Seller 1", + initials = "S1", + lastMessage = "You: Thanks for the information", + timestamp = "Yesterday, 5:30 PM", + isOnline = true, + unreadCount = 0 + ), + ChatPreview( + id = "5", + name = "Veterinarian", + initials = "V", + lastMessage = "The animal health check is complete", + timestamp = "2 days ago", + isOnline = false, + unreadCount = 0 + ), + ChatPreview( + id = "6", + name = "Market Vendor", + initials = "MV", + lastMessage = "You: Available this weekend?", + timestamp = "3 days ago", + isOnline = false, + unreadCount = 0 + ) + ) + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFFCFBF8)) + ) { + Column( + modifier = Modifier.fillMaxSize() + ) { + ChatsHeader( + onBackClick = onBackClick, + onNewChatClick = onNewChatClick, + onMenuClick = onMenuClick + ) + + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + .padding(horizontal = 16.dp, vertical = 16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + items(chatList) { chat -> + ChatListItem( + chat = chat, + onClick = { onChatItemClick(chat.id) } + ) + } + } + + ContactsBottomNav( + currentTab = ContactsTab.CHATS, + onTabClick = onTabClick + ) + } + } +} + +@Composable +fun ChatsHeader( + onBackClick: () -> Unit, + onNewChatClick: () -> Unit, + onMenuClick: () -> Unit, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier + .fillMaxWidth() + .height(65.dp) + .border( + width = 1.078.dp, + color = Color(0xFF000000).copy(alpha = 0.1f) + ) + .background(Color(0xFFFCFBF8)) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 14.dp, vertical = 10.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back", + tint = Color(0xFF0A0A0A), + modifier = Modifier + .size(26.dp) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onBackClick() } + ) + + Text( + text = "Chats", + fontSize = 24.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF0A0A0A), + lineHeight = 42.sp + ) + } + + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = "New Chat", + tint = Color(0xFF0A0A0A), + modifier = Modifier + .size(20.dp) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onNewChatClick() } + ) + + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = "Menu", + tint = Color(0xFF0A0A0A), + modifier = Modifier + .size(20.dp) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onMenuClick() } + ) + } + } + } +} + +@Composable +fun ChatListItem( + chat: ChatPreview, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier + .fillMaxWidth() + .height(76.dp) + .border( + width = 1.078.dp, + color = Color(0xFF000000).copy(alpha = 0.1f), + shape = RoundedCornerShape(12.dp) + ) + .background(Color(0xFFFCFBF8), RoundedCornerShape(12.dp)) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onClick() } + .padding(horizontal = 16.dp, vertical = 14.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.weight(1f) + ) { + Box( + modifier = Modifier + .size(48.dp) + .background(Color(0xFFE5E7EB), CircleShape), + contentAlignment = Alignment.Center + ) { + Text( + text = chat.initials, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF0A0A0A), + lineHeight = 21.sp + ) + + if (chat.isOnline) { + Box( + modifier = Modifier + .align(Alignment.BottomEnd) + .size(12.dp) + .background(Color(0xFF00A63E), CircleShape) + .border(2.dp, Color.White, CircleShape) + ) + } + } + + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier.weight(1f) + ) { + Text( + text = chat.name, + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF1E2939), + lineHeight = 20.sp, + letterSpacing = (-0.312).sp + ) + + Text( + text = chat.lastMessage, + fontSize = 12.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF717182), + lineHeight = 16.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + + Column( + horizontalAlignment = Alignment.End, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = chat.timestamp, + fontSize = 12.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF717182), + lineHeight = 16.sp + ) + + if (chat.unreadCount > 0) { + Box( + modifier = Modifier + .size(24.dp) + .background(Color(0xFF155DFC), CircleShape), + contentAlignment = Alignment.Center + ) { + Text( + text = chat.unreadCount.toString(), + fontSize = 10.sp, + fontWeight = FontWeight.Medium, + color = Color.White, + lineHeight = 14.sp + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/chat/ContactsScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/chat/ContactsScreen.kt new file mode 100644 index 0000000..6dc9b5f --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/chat/ContactsScreen.kt @@ -0,0 +1,422 @@ +package com.example.farmmarketplace.ui.screens + +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.Chat +import androidx.compose.material.icons.automirrored.filled.Message +import androidx.compose.material.icons.automirrored.outlined.Message +import androidx.compose.material.icons.filled.Chat +import androidx.compose.material.icons.filled.Contacts +import androidx.compose.material.icons.filled.Message +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material.icons.filled.Phone +import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.outlined.Phone +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.example.livingai_lg.ui.navigation.AppScreen + +data class Contact( + val id: String, + val name: String, + val initials: String, + val status: String, + val isOnline: Boolean = false, + val phoneNumber: String? = null +) + +enum class ContactsTab { + CONTACTS, + CALLS, + CHATS +} + +@Composable +fun ContactsScreen( + onBackClick: () -> Unit = {}, + onSearchClick: () -> Unit = {}, + onMenuClick: () -> Unit = {}, + onContactClick: (String) -> Unit = {}, + onCallClick: (String) -> Unit = {}, + onMessageClick: (String) -> Unit = {}, + onTabClick: (route: String) -> Unit = {} +) { + val contacts = listOf( + Contact( + id = "1", + name = "Farmer Kumar", + initials = "FK", + status = "Online", + isOnline = true + ), + Contact( + id = "2", + name = "Seller Raj", + initials = "SR", + status = "+91 98765 43211", + isOnline = false, + phoneNumber = "+91 98765 43211" + ), + Contact( + id = "3", + name = "Buyer Priya", + initials = "BP", + status = "Online", + isOnline = true + ), + Contact( + id = "4", + name = "Seller 1", + initials = "S1", + status = "+91 98765 43213", + isOnline = false, + phoneNumber = "+91 98765 43213" + ), + Contact( + id = "5", + name = "Veterinarian", + initials = "V", + status = "Online", + isOnline = true + ), + Contact( + id = "6", + name = "Farm Supply", + initials = "FS", + status = "+91 98765 43215", + isOnline = false, + phoneNumber = "+91 98765 43215" + ), + Contact( + id = "7", + name = "Transport Co.", + initials = "TC", + status = "+91 98765 43216", + isOnline = false, + phoneNumber = "+91 98765 43216" + ) + ) + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFFCFBF8)) + ) { + Column( + modifier = Modifier.fillMaxSize() + ) { + ContactsHeader( + onBackClick = onBackClick, + onSearchClick = onSearchClick, + onMenuClick = onMenuClick + ) + + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + items(contacts) { contact -> + ContactItem( + contact = contact, + onContactClick = { onContactClick(contact.id) }, + onCallClick = { onCallClick(contact.id) }, + onMessageClick = { onMessageClick(contact.id) } + ) + } + } + + ContactsBottomNav( + currentTab = ContactsTab.CONTACTS, + onTabClick = onTabClick + ) + } + } +} + +@Composable +fun ContactsHeader( + onBackClick: () -> Unit, + onSearchClick: () -> Unit, + onMenuClick: () -> Unit, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier + .fillMaxWidth() + .height(65.dp) + .border( + width = 1.078.dp, + color = Color(0xFF000000).copy(alpha = 0.1f) + ) + .background(Color(0xFFFCFBF8)) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Icon( + imageVector = Icons.AutoMirrored.Default.ArrowBack, + contentDescription = "Back", + tint = Color(0xFF0A0A0A), + modifier = Modifier + .size(26.dp) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onBackClick() } + ) + + Text( + text = "Contacts", + fontSize = 24.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF0A0A0A), + lineHeight = 42.sp + ) + } + + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Search, + contentDescription = "Search", + tint = Color(0xFF0A0A0A), + modifier = Modifier + .size(20.dp) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onSearchClick() } + ) + + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = "Menu", + tint = Color(0xFF0A0A0A), + modifier = Modifier + .size(20.dp) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onMenuClick() } + ) + } + } + } +} + +@Composable +fun ContactItem( + contact: Contact, + onContactClick: () -> Unit, + onCallClick: () -> Unit, + onMessageClick: () -> Unit, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier + .fillMaxWidth() + .height(73.dp) + .border( + width = 1.078.dp, + color = Color(0xFF000000).copy(alpha = 0.05f) + ) + .background(Color(0xFFFCFBF8)) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onContactClick() } + .padding(horizontal = 16.dp, vertical = 12.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .size(48.dp) + .background(Color(0xFFE5E7EB), CircleShape), + contentAlignment = Alignment.Center + ) { + Text( + text = contact.initials, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF0A0A0A), + lineHeight = 21.sp + ) + } + + Column( + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = contact.name, + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF1E2939), + lineHeight = 20.sp, + letterSpacing = (-0.312).sp + ) + + Text( + text = contact.status, + fontSize = 12.sp, + fontWeight = FontWeight.Normal, + color = Color(0xFF717182), + lineHeight = 16.sp + ) + } + } + + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .size(36.dp) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onCallClick() }, + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = Icons.Outlined.Phone, + contentDescription = "Call", + tint = Color(0xFF030213), + modifier = Modifier.size(20.dp) + ) + } + + Box( + modifier = Modifier + .size(36.dp) + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onMessageClick() }, + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = Icons.AutoMirrored.Outlined.Message, + contentDescription = "Message", + tint = Color(0xFF030213), + modifier = Modifier.size(20.dp) + ) + } + } + } +} + +@Composable +fun ContactsBottomNav( + currentTab: ContactsTab, + onTabClick: (route: String) -> Unit, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier + .fillMaxWidth() + .height(65.dp) + .border( + width = 1.078.dp, + color = Color(0xFF000000).copy(alpha = 0.1f) + ) + .background(Color(0xFFFCFBF8)) + .padding(horizontal = 32.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + + ContactsTabItem( + icon = Icons.AutoMirrored.Filled.Chat, + label = "Chats", + isSelected = currentTab == ContactsTab.CHATS, + onClick = { onTabClick(AppScreen.CHATS) } + ) + ContactsTabItem( + icon = Icons.Default.Phone, + label = "Calls", + isSelected = currentTab == ContactsTab.CALLS, + onClick = { onTabClick(AppScreen.CALLS) } + ) + ContactsTabItem( + icon = Icons.Default.Contacts, + label = "Contacts", + isSelected = currentTab == ContactsTab.CONTACTS, + onClick = { onTabClick(AppScreen.CONTACTS) } + ) + + + } +} + +@Composable +fun ContactsTabItem( + icon: ImageVector, + label: String, + isSelected: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier + .clickable( + indication = LocalIndication.current, + interactionSource = remember { MutableInteractionSource() } + ) { onClick() } + .padding(4.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Icon( + imageVector = icon, + contentDescription = label, + tint = Color(0xFF0A0A0A), + modifier = Modifier.size(24.dp) + ) + + Text( + text = label, + fontSize = 12.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF0A0A0A), + lineHeight = 16.sp, + textAlign = TextAlign.Center + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/livingai_lg/ui/theme/AppTypography.kt b/app/src/main/java/com/example/livingai_lg/ui/theme/AppTypography.kt new file mode 100644 index 0000000..bb97502 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/theme/AppTypography.kt @@ -0,0 +1,12 @@ +package com.example.livingai_lg.ui.theme + +import androidx.compose.ui.unit.sp + +object AppTypography { + val Display = 32.sp + val Title = 20.sp + val Body = 16.sp + val BodySmall = 14.sp + val Caption = 12.sp + val Micro = 10.sp +}