New screens and improvements

This commit is contained in:
ankitsaraf 2025-12-19 18:59:01 +05:30
parent 4f8ae88820
commit 6b634f40b7
45 changed files with 3277 additions and 649 deletions

View File

@ -4,14 +4,9 @@ import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge 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.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavType import androidx.navigation.NavType
@ -19,17 +14,16 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument import androidx.navigation.navArgument
import com.example.livingai_lg.ui.AuthState
import com.example.livingai_lg.ui.MainViewModel import com.example.livingai_lg.ui.MainViewModel
import com.example.livingai_lg.ui.MainViewModelFactory 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.navigation.AppNavigation
import com.example.livingai_lg.ui.theme.FarmMarketplaceTheme import com.example.livingai_lg.ui.theme.FarmMarketplaceTheme
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() //enableEdgeToEdge()
setContent { setContent {
FarmMarketplaceTheme { FarmMarketplaceTheme {
val mainViewModel: MainViewModel = viewModel(factory = MainViewModelFactory(LocalContext.current)) 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 @Composable
fun AuthNavigation() { fun AuthNavigation() {
val navController = rememberNavController() val navController = rememberNavController()

View File

@ -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()
}
)
}
}
}

View File

@ -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<UserAddress>,
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<UserAddress>,
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
)
}
}

View File

@ -6,15 +6,22 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape 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.Icon
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp 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.formatPrice
import com.example.livingai_lg.ui.utils.formatViews import com.example.livingai_lg.ui.utils.formatViews
import com.example.livingai_lg.R import com.example.livingai_lg.R
import com.example.livingai_lg.ui.theme.AppTypography
@Composable @Composable
fun BuyAnimalCard( fun BuyAnimalCard(
@ -34,6 +42,7 @@ fun BuyAnimalCard(
onSavedChange: (Boolean) -> Unit, onSavedChange: (Boolean) -> Unit,
onProductClick: () -> Unit, onProductClick: () -> Unit,
onSellerClick:(sellerId: String)-> Unit, onSellerClick:(sellerId: String)-> Unit,
onBookmarkClick: () -> Unit
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
@ -46,163 +55,204 @@ fun BuyAnimalCard(
Color(0xFF000000).copy(alpha = 0.1f), Color(0xFF000000).copy(alpha = 0.1f),
RoundedCornerShape(14.dp) RoundedCornerShape(14.dp)
) )
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() },
onClick = onProductClick
)
) { ) {
Column { 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( Column(
modifier = Modifier.padding(16.dp), modifier = Modifier.clickable(
verticalArrangement = Arrangement.spacedBy(12.dp) indication = LocalIndication.current,
) { interactionSource = remember { MutableInteractionSource() },
Row( onClick = onProductClick
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
) )
) {
// 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)) { // Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
// ActionButton(R.drawable.ic_chat, "Chat") // ActionButton(R.drawable.ic_chat, "Chat")
// ActionButton(R.drawable.ic_phone, "Call") // ActionButton(R.drawable.ic_phone, "Call")
// ActionButton(R.drawable.ic_location, "Location") // ActionButton(R.drawable.ic_location, "Location")
// ActionButton(R.drawable.ic_bookmark_plus, "Bookmark") // 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
)
} }
} }
} }

View File

@ -7,6 +7,8 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape 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.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment 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.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.livingai_lg.ui.theme.AppTypography
import com.example.livingai_lg.ui.theme.FarmTextDark import com.example.livingai_lg.ui.theme.FarmTextDark
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -38,7 +41,7 @@ fun DropdownInput(
if (label != null) { if (label != null) {
Text( Text(
text = label, text = label,
fontSize = 16.sp, fontSize = AppTypography.Body,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = FarmTextDark color = FarmTextDark
) )
@ -78,15 +81,16 @@ fun DropdownInput(
// Custom placeholder support // Custom placeholder support
Text( Text(
text = selected.ifEmpty { placeholder }, text = selected.ifEmpty { placeholder },
fontSize = 16.sp, fontSize = AppTypography.Body,
color = if (selected.isEmpty()) Color(0xFF99A1AF) else FarmTextDark color = if (selected.isEmpty()) Color(0xFF99A1AF) else FarmTextDark
) )
Text( Icon(
text = "", imageVector = Icons.Default.ArrowDropDown,
fontSize = 12.sp, contentDescription = "Dropdown",
color = FarmTextDark tint = FarmTextDark
) )
} }
} }
@ -99,7 +103,7 @@ fun DropdownInput(
options.forEach { item -> options.forEach { item ->
DropdownMenuItem( DropdownMenuItem(
text = { text = {
Text(item, fontSize = 16.sp, color = FarmTextDark) Text(item, fontSize = AppTypography.Body, color = FarmTextDark)
}, },
onClick = { onClick = {
onSelect(item) onSelect(item)

View File

@ -26,7 +26,7 @@ import com.example.livingai_lg.R
@Composable @Composable
fun FilterButton( fun FilterButton(
onFilterClick: () -> Unit onClick: () -> Unit
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@ -37,7 +37,7 @@ fun FilterButton(
.clickable( .clickable(
indication = LocalIndication.current, indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
onClick = onFilterClick, onClick = onClick,
), ),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)

View File

@ -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()
}
)
}
}
}

View File

@ -1,14 +1,26 @@
package com.example.livingai_lg.ui.components 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.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape 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.material3.Icon
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.example.livingai_lg.R import com.example.livingai_lg.R
@ -16,35 +28,44 @@ import com.example.livingai_lg.R
@Composable @Composable
fun FloatingActionBar( fun FloatingActionBar(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
showContainer: Boolean = true,
onChatClick: () -> Unit = {}, onChatClick: () -> Unit = {},
onCallClick: () -> Unit = {}, onCallClick: () -> Unit = {},
onLocationClick: () -> Unit = {}, onLocationClick: () -> Unit = {},
onBookmarkClick: () -> 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( Box(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
//.padding(horizontal = 24.dp) //.padding(horizontal = 24.dp)
.shadow( .then(containerModifier)
elevation = 12.dp,
shape = RoundedCornerShape(50), // pill shape
clip = false
)
.background(
color = Color.White,
shape = RoundedCornerShape(50)
)
.padding(vertical = 12.dp)
) { ) {
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly, horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
FloatingActionIcon(R.drawable.ic_chat, onChatClick) FloatingActionIcon(Icons.AutoMirrored.Outlined.Chat, onChatClick)
FloatingActionIcon(R.drawable.ic_phone, onCallClick) FloatingActionIcon(Icons.Outlined.Phone, onCallClick)
FloatingActionIcon(R.drawable.ic_location, onLocationClick) FloatingActionIcon(Icons.Outlined.LocationOn, onLocationClick)
FloatingActionIcon(R.drawable.ic_bookmark_plus, onBookmarkClick) FloatingActionIcon(Icons.Outlined.BookmarkAdd, onBookmarkClick)
} }
} }
} }
@ -52,7 +73,7 @@ fun FloatingActionBar(
@Composable @Composable
private fun FloatingActionIcon( private fun FloatingActionIcon(
iconRes: Int, icon: ImageVector,
onClick: () -> Unit onClick: () -> Unit
) { ) {
Box( Box(
@ -63,11 +84,16 @@ private fun FloatingActionIcon(
shape = RoundedCornerShape(24.dp), shape = RoundedCornerShape(24.dp),
clip = false 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 contentAlignment = Alignment.Center
) { ) {
Icon( Icon(
painter = painterResource(iconRes), imageVector = icon,
contentDescription = null, contentDescription = null,
tint = Color(0xFF0A0A0A), tint = Color(0xFF0A0A0A),
modifier = Modifier.size(22.dp) modifier = Modifier.size(22.dp)

View File

@ -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<AppNotification>,
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
)
}
}

View File

@ -16,16 +16,19 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.livingai_lg.ui.theme.AppTypography
@Composable @Composable
fun OptionCard( fun OptionCard(
label: String, label: String,
icon: Int, icon: Any,
iconBackgroundColor: Color, iconBackgroundColor: Color,
iconTint: Color = Color.Unspecified,
onClick: () -> Unit onClick: () -> Unit
) { ) {
Box( Box(
@ -54,17 +57,28 @@ fun OptionCard(
.background(iconBackgroundColor, RoundedCornerShape(14.dp)), .background(iconBackgroundColor, RoundedCornerShape(14.dp)),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Icon( when (icon) {
painter = painterResource(id = icon), is ImageVector -> {
contentDescription = label, Icon(
tint = Color.Unspecified imageVector = icon,
) contentDescription = label,
tint = iconTint
)
}
is Int -> {
Icon(
painter = painterResource(icon),
contentDescription = label,
tint = Color.Unspecified
)
}
}
} }
// Label // Label
Text( Text(
text = label, text = label,
fontSize = 16.sp, fontSize = AppTypography.Body,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A), color = Color(0xFF0A0A0A),
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)

View File

@ -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)
)
}
}
}

View File

@ -25,17 +25,23 @@ import androidx.compose.ui.unit.sp
import com.example.livingai_lg.R import com.example.livingai_lg.R
@Composable @Composable
fun SortButton(onSortClick: () -> Unit) { fun SortButton(
onClick: () -> Unit
) {
Row( Row(
modifier = Modifier modifier = Modifier
.height(36.dp) .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)) .background(Color.White, RoundedCornerShape(8.dp))
.padding(horizontal = 8.dp) .padding(horizontal = 8.dp)
.clickable( .clickable(
indication = LocalIndication.current, indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
onClick = onSortClick, onClick = onClick
), ),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
@ -53,4 +59,4 @@ fun SortButton(onSortClick: () -> Unit) {
color = Color(0xFF0A0A0A) color = Color(0xFF0A0A0A)
) )
} }
} }

View File

@ -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<SortField>) -> 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
)
}
}
}
}
}

View File

@ -31,23 +31,15 @@ import com.example.livingai_lg.R
@Composable @Composable
fun UserLocationHeader( fun UserLocationHeader(
user: UserProfile, user: UserProfile,
selectedAddressId: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onAddressSelected: (UserAddress) -> Unit = {}, onOpenAddressOverlay: () -> Unit
onAddNewClick: () -> Unit = {} // future navigation hook
) { ) {
var expanded by remember { mutableStateOf(false) }
var selectedAddress by remember {
mutableStateOf(
user.addresses.firstOrNull { it.isPrimary }
?: user.addresses.first()
)
}
Row( Row(
modifier = modifier.wrapContentWidth(), modifier = modifier.wrapContentWidth(),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
val selectedAddress = user.addresses.find { it.id == selectedAddressId } ?: user.addresses.first()
// Profile image // Profile image
Box( Box(
@ -75,79 +67,38 @@ fun UserLocationHeader(
Spacer(modifier = Modifier.width(12.dp)) Spacer(modifier = Modifier.width(12.dp))
// Anchor ONLY the text section // Address + arrow (click opens overlay)
Box { Column(
Column( modifier = Modifier.clickable(
modifier = Modifier indication = LocalIndication.current,
.clickable( interactionSource = remember { MutableInteractionSource() }
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) { expanded = true }
) { ) {
Row(verticalAlignment = Alignment.CenterVertically) { onOpenAddressOverlay()
Text( }
text = selectedAddress.name, ) {
fontSize = 16.sp, Row(verticalAlignment = Alignment.CenterVertically) {
fontWeight = FontWeight.SemiBold,
color = Color.Black
)
Icon(
imageVector = Icons.Default.KeyboardArrowDown,
contentDescription = null,
tint = Color.Black,
modifier = Modifier.size(18.dp)
)
}
Text( Text(
text = selectedAddress.address, text = selectedAddress.name,
fontSize = 13.sp, fontSize = 16.sp,
color = Color.Black.copy(alpha = 0.7f), fontWeight = FontWeight.SemiBold,
maxLines = 1, color = Color.Black
overflow = TextOverflow.Ellipsis )
Icon(
imageVector = Icons.Default.KeyboardArrowDown,
contentDescription = null,
tint = Color.Black,
modifier = Modifier.size(18.dp)
) )
} }
// Dropdown appears BELOW name/address Text(
DropdownMenu( text = selectedAddress.address,
expanded = expanded, fontSize = 13.sp,
onDismissRequest = { expanded = false } color = Color.Black.copy(alpha = 0.7f),
) { maxLines = 1,
user.addresses.forEach { address -> overflow = TextOverflow.Ellipsis
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()
}
)
}
} }
} }
} }

View File

@ -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 android.widget.Toast
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@ -14,7 +13,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource

View File

@ -1,4 +1,4 @@
package com.example.livingai_lg.ui.login package com.example.livingai_lg.ui.login_legacy
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.background import androidx.compose.foundation.background
@ -11,7 +11,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight

View File

@ -1,8 +1,7 @@
package com.example.livingai_lg.ui.login package com.example.livingai_lg.ui.login_legacy
import android.widget.Toast 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.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.* import androidx.compose.material3.*
@ -10,7 +9,6 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight

View File

@ -1,4 +1,4 @@
package com.example.livingai_lg.ui.login package com.example.livingai_lg.ui.login_legacy
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.background import androidx.compose.foundation.background
@ -14,7 +14,6 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight

View File

@ -1,4 +1,4 @@
package com.example.livingai_lg.ui.login package com.example.livingai_lg.ui.login_legacy
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.background import androidx.compose.foundation.background
@ -15,7 +15,6 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight

View File

@ -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.foundation.layout.*
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp

View File

@ -25,31 +25,42 @@ data class Animal(
val sampleAnimals = listOf( val sampleAnimals = listOf(
Animal( Animal(
id = "1", id = "1",
name = "Golden Retriever", name = "Rita",
price = 80000, age = 45,
imageUrl = listOf("https://api.builder.io/api/v1/image/assets/TEMP/8b3d82a183db9ded7741b4b8bf0cd81fb2733b89?width=686"), breed = "Gir",
distance = 2500L,// miles away location = "Punjab",
views = 94,//Views 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", sellerId = "1",
sellerName = "Seller 1", sellerName = "Seller 1",
sellerType = "Wholeseller", 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.",
rating = 4.5f, displayLocation = "Mediatek Pashu Mela, Marathalli, Bangalore (13 km)",
ratingCount = 2076, milkCapacity = 3.2f
description = "Friendly and energetic companion looking for an active family.",
), ),
Animal( Animal(
id = "2", id = "2",
name = "Mudkip", name = "Sahi",
price = 999999999, price = 95000,
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"), 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, distance = 0L,
views = 100000, views = 100,
sellerId = "1", sellerId = "1",
sellerName = "Seller ???", sellerName = "Seller ???",
sellerType = "Wholeseller", sellerType = "Wholeseller",
rating = 5f, rating = 5f,
ratingCount = 2076, ratingCount = 2076,
description = "Friendly and energetic companion looking for an active family.", description = "Friendly and energetic companion looking for an active family.",
milkCapacity = 2.5f
), ),
Animal( Animal(
id = "3", id = "3",

View File

@ -13,15 +13,13 @@ val mainBottomNavItems = listOf(
BottomNavItemData("Home", R.drawable.ic_home , AppScreen.BUY_ANIMALS), BottomNavItemData("Home", R.drawable.ic_home , AppScreen.BUY_ANIMALS),
BottomNavItemData("Sell", R.drawable.ic_tag, AppScreen.CREATE_ANIMAL_LISTING), BottomNavItemData("Sell", R.drawable.ic_tag, AppScreen.CREATE_ANIMAL_LISTING),
// TODO: // 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("Services", R.drawable.ic_config, AppScreen.CREATE_PROFILE),
BottomNavItemData("Mandi", R.drawable.ic_market, AppScreen.CREATE_PROFILE) BottomNavItemData("Mandi", R.drawable.ic_market, AppScreen.CREATE_PROFILE)
) )
val chatBottomNavItems = listOf( val chatBottomNavItems = listOf(
BottomNavItemData("Home", R.drawable.ic_home ,"home"), BottomNavItemData("Contacts", R.drawable.ic_home ,"home"),
BottomNavItemData("Sell", R.drawable.ic_tag, "sell"), BottomNavItemData("Calls", R.drawable.ic_tag, "sell"),
BottomNavItemData("Chats", R.drawable.ic_chat, "chats"), BottomNavItemData("Chats", R.drawable.ic_chat, "chats"),
BottomNavItemData("Services", R.drawable.ic_config, "services"), )
BottomNavItemData("Mandi", R.drawable.ic_market, "mandi")
)

View File

@ -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
)
)

View File

@ -1,13 +1,16 @@
package com.example.livingai_lg.ui.models package com.example.livingai_lg.ui.models
import androidx.compose.ui.graphics.Color 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 import com.example.livingai_lg.R
data class ProfileType( data class ProfileType(
val id: String, val id: String,
val title: String, val title: String,
val icon: Int, val icon: Any,
val backgroundColor: Color val iconTint: Color = Color.Unspecified,
val backgroundColor: Color,
) )
val profileTypes = listOf( val profileTypes = listOf(
@ -15,24 +18,28 @@ val profileTypes = listOf(
id = "buyer_seller", id = "buyer_seller",
title = "I'm a Buyer/Seller", title = "I'm a Buyer/Seller",
icon = R.drawable.ic_shop, icon = R.drawable.ic_shop,
iconTint = Color.White,
backgroundColor = Color(0xFF9D4EDD) backgroundColor = Color(0xFF9D4EDD)
), ),
ProfileType( ProfileType(
id = "wholesale_trader", id = "wholesale_trader",
title = "I'm a Wholesale Trader", title = "I'm a Wholesale Trader",
icon = R.drawable.ic_bag, icon = R.drawable.ic_bag,
iconTint = Color.White,
backgroundColor = Color(0xFF3A86FF) backgroundColor = Color(0xFF3A86FF)
), ),
ProfileType( ProfileType(
id = "service_provider", id = "service_provider",
title = "I'm a Service Provider", title = "I'm a Service Provider",
icon = R.drawable.ic_spanner, icon = R.drawable.ic_spanner,
iconTint = Color.White,
backgroundColor = Color(0xFFFF5722) backgroundColor = Color(0xFFFF5722)
), ),
ProfileType( ProfileType(
id = "mandi_host", id = "mandi_host",
title = "I'm a Mandi Host", title = "I'm a Mandi Host",
icon = R.drawable.ic_shop2, icon = R.drawable.ic_shop2,
iconTint = Color.White,
backgroundColor = Color(0xFF4CAF50) backgroundColor = Color(0xFF4CAF50)
) )
) )

View File

@ -1,6 +1,7 @@
package com.example.livingai_lg.ui.models package com.example.livingai_lg.ui.models
data class UserAddress( data class UserAddress(
val id: String,
val name: String, val name: String,
val address: String, val address: String,
val isPrimary: Boolean = false 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", 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( addresses = listOf(
UserAddress( UserAddress(
id = "1",
name = "Home", name = "Home",
address = "205 1st floor 7th cross 27th main, PW", address = "205 1st floor 7th cross 27th main, PW",
isPrimary = true isPrimary = true
), ),
UserAddress( UserAddress(
id = "2",
name = "Farm", name = "Farm",
address = "2nd block, MG Farms" address = "2nd block, MG Farms"
) )

View File

@ -4,6 +4,9 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable 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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.navigation.NavController 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.OtpScreen
import com.example.livingai_lg.ui.screens.auth.SignInScreen import com.example.livingai_lg.ui.screens.auth.SignInScreen
import com.example.livingai_lg.ui.screens.auth.SignUpScreen 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 { object AppScreen {
const val LANDING = "landing" const val LANDING = "landing"
@ -55,6 +59,19 @@ object AppScreen {
const val POST_SALE_SURVEY = "post_sale_survey" 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) = fun otp(phone: String, name: String) =
"$OTP/$phone/$name" "$OTP/$phone/$name"
@ -74,23 +91,55 @@ object AppScreen {
fun saleArchive(saleId: String) = fun saleArchive(saleId: String) =
"$SALE_ARCHIVE/$saleId" "$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 @Composable
fun AppNavigation( fun AppNavigation(
authState: AuthState authState: AuthState
) { ) {
val navController = rememberNavController()
var isLoggedIn = false;
when (authState) { when (authState) {
is AuthState.Unauthenticated -> {AuthNavGraph()} is AuthState.Unauthenticated -> {isLoggedIn = false; }
is AuthState.Authenticated -> {MainNavGraph()} is AuthState.Authenticated -> {isLoggedIn = true;}
is AuthState.Unknown -> { is AuthState.Unknown -> {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CircularProgressIndicator() 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 onNavClick: (String) -> Unit = { route ->
// val currentRoute = // val currentRoute =

View File

@ -1,10 +1,13 @@
package com.example.livingai_lg.ui.navigation package com.example.livingai_lg.ui.navigation
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.NavType import androidx.navigation.NavType
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.navigation
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument import androidx.navigation.navArgument
import com.example.livingai_lg.ui.screens.SaleArchiveScreen 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.SignInScreen
import com.example.livingai_lg.ui.screens.auth.SignUpScreen import com.example.livingai_lg.ui.screens.auth.SignUpScreen
@Composable fun NavGraphBuilder.authNavGraph(navController: NavController) {
fun AuthNavGraph( navigation(
navController: NavHostController = rememberNavController() route = Graph.AUTH,
) {
NavHost(
navController = navController,
startDestination = AppScreen.LANDING startDestination = AppScreen.LANDING
) { ) {
composable(AppScreen.LANDING) { composable(AppScreen.LANDING) {
LandingScreen( LandingScreen(
onSignUpClick = { navController.navigate(AppScreen.SIGN_UP) }, onSignUpClick = { navController.navigate(AppScreen.SIGN_UP) },
onSignInClick = { navController.navigate(AppScreen.SIGN_IN) }, 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( OtpScreen(
phoneNumber = backStackEntry.arguments?.getString("phoneNumber") ?: "", phoneNumber = backStackEntry.arguments?.getString("phoneNumber") ?: "",
name = backStackEntry.arguments?.getString("name") ?: "", 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")))}
) )
} }
} }

View File

@ -1,13 +1,16 @@
package com.example.livingai_lg.ui.navigation package com.example.livingai_lg.ui.navigation
import androidx.compose.runtime.Composable import android.util.Log
import androidx.navigation.NavHostController import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavType import androidx.navigation.NavType
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.navigation
import androidx.navigation.navArgument 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.AnimalProfileScreen
import com.example.livingai_lg.ui.screens.BuyScreen import com.example.livingai_lg.ui.screens.BuyScreen
import com.example.livingai_lg.ui.screens.ChooseServiceScreen 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.NewListingScreen
import com.example.livingai_lg.ui.screens.PostSaleSurveyScreen import com.example.livingai_lg.ui.screens.PostSaleSurveyScreen
import com.example.livingai_lg.ui.screens.SaleArchiveScreen 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.SellerProfileScreen
import com.example.livingai_lg.ui.screens.SortScreen 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 NavGraphBuilder.mainNavGraph(navController: NavController) {
fun MainNavGraph(
navController: NavHostController = rememberNavController()
) {
val onNavClick: (String) -> Unit = { route -> val onNavClick: (String) -> Unit = { route ->
val currentRoute = val currentRoute =
navController.currentBackStackEntry?.destination?.route navController.currentBackStackEntry?.destination?.route
Log.d("Current Route:"," $currentRoute $route")
if (currentRoute != route) { if (currentRoute != route) {
navController.navigate(route) { navController.navigate(route) {
launchSingleTop = true launchSingleTop = true
restoreState = true //restoreState = true
popUpTo(navController.graph.startDestinationId) { // popUpTo(navController.graph.startDestinationId) {
saveState = true // saveState = true
} // }
} }
} }
} }
NavHost( navigation(
navController = navController, route = Graph.MAIN,
startDestination = AppScreen.BUY_ANIMALS startDestination = AppScreen.createProfile("guest")
) { ){
// NavHost(
// navController = navController,
// startDestination = AppScreen.createProfile("guest")
// ) {
composable( composable(
"${AppScreen.CREATE_PROFILE}/{name}", "${AppScreen.CREATE_PROFILE}/{name}",
arguments = listOf(navArgument("name") { type = NavType.StringType }) arguments = listOf(navArgument("name") { type = NavType.StringType })
@ -55,12 +65,6 @@ fun MainNavGraph(
) )
} }
composable(AppScreen.CHOOSE_SERVICE) {
ChooseServiceScreen (
onServiceSelected = { navController.navigate(AppScreen.LANDING) },
)
}
composable( composable(
"${AppScreen.CHOOSE_SERVICE}/{profileId}", "${AppScreen.CHOOSE_SERVICE}/{profileId}",
arguments = listOf(navArgument("profileId") { type = NavType.StringType }) arguments = listOf(navArgument("profileId") { type = NavType.StringType })
@ -82,8 +86,6 @@ fun MainNavGraph(
) )
}, },
onNavClick = onNavClick, onNavClick = onNavClick,
onFilterClick = {navController.navigate(AppScreen.BUY_ANIMALS_FILTERS)},
onSortClick = {navController.navigate(AppScreen.BUY_ANIMALS_SORT)},
onSellerClick = { sellerId -> onSellerClick = { sellerId ->
navController.navigate( navController.navigate(
AppScreen.sellerProfile(sellerId) AppScreen.sellerProfile(sellerId)
@ -92,20 +94,17 @@ fun MainNavGraph(
) )
} }
composable(AppScreen.BUY_ANIMALS_FILTERS) { composable(AppScreen.BUY_ANIMALS_FILTERS) {
FilterScreen( FilterScreen(
onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
onBackClick = { onBackClick = {
navController.popBackStack() navController.popBackStack()
}, },
onSkipClick = { onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
navController.popBackStack()
},
onCancelClick = { onCancelClick = {
navController.popBackStack() navController.popBackStack()
}, },
)
)
} }
composable(AppScreen.BUY_ANIMALS_SORT) { composable(AppScreen.BUY_ANIMALS_SORT) {
@ -114,9 +113,6 @@ fun MainNavGraph(
onBackClick = { onBackClick = {
navController.popBackStack() navController.popBackStack()
}, },
onSkipClick = {
navController.popBackStack()
},
onCancelClick = { onCancelClick = {
navController.popBackStack() navController.popBackStack()
}, },
@ -124,13 +120,25 @@ fun MainNavGraph(
) )
} }
composable(AppScreen.SAVED_LISTINGS) {
SavedListingsScreen(
onNavClick = onNavClick,
onBackClick = { navController.popBackStack() })
}
composable(AppScreen.CREATE_ANIMAL_LISTING) { composable(AppScreen.CREATE_ANIMAL_LISTING) {
NewListingScreen ( NewListingScreen (
onSaveClick = {navController.navigate( onSaveClick = {navController.navigate(
AppScreen.postSaleSurvey("2") AppScreen.postSaleSurvey("2")
)}, )},
onBackClick = { onBackClick = {
navController.popBackStack() navController.navigate(AppScreen.BUY_ANIMALS){
popUpTo(AppScreen.BUY_ANIMALS){
inclusive = true
}
}
} }
) )
} }
@ -175,7 +183,7 @@ fun MainNavGraph(
PostSaleSurveyScreen ( PostSaleSurveyScreen (
animalId = animalId, animalId = animalId,
onBackClick = { onBackClick = {
navController.popBackStack() navController.navigate(AppScreen.CREATE_ANIMAL_LISTING)
}, },
onSubmit = {navController.navigate( onSubmit = {navController.navigate(
AppScreen.saleArchive("2") AppScreen.saleArchive("2")
@ -200,6 +208,7 @@ fun MainNavGraph(
onBackClick = { onBackClick = {
navController.popBackStack() navController.popBackStack()
}, },
onNavClick = onNavClick,
onSellerClick = { sellerId -> onSellerClick = { sellerId ->
navController.navigate( navController.navigate(
AppScreen.sellerProfile(sellerId) AppScreen.sellerProfile(sellerId)
@ -226,5 +235,51 @@ fun MainNavGraph(
navController.popBackStack() 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()
}
)
}
}
} }

View File

@ -1,5 +1,6 @@
package com.example.livingai_lg.ui.screens package com.example.livingai_lg.ui.screens
import androidx.compose.foundation.Image
import com.example.livingai_lg.ui.components.ImageCarousel import com.example.livingai_lg.ui.components.ImageCarousel
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background import androidx.compose.foundation.background
@ -9,11 +10,21 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll 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.Icon
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow 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.draw.drawBehind
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import com.example.livingai_lg.ui.models.Animal import com.example.livingai_lg.ui.models.Animal
import com.example.livingai_lg.ui.utils.formatPrice 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.models.sampleAnimals
import com.example.livingai_lg.ui.utils.formatAge import com.example.livingai_lg.ui.utils.formatAge
import com.example.livingai_lg.R 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 @Composable
@ -45,12 +64,15 @@ fun AnimalProfileScreen(
animalId: String, animalId: String,
onBackClick: () -> Unit = {}, onBackClick: () -> Unit = {},
onSellerClick: (sellerId: String) -> 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") val animal = sampleAnimals.find { animal -> animal.id == animalId } ?: Animal(id = "null")
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(Color(0xFFF7F4EE)) .background(Color(0xFFF7F4EE))
.padding(12.dp)
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@ -62,7 +84,7 @@ fun AnimalProfileScreen(
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(500.dp) .height(350.dp)
.shadow(4.dp) .shadow(4.dp)
) { ) {
// Main image // Main image
@ -92,19 +114,31 @@ fun AnimalProfileScreen(
Row( Row(
modifier = Modifier modifier = Modifier
.align(Alignment.TopStart) .align(Alignment.TopStart)
.padding(start = 16.dp, top = 16.dp), .padding(start = 5.dp, top = 5.dp)
horizontalArrangement = Arrangement.spacedBy(8.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 verticalAlignment = Alignment.CenterVertically
) { ) {
Icon( Icon(
painter = painterResource(R.drawable.ic_view), painter = painterResource(R.drawable.ic_view),
contentDescription = "Views", contentDescription = "Views",
tint = Color.White, tint = Color.White,
modifier = Modifier.size(24.dp) modifier = Modifier.size(16.dp)
) )
Text( Text(
text = formatViews(animal.views), text = formatViews(animal.views),
fontSize = 12.sp, fontSize = AppTypography.BodySmall,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
color = Color.White color = Color.White
) )
@ -120,25 +154,41 @@ fun AnimalProfileScreen(
) { ) {
Text( Text(
text = "${animal.name}, ${formatAge(animal.age)}", text = "${animal.name}, ${formatAge(animal.age)}",
fontSize = 30.sp, fontSize = AppTypography.Title,
fontWeight = FontWeight.Normal, 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(
text = "${animal.breed}${animal.location}, ${animal.distance}".uppercase(), text = "${animal.breed}${animal.location}, ${formatDistance(animal.distance)}".uppercase(),
fontSize = 12.sp, fontSize = AppTypography.Body,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
color = Color.White.copy(alpha = 0.7f), 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) // AI Score badge (bottom left)
Row( Row(
modifier = Modifier modifier = Modifier
.align(Alignment.BottomStart) .align(Alignment.BottomStart)
.padding(start = 0.dp, bottom = 5.dp) .padding(start = 5.dp, bottom = 5.dp)
.height(48.dp) .height(60.dp)
.border(2.dp, Color(0xFF717182), CircleShape) .border(2.dp, Color(0xFF717182), CircleShape)
.padding(horizontal = 12.dp), .padding(horizontal = 12.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp),
@ -147,7 +197,7 @@ fun AnimalProfileScreen(
AIScoreCircle(score = animal.aiScore ?: 0f) AIScoreCircle(score = animal.aiScore ?: 0f)
Text( Text(
text = "AI Score", text = "AI Score",
fontSize = 18.sp, fontSize = AppTypography.Body,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
color = Color.White color = Color.White
) )
@ -159,11 +209,12 @@ fun AnimalProfileScreen(
append("At Display: ") append("At Display: ")
append(animal.displayLocation) append(animal.displayLocation)
}, },
fontSize = 8.sp, fontSize = AppTypography.Caption,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
color = Color.White, color = Color.White.copy(alpha = 0.7f),
lineHeight = 13.sp, lineHeight = 13.sp,
textDecoration = TextDecoration.Underline, textDecoration = TextDecoration.Underline,
textAlign = TextAlign.Right,
modifier = Modifier modifier = Modifier
.align(Alignment.BottomEnd) .align(Alignment.BottomEnd)
.padding(end = 16.dp, bottom = 8.dp) .padding(end = 16.dp, bottom = 8.dp)
@ -190,8 +241,8 @@ fun AnimalProfileScreen(
) { ) {
Text( Text(
text = formatPrice(animal.price), text = formatPrice(animal.price),
fontSize = 30.sp, fontSize = AppTypography.Title,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Bold,
color = Color(0xFF0A0A0A) color = Color(0xFF0A0A0A)
) )
@ -204,12 +255,12 @@ fun AnimalProfileScreen(
Icon( Icon(
painter = painterResource(R.drawable.ic_thumbs_up), painter = painterResource(R.drawable.ic_thumbs_up),
contentDescription = "Fair Price", contentDescription = "Fair Price",
tint = Color(0xFF0A0A0A), tint = Color(0xFF00C950),
modifier = Modifier.size(15.dp) modifier = Modifier.size(15.dp)
) )
Text( Text(
text = "Fair Price", text = "Fair Price",
fontSize = 12.sp, fontSize = AppTypography.Caption,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
color = Color(0xFF00C950) color = Color(0xFF00C950)
) )
@ -219,7 +270,7 @@ fun AnimalProfileScreen(
Column( Column(
horizontalAlignment = Alignment.End, horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier.clickable( modifier = Modifier.clickable(
indication = LocalIndication.current, indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
@ -229,7 +280,7 @@ fun AnimalProfileScreen(
{ {
Text( Text(
text = "Sold By: ${animal.sellerName}", text = "Sold By: ${animal.sellerName}",
fontSize = 16.sp, fontSize = AppTypography.Body,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A) color = Color(0xFF0A0A0A)
) )
@ -239,19 +290,13 @@ fun AnimalProfileScreen(
horizontalArrangement = Arrangement.spacedBy(4.dp), horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
repeat(5) { index -> RatingStars(
Icon( rating = animal.rating ?: 0f,
painter = painterResource(R.drawable.ic_star), starSize = 12.dp
contentDescription = null, )
tint = if (index < (animal.rating ?: 0).toInt()) Color(
0xFFDE9A07
) else Color(0xFFDE9A07),
modifier = Modifier.size(12.dp)
)
}
Text( Text(
text = "${animal.rating} (${animal.ratingCount} Ratings)", text = "${animal.rating} (${animal.ratingCount} Ratings)",
fontSize = 10.sp, fontSize = AppTypography.Caption,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A) color = Color(0xFF0A0A0A)
) )
@ -267,13 +312,13 @@ fun AnimalProfileScreen(
) { ) {
Text( Text(
text = "About", text = "About",
fontSize = 16.sp, fontSize = AppTypography.Body,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = Color(0xFF09090B).copy(alpha = 0.5f) color = Color(0xFF09090B).copy(alpha = 0.5f)
) )
Text( Text(
text = animal.description ?: "", text = animal.description ?: "",
fontSize = 14.sp, fontSize = AppTypography.BodySmall,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
color = Color(0xFF09090B), color = Color(0xFF09090B),
lineHeight = 24.sp lineHeight = 24.sp
@ -297,24 +342,43 @@ fun AnimalProfileScreen(
// Ad space banner // Ad space banner
AdSpaceBanner() AdSpaceBanner()
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(64.dp))
} }
} }
FloatingActionBar( FloatingActionBar(
modifier = Modifier modifier = Modifier
.align(Alignment.BottomCenter) .align(Alignment.BottomCenter)
.padding(bottom = 24.dp) .padding(bottom = 10.dp)
.offset(y = (-20).dp) .offset(y = (-10).dp)
.zIndex(10f), // 👈 ensure it floats above everything .zIndex(10f), // 👈 ensure it floats above everything
onChatClick = { /* TODO */ }, onChatClick = { /* TODO */ },
onCallClick = { /* TODO */ }, onCallClick = { /* TODO */ },
onLocationClick = { /* 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 @Composable
fun AIScoreCircle(score: Float) { fun AIScoreCircle(score: Float) {
Box( Box(

View File

@ -1,12 +1,20 @@
package com.example.livingai_lg.ui.screens 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.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn 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.material3.Icon
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color 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.sampleAnimals
import com.example.livingai_lg.ui.models.userProfile import com.example.livingai_lg.ui.models.userProfile
import com.example.livingai_lg.R 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 @Composable
fun BuyScreen( fun BuyScreen(
@ -37,10 +51,13 @@ fun BuyScreen(
) { ) {
val selectedAnimalType = remember { mutableStateOf<String?>(null) } val selectedAnimalType = remember { mutableStateOf<String?>(null) }
val isSaved = remember { mutableStateOf(false) } val isSaved = remember { mutableStateOf(false) }
var showAddressSelector by remember { mutableStateOf(false) }
var selectedAddressId by remember { mutableStateOf<String?>(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( Box(
modifier = Modifier modifier = Modifier
@ -74,10 +91,8 @@ fun BuyScreen(
UserLocationHeader( UserLocationHeader(
user = userProfile, user = userProfile,
onAddressSelected = { onOpenAddressOverlay = { showAddressSelector = true },
// optional: reload listings, persist selection, etc. selectedAddressId = selectedAddressId?:"1",
},
onAddNewClick = {}
) )
// Right-side actions (notifications, etc.) // Right-side actions (notifications, etc.)
@ -86,6 +101,10 @@ fun BuyScreen(
contentDescription = "Notifications", contentDescription = "Notifications",
tint = Color.Black, tint = Color.Black,
modifier = Modifier.size(24.dp) modifier = Modifier.size(24.dp)
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
){ showNotifications = true }
) )
} }
// Row( // Row(
@ -129,8 +148,10 @@ fun BuyScreen(
.padding(horizontal = 22.dp), .padding(horizontal = 22.dp),
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
SortButton(onSortClick) SortButton(
FilterButton(onFilterClick) onClick = { showSortOverlay.value = true }
)
FilterButton(onClick = { showFilterOverlay.value = true })
} }
sampleAnimals.forEach { animal -> sampleAnimals.forEach { animal ->
@ -142,7 +163,8 @@ fun BuyScreen(
isSaved = isSaved.value, isSaved = isSaved.value,
onSavedChange = { isSaved.value = it }, onSavedChange = { isSaved.value = it },
onProductClick = { onProductClick(animal.id)}, onProductClick = { onProductClick(animal.id)},
onSellerClick = onSellerClick onSellerClick = onSellerClick,
onBookmarkClick = { showSavedPopup = true }
) )
Spacer(modifier = Modifier.height(16.dp)) 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
}
)
} }
} }

View File

@ -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.OptionCard
import com.example.livingai_lg.ui.components.backgrounds.StoreBackground import com.example.livingai_lg.ui.components.backgrounds.StoreBackground
import com.example.livingai_lg.R import com.example.livingai_lg.R
import com.example.livingai_lg.ui.theme.AppTypography
data class ServiceType( data class ServiceType(
val id: String, val id: String,
val title: String, val title: String,
val icon: Int, val icon: Any,
val iconTint: Color,
val backgroundColor: Color val backgroundColor: Color
) )
@ -46,30 +49,35 @@ fun ChooseServiceScreen(
id = "transport", id = "transport",
title = "Transport", title = "Transport",
icon = R.drawable.ic_shop, icon = R.drawable.ic_shop,
iconTint = Color.White,
backgroundColor = Color(0xFF9D4EDD) backgroundColor = Color(0xFF9D4EDD)
), ),
ServiceType( ServiceType(
id = "vet", id = "vet",
title = "Vet", title = "Vet",
icon = R.drawable.ic_bag, icon = R.drawable.ic_bag,
iconTint = Color.White,
backgroundColor = Color(0xFF3A86FF) backgroundColor = Color(0xFF3A86FF)
), ),
ServiceType( ServiceType(
id = "feed_supplier", id = "feed_supplier",
title = "Feed Supplier", title = "Feed Supplier",
icon = R.drawable.ic_spanner, icon = R.drawable.ic_spanner,
iconTint = Color.White,
backgroundColor = Color(0xFFFF5722) backgroundColor = Color(0xFFFF5722)
), ),
ServiceType( ServiceType(
id = "medicine_supplier", id = "medicine_supplier",
title = "Medicine Supplier", title = "Medicine Supplier",
icon = R.drawable.ic_shop2, icon = R.drawable.ic_shop2,
iconTint = Color.White,
backgroundColor = Color(0xFF4CAF50) backgroundColor = Color(0xFF4CAF50)
), ),
ServiceType( ServiceType(
id = "other", id = "other",
title = "Other", title = "Other",
icon = R.drawable.ic_other, icon = R.drawable.ic_other,
iconTint = Color.White,
backgroundColor = Color(0xFFD4A942) backgroundColor = Color(0xFFD4A942)
) )
) )
@ -101,7 +109,7 @@ fun ChooseServiceScreen(
) { ) {
Text( Text(
text = "Choose Service", text = "Choose Service",
fontSize = 36.sp, fontSize = AppTypography.Display,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A) color = Color(0xFF0A0A0A)
) )
@ -109,8 +117,8 @@ fun ChooseServiceScreen(
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( Text(
text = "Choose Service**", text = "Choose a Service",
fontSize = 16.sp, fontSize = AppTypography.Body,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A).copy(alpha = 0.8f) color = Color(0xFF0A0A0A).copy(alpha = 0.8f)
) )
@ -129,6 +137,7 @@ fun ChooseServiceScreen(
OptionCard( OptionCard(
label = service.title, label = service.title,
icon = service.icon, icon = service.icon,
iconTint = service.iconTint,
iconBackgroundColor = service.backgroundColor, iconBackgroundColor = service.backgroundColor,
onClick = { onClick = {
selectedService.value = service.id 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)
)
)
}
}
}

View File

@ -5,6 +5,8 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll 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.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf 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.components.backgrounds.StoreBackground
import com.example.livingai_lg.ui.models.profileTypes import com.example.livingai_lg.ui.models.profileTypes
import com.example.livingai_lg.ui.theme.AppTypography
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -75,7 +78,7 @@ fun CreateProfileScreen(
) { ) {
Text( Text(
text = "Create Profile", text = "Create Profile",
fontSize = 36.sp, fontSize = AppTypography.Display,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A) color = Color(0xFF0A0A0A)
) )
@ -84,7 +87,7 @@ fun CreateProfileScreen(
Text( Text(
text = "Choose Profile Type", text = "Choose Profile Type",
fontSize = 16.sp, fontSize = AppTypography.Body,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A).copy(alpha = 0.8f) color = Color(0xFF0A0A0A).copy(alpha = 0.8f)
) )
@ -105,7 +108,8 @@ fun CreateProfileScreen(
icon = profile.icon, icon = profile.icon,
iconBackgroundColor = profile.backgroundColor, iconBackgroundColor = profile.backgroundColor,
onClick = { onClick = {
updateProfile(profile.id) onProfileSelected(profile.id)
//updateProfile(profile.id)
} }
) )
} }

View File

@ -1,5 +1,9 @@
package com.example.livingai_lg.ui.screens 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.BorderStroke
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background import androidx.compose.foundation.background
@ -24,14 +28,14 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.livingai_lg.ui.components.DropdownInput import com.example.livingai_lg.ui.components.DropdownInput
import com.example.livingai_lg.ui.components.RangeFilter import com.example.livingai_lg.ui.components.RangeFilter
import com.example.livingai_lg.ui.theme.AppTypography
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun FilterScreen( fun FilterScreen(
onBackClick: () -> Unit = {}, onBackClick: () -> Unit = {},
onSkipClick: () -> Unit = {},
onSubmitClick: () -> Unit = {}, onSubmitClick: () -> Unit = {},
onCancelClick: () -> Unit = {} onCancelClick: () -> Unit = {},
) { ) {
var selectedAnimal by remember { mutableStateOf("") } var selectedAnimal by remember { mutableStateOf("") }
var selectedBreed by remember { mutableStateOf("") } var selectedBreed by remember { mutableStateOf("") }
@ -104,16 +108,6 @@ fun FilterScreen(
color = Color.Black 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 modifier = Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(horizontal = 16.dp) .padding(horizontal = 32.dp)
.padding(bottom = 24.dp), .padding(bottom = 24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp) verticalArrangement = Arrangement.spacedBy(24.dp)
) { ) {
@ -130,117 +124,43 @@ fun FilterScreen(
Column( Column(
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
Text( DropdownInput(
text = "Animal", label = "Animal",
fontSize = 16.sp, selected = selectedAnimal,
fontWeight = FontWeight.Normal, options = listOf("Cow", "Buffalo", "Goat", "Sheep"),
color = Color.Black
)
ExposedDropdownMenuBox(
expanded = animalExpanded, expanded = animalExpanded,
onExpandedChange = { animalExpanded = it } onExpandedChange = { animalExpanded = it },
) { onSelect = { item ->
OutlinedTextField( selectedAnimal = item
value = selectedAnimal, animalExpanded = false
onValueChange = {}, },
readOnly = true, placeholder = "Select Animal", // <--- half width
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
}
)
}
}
}
} }
// Breed Section // Breed Section
Column( Column(
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
Text( DropdownInput(
text = "Breed", label = "Breed",
fontSize = 16.sp, selected = selectedBreed,
fontWeight = FontWeight.Normal, options = listOf("Holstein", "Jersey", "Gir", "Sahiwal"),
color = Color.Black
)
ExposedDropdownMenuBox(
expanded = breedExpanded, expanded = breedExpanded,
onExpandedChange = { breedExpanded = it } onExpandedChange = { breedExpanded = it },
) { onSelect = { item ->
OutlinedTextField( selectedBreed = item
value = selectedBreed, breedExpanded = false
onValueChange = {}, },
readOnly = true, placeholder = "Select Breed", // <--- half width
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
}
)
}
}
}
} }
// Price and Age Row // Price and Age Row
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp) // 👈 reduce spacing
) {
Column( Column(
modifier = Modifier.weight(1f) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
RangeFilter( RangeFilter(
modifier = Modifier.fillMaxWidth(), // 👈 important modifier = Modifier.fillMaxWidth(), // 👈 important
@ -257,7 +177,8 @@ fun FilterScreen(
} }
Column( Column(
modifier = Modifier.weight(1f) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
RangeFilter( RangeFilter(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -273,17 +194,13 @@ fun FilterScreen(
} }
) )
} }
}
// Distance and Gender Row // Distance and Gender Row
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(64.dp)
) {
// Distance Section // Distance Section
Column( Column(
modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(8.dp)
verticalArrangement = Arrangement.spacedBy(3.dp)
) { ) {
DropdownInput( DropdownInput(
label = "Distance", label = "Distance",
@ -301,8 +218,7 @@ fun FilterScreen(
// Gender Section // Gender Section
Column( Column(
modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(8.dp)
verticalArrangement = Arrangement.spacedBy(3.dp)
) { ) {
DropdownInput( DropdownInput(
label = "Gender", label = "Gender",
@ -319,7 +235,7 @@ fun FilterScreen(
} }
}
// Pregnancy Status Section // Pregnancy Status Section
Column( Column(
@ -327,7 +243,7 @@ fun FilterScreen(
) { ) {
Text( Text(
text = "Pregnancy Status", text = "Pregnancy Status",
fontSize = 16.sp, fontSize = AppTypography.Body,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
color = Color(0xFF364153) color = Color(0xFF364153)
) )
@ -352,12 +268,9 @@ fun FilterScreen(
} }
} }
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp) // 👈 reduce spacing
) {
Column( Column(
modifier = Modifier.weight(1f) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
RangeFilter( RangeFilter(
modifier = Modifier.fillMaxWidth(), // 👈 important modifier = Modifier.fillMaxWidth(), // 👈 important
@ -374,7 +287,7 @@ fun FilterScreen(
} }
Column( Column(
modifier = Modifier.weight(1f) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
RangeFilter( RangeFilter(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -390,7 +303,7 @@ fun FilterScreen(
} }
) )
} }
}
// Calving Number Section // Calving Number Section
Column( Column(
@ -469,7 +382,7 @@ fun FilterScreen(
) { ) {
Text( Text(
text = "Submit", text = "Submit",
fontSize = 16.sp, fontSize = AppTypography.Body,
fontWeight = FontWeight.Normal fontWeight = FontWeight.Normal
) )
} }
@ -492,7 +405,7 @@ fun FilterScreen(
) { ) {
Text( Text(
text = "Cancel", text = "Cancel",
fontSize = 16.sp, fontSize = AppTypography.Body,
fontWeight = FontWeight.Normal fontWeight = FontWeight.Normal
) )
} }

View File

@ -23,6 +23,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.livingai_lg.ui.components.MediaPickerCard import com.example.livingai_lg.ui.components.MediaPickerCard
import com.example.livingai_lg.ui.models.NewListingFormState import com.example.livingai_lg.ui.models.NewListingFormState
import com.example.livingai_lg.ui.theme.AppTypography
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -56,7 +57,7 @@ fun NewListingScreen(
title = { title = {
Text( Text(
text = "New Listing", text = "New Listing",
fontSize = 24.sp, fontSize = AppTypography.Display,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A) color = Color(0xFF0A0A0A)
) )

View File

@ -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<AppNotification>,
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)
)
}
}
}

View File

@ -94,18 +94,6 @@ fun PostSaleSurveyScreen(
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
) { onBackClick() } ) { 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)) Spacer(modifier = Modifier.height(26.dp))

View File

@ -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
)
}
}
}

View File

@ -1,19 +1,29 @@
package com.example.livingai_lg.ui.screens 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.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons 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.ArrowBack
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
@ -39,12 +49,10 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.livingai_lg.ui.components.SortItem import com.example.livingai_lg.ui.components.SortItem
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun SortScreen( fun SortScreen(
onBackClick: () -> Unit = {}, onBackClick: () -> Unit = {},
onSkipClick: () -> Unit = {},
onApplyClick: (List<SortField>) -> Unit = {}, onApplyClick: (List<SortField>) -> Unit = {},
onCancelClick: () -> Unit = {} onCancelClick: () -> Unit = {}
) { ) {
@ -103,18 +111,14 @@ fun SortScreen(
) { ) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = onBackClick) { IconButton(onClick = onBackClick) {
Icon(Icons.Default.ArrowBack, null) Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "Back",
tint = Color(0xFF0A0A0A)
)
} }
Text("Sort By", fontSize = 32.sp) Text("Sort By", fontSize = 32.sp)
} }
TextButton(onClick = onSkipClick) {
Text(
"Skip",
textDecoration = TextDecoration.Underline,
fontSize = 20.sp
)
}
} }
Column( Column(

View File

@ -39,7 +39,7 @@ import kotlinx.coroutines.launch
@Composable @Composable
fun SignInScreen( fun SignInScreen(
onSignUpClick: () -> Unit = {}, onSignUpClick: () -> Unit = {},
onSignInClick: (phone: String, name: String) -> Unit = {}, onSignInClick: (phone: String, name: String) -> Unit = {_,_->},
) { ) {
val phoneNumber = remember { mutableStateOf("") } val phoneNumber = remember { mutableStateOf("") }
val isValid = phoneNumber.value.length == 10 val isValid = phoneNumber.value.length == 10

View File

@ -52,7 +52,7 @@ private fun String.isValidPhoneNumber(): Boolean {
@Composable @Composable
fun SignUpScreen( fun SignUpScreen(
onSignInClick: () -> Unit = {}, onSignInClick: () -> Unit = {},
onSignUpClick: (phone: String, name: String) -> Unit = {} onSignUpClick: (phone: String, name: String) -> Unit = {_,_->}
) { ) {
var formData by remember { mutableStateOf(SignUpFormData()) } var formData by remember { mutableStateOf(SignUpFormData()) }

View File

@ -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)
)
}
}
}

View File

@ -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)
)
}
}
}
}
}

View File

@ -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
)
}
}
}
}
}

View File

@ -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
)
}
}

View File

@ -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
}