New screens and improvements
This commit is contained in:
parent
4f8ae88820
commit
6b634f40b7
|
|
@ -4,14 +4,9 @@ import android.os.Bundle
|
|||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavType
|
||||
|
|
@ -19,17 +14,16 @@ import androidx.navigation.compose.NavHost
|
|||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.example.livingai_lg.ui.AuthState
|
||||
import com.example.livingai_lg.ui.MainViewModel
|
||||
import com.example.livingai_lg.ui.MainViewModelFactory
|
||||
import com.example.livingai_lg.ui.login.*
|
||||
import com.example.livingai_lg.ui.login_legacy.*
|
||||
import com.example.livingai_lg.ui.navigation.AppNavigation
|
||||
import com.example.livingai_lg.ui.theme.FarmMarketplaceTheme
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
//enableEdgeToEdge()
|
||||
setContent {
|
||||
FarmMarketplaceTheme {
|
||||
val mainViewModel: MainViewModel = viewModel(factory = MainViewModelFactory(LocalContext.current))
|
||||
|
|
@ -54,6 +48,8 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: remove the old code after testing new stuff
|
||||
@Composable
|
||||
fun AuthNavigation() {
|
||||
val navController = rememberNavController()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6,15 +6,22 @@ import androidx.compose.foundation.border
|
|||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.LocationOn
|
||||
import androidx.compose.material.icons.outlined.Visibility
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shadow
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -26,6 +33,7 @@ import com.example.livingai_lg.ui.utils.formatDistance
|
|||
import com.example.livingai_lg.ui.utils.formatPrice
|
||||
import com.example.livingai_lg.ui.utils.formatViews
|
||||
import com.example.livingai_lg.R
|
||||
import com.example.livingai_lg.ui.theme.AppTypography
|
||||
|
||||
@Composable
|
||||
fun BuyAnimalCard(
|
||||
|
|
@ -34,6 +42,7 @@ fun BuyAnimalCard(
|
|||
onSavedChange: (Boolean) -> Unit,
|
||||
onProductClick: () -> Unit,
|
||||
onSellerClick:(sellerId: String)-> Unit,
|
||||
onBookmarkClick: () -> Unit
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
|
@ -46,163 +55,204 @@ fun BuyAnimalCard(
|
|||
Color(0xFF000000).copy(alpha = 0.1f),
|
||||
RoundedCornerShape(14.dp)
|
||||
)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onProductClick
|
||||
)
|
||||
|
||||
) {
|
||||
Column {
|
||||
// Image
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(257.dp)
|
||||
) {
|
||||
ImageCarousel(
|
||||
imageUrls = product.imageUrl ?: emptyList(),
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
|
||||
// Views
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart)
|
||||
.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_view),
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(formatViews(product.views), fontSize = 10.sp, color = Color.White)
|
||||
}
|
||||
|
||||
// Distance
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_location),
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(12.dp)
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(formatDistance( product.distance), fontSize = 16.sp, color = Color.White)
|
||||
}
|
||||
}
|
||||
|
||||
// Content
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
product.name?: "",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
Text(
|
||||
formatPrice(product.price),
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
|
||||
Column(horizontalAlignment = Alignment.End,modifier = Modifier.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
onSellerClick(product.sellerId?:"")
|
||||
}) {
|
||||
Text("Sold By: ${product.sellerName}", fontSize = 13.sp)
|
||||
Text(product.sellerType?: "???", fontSize = 13.sp)
|
||||
}
|
||||
}
|
||||
|
||||
// Rating
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_star),
|
||||
contentDescription = null,
|
||||
tint = Color(0xFFDE9A07),
|
||||
modifier = Modifier.size(8.dp)
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(
|
||||
"${product.rating} (${product.ratingCount} Ratings)",
|
||||
fontSize = 8.sp
|
||||
)
|
||||
}
|
||||
|
||||
// Badges
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp))
|
||||
.padding(horizontal = 8.dp, vertical = 6.dp)
|
||||
) {
|
||||
val scoreString = "AI Score: ${product.aiScore?: 0}"
|
||||
Text(scoreString, fontSize = 12.sp)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp))
|
||||
.padding(horizontal = 8.dp, vertical = 6.dp)
|
||||
) {
|
||||
Text("placeholder", fontSize = 12.sp)
|
||||
}
|
||||
if(product.milkCapacity != null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp))
|
||||
.padding(horizontal = 8.dp, vertical = 6.dp)
|
||||
) {
|
||||
Text("Milk Capacity: ${product.milkCapacity}L", fontSize = 12.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Description
|
||||
Text(
|
||||
product.description?: "",
|
||||
fontSize = 14.sp,
|
||||
color = Color(0xFF717182),
|
||||
lineHeight = 20.sp
|
||||
modifier = Modifier.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onProductClick
|
||||
)
|
||||
) {
|
||||
|
||||
// Actions
|
||||
// Image
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(257.dp)
|
||||
) {
|
||||
ImageCarousel(
|
||||
imageUrls = product.imageUrl ?: emptyList(),
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
|
||||
// Views
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart)
|
||||
.padding(6.dp)
|
||||
.shadow(
|
||||
elevation = 6.dp,
|
||||
shape = RoundedCornerShape(50),
|
||||
ambientColor = Color.Black.copy(alpha = 0.4f),
|
||||
spotColor = Color.Black.copy(alpha = 0.4f)
|
||||
)
|
||||
.background(
|
||||
color = Color.Black.copy(alpha = 0.35f), // 👈 light but effective
|
||||
shape = RoundedCornerShape(50)
|
||||
)
|
||||
.padding(horizontal = 6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Visibility,
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(16.dp).shadow(
|
||||
elevation = 6.dp,
|
||||
shape = CircleShape,
|
||||
clip = false
|
||||
)
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(formatViews(product.views), fontSize = AppTypography.Caption, color = Color.White,style = LocalTextStyle.current.copy(
|
||||
))
|
||||
}
|
||||
|
||||
// Distance
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.padding(6.dp)
|
||||
.shadow(
|
||||
elevation = 6.dp,
|
||||
shape = RoundedCornerShape(50),
|
||||
ambientColor = Color.Black.copy(alpha = 0.4f),
|
||||
spotColor = Color.Black.copy(alpha = 0.4f)
|
||||
)
|
||||
.background(
|
||||
color = Color.Black.copy(alpha = 0.35f), // 👈 light but effective
|
||||
shape = RoundedCornerShape(50)
|
||||
)
|
||||
.padding(horizontal = 6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.LocationOn,
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(12.dp)
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(
|
||||
formatDistance(product.distance),
|
||||
fontSize = AppTypography.Body,
|
||||
color = Color.White,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Content
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
product.name ?: "",
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
Text(
|
||||
formatPrice(product.price),
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.End, modifier = Modifier.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
onSellerClick(product.sellerId ?: "")
|
||||
}) {
|
||||
Text("Sold By: ${product.sellerName}", fontSize = AppTypography.BodySmall)
|
||||
Text(product.sellerType ?: "???", fontSize = AppTypography.BodySmall)
|
||||
}
|
||||
}
|
||||
|
||||
// Rating
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
RatingStars(product.rating?:0f, starSize = 12.dp)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(
|
||||
"${product.rating} (${product.ratingCount} Ratings)",
|
||||
fontSize = AppTypography.Caption
|
||||
)
|
||||
}
|
||||
|
||||
// Badges
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp))
|
||||
.padding(horizontal = 8.dp, vertical = 6.dp)
|
||||
) {
|
||||
Row {
|
||||
Text("AI Score: ", fontSize = AppTypography.Caption)
|
||||
val scoreString = "${product.aiScore ?: 0}"
|
||||
Text(scoreString, fontSize = AppTypography.Caption, fontWeight = FontWeight.Bold)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (product.milkCapacity != null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp))
|
||||
.padding(horizontal = 8.dp, vertical = 6.dp)
|
||||
) {
|
||||
Row {
|
||||
Text(
|
||||
"Milk Capacity:",
|
||||
fontSize = AppTypography.Caption
|
||||
)
|
||||
Text(
|
||||
"${product.milkCapacity}L",
|
||||
fontSize = AppTypography.Caption,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Description
|
||||
Text(
|
||||
product.description ?: "",
|
||||
fontSize = 14.sp,
|
||||
color = Color(0xFF717182),
|
||||
lineHeight = 20.sp
|
||||
)
|
||||
|
||||
// Actions
|
||||
// Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
// ActionButton(R.drawable.ic_chat, "Chat")
|
||||
// ActionButton(R.drawable.ic_phone, "Call")
|
||||
// ActionButton(R.drawable.ic_location, "Location")
|
||||
// ActionButton(R.drawable.ic_bookmark_plus, "Bookmark")
|
||||
// }
|
||||
FloatingActionBar(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 12.dp)
|
||||
.zIndex(10f), // 👈 ensure it floats above everything
|
||||
onChatClick = { /* TODO */ },
|
||||
onCallClick = { /* TODO */ },
|
||||
onLocationClick = { /* TODO */ },
|
||||
onBookmarkClick = { /* TODO */ }
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
FloatingActionBar(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 12.dp)
|
||||
.zIndex(10f), // 👈 ensure it floats above everything
|
||||
showContainer = false,
|
||||
onChatClick = { /* TODO */ },
|
||||
onCallClick = { /* TODO */ },
|
||||
onLocationClick = { /* TODO */ },
|
||||
onBookmarkClick = onBookmarkClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import androidx.compose.foundation.clickable
|
|||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -16,6 +18,7 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.theme.AppTypography
|
||||
import com.example.livingai_lg.ui.theme.FarmTextDark
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
|
@ -38,7 +41,7 @@ fun DropdownInput(
|
|||
if (label != null) {
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = 16.sp,
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = FarmTextDark
|
||||
)
|
||||
|
|
@ -78,15 +81,16 @@ fun DropdownInput(
|
|||
// Custom placeholder support
|
||||
Text(
|
||||
text = selected.ifEmpty { placeholder },
|
||||
fontSize = 16.sp,
|
||||
fontSize = AppTypography.Body,
|
||||
color = if (selected.isEmpty()) Color(0xFF99A1AF) else FarmTextDark
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "▼",
|
||||
fontSize = 12.sp,
|
||||
color = FarmTextDark
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowDropDown,
|
||||
contentDescription = "Dropdown",
|
||||
tint = FarmTextDark
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -99,7 +103,7 @@ fun DropdownInput(
|
|||
options.forEach { item ->
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(item, fontSize = 16.sp, color = FarmTextDark)
|
||||
Text(item, fontSize = AppTypography.Body, color = FarmTextDark)
|
||||
},
|
||||
onClick = {
|
||||
onSelect(item)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import com.example.livingai_lg.R
|
|||
|
||||
@Composable
|
||||
fun FilterButton(
|
||||
onFilterClick: () -> Unit
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
|
@ -37,7 +37,7 @@ fun FilterButton(
|
|||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onFilterClick,
|
||||
onClick = onClick,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,26 @@
|
|||
package com.example.livingai_lg.ui.components
|
||||
|
||||
import android.media.Image
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Chat
|
||||
import androidx.compose.material.icons.outlined.BookmarkAdd
|
||||
import androidx.compose.material.icons.outlined.Chat
|
||||
import androidx.compose.material.icons.outlined.LocationOn
|
||||
import androidx.compose.material.icons.outlined.Phone
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.example.livingai_lg.R
|
||||
|
|
@ -16,35 +28,44 @@ import com.example.livingai_lg.R
|
|||
@Composable
|
||||
fun FloatingActionBar(
|
||||
modifier: Modifier = Modifier,
|
||||
showContainer: Boolean = true,
|
||||
onChatClick: () -> Unit = {},
|
||||
onCallClick: () -> Unit = {},
|
||||
onLocationClick: () -> Unit = {},
|
||||
onBookmarkClick: () -> Unit = {}
|
||||
) {
|
||||
val containerModifier =
|
||||
if (showContainer) {
|
||||
Modifier
|
||||
.shadow(
|
||||
elevation = 12.dp,
|
||||
shape = RoundedCornerShape(50),
|
||||
clip = false
|
||||
)
|
||||
.background(
|
||||
color = Color.White,
|
||||
shape = RoundedCornerShape(50)
|
||||
)
|
||||
.padding(horizontal = 12.dp, vertical = 12.dp)
|
||||
} else {
|
||||
Modifier // no background, no shadow
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
//.padding(horizontal = 24.dp)
|
||||
.shadow(
|
||||
elevation = 12.dp,
|
||||
shape = RoundedCornerShape(50), // pill shape
|
||||
clip = false
|
||||
)
|
||||
.background(
|
||||
color = Color.White,
|
||||
shape = RoundedCornerShape(50)
|
||||
)
|
||||
.padding(vertical = 12.dp)
|
||||
.then(containerModifier)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
FloatingActionIcon(R.drawable.ic_chat, onChatClick)
|
||||
FloatingActionIcon(R.drawable.ic_phone, onCallClick)
|
||||
FloatingActionIcon(R.drawable.ic_location, onLocationClick)
|
||||
FloatingActionIcon(R.drawable.ic_bookmark_plus, onBookmarkClick)
|
||||
FloatingActionIcon(Icons.AutoMirrored.Outlined.Chat, onChatClick)
|
||||
FloatingActionIcon(Icons.Outlined.Phone, onCallClick)
|
||||
FloatingActionIcon(Icons.Outlined.LocationOn, onLocationClick)
|
||||
FloatingActionIcon(Icons.Outlined.BookmarkAdd, onBookmarkClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +73,7 @@ fun FloatingActionBar(
|
|||
|
||||
@Composable
|
||||
private fun FloatingActionIcon(
|
||||
iconRes: Int,
|
||||
icon: ImageVector,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Box(
|
||||
|
|
@ -63,11 +84,16 @@ private fun FloatingActionIcon(
|
|||
shape = RoundedCornerShape(24.dp),
|
||||
clip = false
|
||||
)
|
||||
.background(Color.White, RoundedCornerShape(24.dp)),
|
||||
.background(Color.White, RoundedCornerShape(24.dp))
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onClick
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(iconRes),
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier.size(22.dp)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -16,16 +16,19 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.theme.AppTypography
|
||||
|
||||
@Composable
|
||||
fun OptionCard(
|
||||
label: String,
|
||||
icon: Int,
|
||||
icon: Any,
|
||||
iconBackgroundColor: Color,
|
||||
iconTint: Color = Color.Unspecified,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Box(
|
||||
|
|
@ -54,17 +57,28 @@ fun OptionCard(
|
|||
.background(iconBackgroundColor, RoundedCornerShape(14.dp)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = icon),
|
||||
contentDescription = label,
|
||||
tint = Color.Unspecified
|
||||
)
|
||||
when (icon) {
|
||||
is ImageVector -> {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = label,
|
||||
tint = iconTint
|
||||
)
|
||||
}
|
||||
is Int -> {
|
||||
Icon(
|
||||
painter = painterResource(icon),
|
||||
contentDescription = label,
|
||||
tint = Color.Unspecified
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Label
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = 16.sp,
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF0A0A0A),
|
||||
modifier = Modifier.weight(1f)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,17 +25,23 @@ import androidx.compose.ui.unit.sp
|
|||
import com.example.livingai_lg.R
|
||||
|
||||
@Composable
|
||||
fun SortButton(onSortClick: () -> Unit) {
|
||||
fun SortButton(
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.height(36.dp)
|
||||
.border(1.078.dp, Color(0xFF000000).copy(alpha = 0.1f), RoundedCornerShape(8.dp))
|
||||
.border(
|
||||
1.078.dp,
|
||||
Color(0xFF000000).copy(alpha = 0.1f),
|
||||
RoundedCornerShape(8.dp)
|
||||
)
|
||||
.background(Color.White, RoundedCornerShape(8.dp))
|
||||
.padding(horizontal = 8.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onSortClick,
|
||||
onClick = onClick
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
|
|
@ -53,4 +59,4 @@ fun SortButton(onSortClick: () -> Unit) {
|
|||
color = Color(0xFF0A0A0A)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,23 +31,15 @@ import com.example.livingai_lg.R
|
|||
@Composable
|
||||
fun UserLocationHeader(
|
||||
user: UserProfile,
|
||||
selectedAddressId: String,
|
||||
modifier: Modifier = Modifier,
|
||||
onAddressSelected: (UserAddress) -> Unit = {},
|
||||
onAddNewClick: () -> Unit = {} // future navigation hook
|
||||
onOpenAddressOverlay: () -> Unit
|
||||
) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
||||
var selectedAddress by remember {
|
||||
mutableStateOf(
|
||||
user.addresses.firstOrNull { it.isPrimary }
|
||||
?: user.addresses.first()
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier.wrapContentWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
val selectedAddress = user.addresses.find { it.id == selectedAddressId } ?: user.addresses.first()
|
||||
|
||||
// Profile image
|
||||
Box(
|
||||
|
|
@ -75,79 +67,38 @@ fun UserLocationHeader(
|
|||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
// Anchor ONLY the text section
|
||||
Box {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { expanded = true }
|
||||
// Address + arrow (click opens overlay)
|
||||
Column(
|
||||
modifier = Modifier.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = selectedAddress.name,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color.Black
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowDown,
|
||||
contentDescription = null,
|
||||
tint = Color.Black,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
}
|
||||
|
||||
onOpenAddressOverlay()
|
||||
}
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = selectedAddress.address,
|
||||
fontSize = 13.sp,
|
||||
color = Color.Black.copy(alpha = 0.7f),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
text = selectedAddress.name,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color.Black
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowDown,
|
||||
contentDescription = null,
|
||||
tint = Color.Black,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
}
|
||||
|
||||
// Dropdown appears BELOW name/address
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false }
|
||||
) {
|
||||
user.addresses.forEach { address ->
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = address.name,
|
||||
fontWeight = if (address == selectedAddress)
|
||||
FontWeight.SemiBold
|
||||
else FontWeight.Normal
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
selectedAddress = address
|
||||
expanded = false
|
||||
onAddressSelected(address)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
// Add New option
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = "Add New +",
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF007BFF)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
expanded = false
|
||||
onAddNewClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = selectedAddress.address,
|
||||
fontSize = 13.sp,
|
||||
color = Color.Black.copy(alpha = 0.7f),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.example.livingai_lg.ui.login
|
||||
package com.example.livingai_lg.ui.login_legacy
|
||||
|
||||
import android.app.Application
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
|
|
@ -14,7 +13,6 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.example.livingai_lg.ui.login
|
||||
package com.example.livingai_lg.ui.login_legacy
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -11,7 +11,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
package com.example.livingai_lg.ui.login
|
||||
package com.example.livingai_lg.ui.login_legacy
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.*
|
||||
|
|
@ -10,7 +9,6 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.example.livingai_lg.ui.login
|
||||
package com.example.livingai_lg.ui.login_legacy
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -14,7 +14,6 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.example.livingai_lg.ui.login
|
||||
package com.example.livingai_lg.ui.login_legacy
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -15,7 +15,6 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
|
@ -1,12 +1,10 @@
|
|||
package com.example.livingai_lg.ui.login
|
||||
package com.example.livingai_lg.ui.login_legacy
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -25,31 +25,42 @@ data class Animal(
|
|||
val sampleAnimals = listOf(
|
||||
Animal(
|
||||
id = "1",
|
||||
name = "Golden Retriever",
|
||||
price = 80000,
|
||||
imageUrl = listOf("https://api.builder.io/api/v1/image/assets/TEMP/8b3d82a183db9ded7741b4b8bf0cd81fb2733b89?width=686"),
|
||||
distance = 2500L,// miles away
|
||||
views = 94,//Views
|
||||
name = "Rita",
|
||||
age = 45,
|
||||
breed = "Gir",
|
||||
location = "Punjab",
|
||||
distance = 12000,
|
||||
imageUrl = listOf("https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2F4.bp.blogspot.com%2F_tecSnxaePMo%2FTLLVknW8dOI%2FAAAAAAAAACo%2F_kd1ZNBXU1o%2Fs1600%2FGIR%2CGujrat.jpg&f=1&nofb=1&ipt=da6ba1d040c396b64d3f08cc99998f66200dcd6c001e4a56def143ab3d1a87ea","https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fcpimg.tistatic.com%2F4478702%2Fb%2F4%2Fgir-cow.jpg&f=1&nofb=1&ipt=19bf391461480585c786d01433d863a383c60048ac2ce063ce91f173e215205d"),
|
||||
views = 9001,
|
||||
aiScore = 0.80f,
|
||||
price = 120000,
|
||||
isFairPrice = true,
|
||||
rating = 4.7f,
|
||||
ratingCount = 2076,
|
||||
sellerId = "1",
|
||||
sellerName = "Seller 1",
|
||||
sellerType = "Wholeseller",
|
||||
rating = 4.5f,
|
||||
ratingCount = 2076,
|
||||
description = "Friendly and energetic companion looking for an active family.",
|
||||
description = "Premium Gir dairy cow in excellent health condition. High-yielding milk producer with consistent output and gentle temperament, making her ideal for commercial dairy operations or family farms.",
|
||||
displayLocation = "Mediatek Pashu Mela, Marathalli, Bangalore (13 km)",
|
||||
milkCapacity = 3.2f
|
||||
),
|
||||
Animal(
|
||||
id = "2",
|
||||
name = "Mudkip",
|
||||
price = 999999999,
|
||||
imageUrl = listOf("https://img.pokemondb.net/artwork/large/mudkip.jpg", "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fcdna.artstation.com%2Fp%2Fassets%2Fimages%2Fimages%2F012%2F524%2F022%2Flarge%2Fanna-ben-david-poster-size-mudkip.jpg%3F1535218851&f=1&nofb=1&ipt=aac92abbad440468461cbec5d9208d4258e5f4ed72e18a9ae17cfd6e0da1ce9f"),
|
||||
name = "Sahi",
|
||||
price = 95000,
|
||||
age = 35,
|
||||
breed = "Sahiwal",
|
||||
location = "Punjab",
|
||||
isFairPrice = true,
|
||||
imageUrl = listOf("https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fcdnbbsr.s3waas.gov.in%2Fs3a5a61717dddc3501cfdf7a4e22d7dbaa%2Fuploads%2F2020%2F09%2F2020091812-1024x680.jpg&f=1&nofb=1&ipt=bb426406b3747e54151e4812472e203f33922fa3b4e11c4feef9aa59a5733146"),
|
||||
distance = 0L,
|
||||
views = 100000,
|
||||
views = 100,
|
||||
sellerId = "1",
|
||||
sellerName = "Seller ???",
|
||||
sellerType = "Wholeseller",
|
||||
rating = 5f,
|
||||
ratingCount = 2076,
|
||||
description = "Friendly and energetic companion looking for an active family.",
|
||||
milkCapacity = 2.5f
|
||||
),
|
||||
Animal(
|
||||
id = "3",
|
||||
|
|
|
|||
|
|
@ -13,15 +13,13 @@ val mainBottomNavItems = listOf(
|
|||
BottomNavItemData("Home", R.drawable.ic_home , AppScreen.BUY_ANIMALS),
|
||||
BottomNavItemData("Sell", R.drawable.ic_tag, AppScreen.CREATE_ANIMAL_LISTING),
|
||||
// TODO:
|
||||
BottomNavItemData("Chats", R.drawable.ic_chat, AppScreen.CREATE_PROFILE),
|
||||
BottomNavItemData("Chats", R.drawable.ic_chat, AppScreen.CHATS),
|
||||
BottomNavItemData("Services", R.drawable.ic_config, AppScreen.CREATE_PROFILE),
|
||||
BottomNavItemData("Mandi", R.drawable.ic_market, AppScreen.CREATE_PROFILE)
|
||||
)
|
||||
|
||||
val chatBottomNavItems = listOf(
|
||||
BottomNavItemData("Home", R.drawable.ic_home ,"home"),
|
||||
BottomNavItemData("Sell", R.drawable.ic_tag, "sell"),
|
||||
BottomNavItemData("Contacts", R.drawable.ic_home ,"home"),
|
||||
BottomNavItemData("Calls", R.drawable.ic_tag, "sell"),
|
||||
BottomNavItemData("Chats", R.drawable.ic_chat, "chats"),
|
||||
BottomNavItemData("Services", R.drawable.ic_config, "services"),
|
||||
BottomNavItemData("Mandi", R.drawable.ic_market, "mandi")
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
)
|
||||
|
|
@ -1,13 +1,16 @@
|
|||
package com.example.livingai_lg.ui.models
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import com.example.livingai_lg.R
|
||||
|
||||
data class ProfileType(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val icon: Int,
|
||||
val backgroundColor: Color
|
||||
val icon: Any,
|
||||
val iconTint: Color = Color.Unspecified,
|
||||
val backgroundColor: Color,
|
||||
)
|
||||
|
||||
val profileTypes = listOf(
|
||||
|
|
@ -15,24 +18,28 @@ val profileTypes = listOf(
|
|||
id = "buyer_seller",
|
||||
title = "I'm a Buyer/Seller",
|
||||
icon = R.drawable.ic_shop,
|
||||
iconTint = Color.White,
|
||||
backgroundColor = Color(0xFF9D4EDD)
|
||||
),
|
||||
ProfileType(
|
||||
id = "wholesale_trader",
|
||||
title = "I'm a Wholesale Trader",
|
||||
icon = R.drawable.ic_bag,
|
||||
iconTint = Color.White,
|
||||
backgroundColor = Color(0xFF3A86FF)
|
||||
),
|
||||
ProfileType(
|
||||
id = "service_provider",
|
||||
title = "I'm a Service Provider",
|
||||
icon = R.drawable.ic_spanner,
|
||||
iconTint = Color.White,
|
||||
backgroundColor = Color(0xFFFF5722)
|
||||
),
|
||||
ProfileType(
|
||||
id = "mandi_host",
|
||||
title = "I'm a Mandi Host",
|
||||
icon = R.drawable.ic_shop2,
|
||||
iconTint = Color.White,
|
||||
backgroundColor = Color(0xFF4CAF50)
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.example.livingai_lg.ui.models
|
||||
|
||||
data class UserAddress(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val address: String,
|
||||
val isPrimary: Boolean = false
|
||||
|
|
@ -18,11 +19,13 @@ val userProfile =
|
|||
profileImageUrl = "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fstatic.vecteezy.com%2Fsystem%2Fresources%2Fpreviews%2F014%2F016%2F808%2Fnon_2x%2Findian-farmer-face-vector.jpg&f=1&nofb=1&ipt=c352fec591428aebefe6cd263d2958765e85d4da69cce3c46b725ba2ff7d3448",
|
||||
addresses = listOf(
|
||||
UserAddress(
|
||||
id = "1",
|
||||
name = "Home",
|
||||
address = "205 1st floor 7th cross 27th main, PW",
|
||||
isPrimary = true
|
||||
),
|
||||
UserAddress(
|
||||
id = "2",
|
||||
name = "Farm",
|
||||
address = "2nd block, MG Farms"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import androidx.compose.foundation.layout.Box
|
|||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.navigation.NavController
|
||||
|
|
@ -28,7 +31,8 @@ import com.example.livingai_lg.ui.screens.auth.LandingScreen
|
|||
import com.example.livingai_lg.ui.screens.auth.OtpScreen
|
||||
import com.example.livingai_lg.ui.screens.auth.SignInScreen
|
||||
import com.example.livingai_lg.ui.screens.auth.SignUpScreen
|
||||
|
||||
import com.example.livingai_lg.ui.navigation.authNavGraph
|
||||
import com.example.livingai_lg.ui.navigation.mainNavGraph
|
||||
|
||||
object AppScreen {
|
||||
const val LANDING = "landing"
|
||||
|
|
@ -55,6 +59,19 @@ object AppScreen {
|
|||
|
||||
const val POST_SALE_SURVEY = "post_sale_survey"
|
||||
|
||||
const val SAVED_LISTINGS = "saved_listings"
|
||||
|
||||
const val CONTACTS = "contacts"
|
||||
|
||||
const val CALLS = "calls"
|
||||
|
||||
const val CHAT = "chat"
|
||||
|
||||
const val CHATS = "chats"
|
||||
|
||||
fun chats(contact: String) =
|
||||
"$CHAT/$contact"
|
||||
|
||||
fun otp(phone: String, name: String) =
|
||||
"$OTP/$phone/$name"
|
||||
|
||||
|
|
@ -74,23 +91,55 @@ object AppScreen {
|
|||
|
||||
fun saleArchive(saleId: String) =
|
||||
"$SALE_ARCHIVE/$saleId"
|
||||
|
||||
}
|
||||
|
||||
object Graph {
|
||||
const val AUTH = "auth"
|
||||
const val MAIN = "auth"
|
||||
|
||||
fun auth(route: String)=
|
||||
"$AUTH/$route"
|
||||
|
||||
fun main(route: String)=
|
||||
"$MAIN/$route"
|
||||
}
|
||||
@Composable
|
||||
fun AppNavigation(
|
||||
authState: AuthState
|
||||
) {
|
||||
val navController = rememberNavController()
|
||||
var isLoggedIn = false;
|
||||
|
||||
when (authState) {
|
||||
is AuthState.Unauthenticated -> {AuthNavGraph()}
|
||||
is AuthState.Authenticated -> {MainNavGraph()}
|
||||
is AuthState.Unauthenticated -> {isLoggedIn = false; }
|
||||
is AuthState.Authenticated -> {isLoggedIn = true;}
|
||||
is AuthState.Unknown -> {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
// AuthState.Loading -> SplashScreen()
|
||||
}
|
||||
// val navController = rememberNavController()
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = if (isLoggedIn) Graph.MAIN else Graph.AUTH
|
||||
) {
|
||||
authNavGraph(navController)
|
||||
mainNavGraph(navController)
|
||||
}
|
||||
// MainNavGraph(navController)
|
||||
// AuthNavGraph(navController)
|
||||
// when (authState) {
|
||||
// is AuthState.Unauthenticated -> {AuthNavGraph()}
|
||||
// is AuthState.Authenticated -> {MainNavGraph()}
|
||||
// is AuthState.Unknown -> {
|
||||
// Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
// CircularProgressIndicator()
|
||||
// }
|
||||
// }
|
||||
// AuthState.Loading -> SplashScreen()
|
||||
// }
|
||||
|
||||
// val onNavClick: (String) -> Unit = { route ->
|
||||
// val currentRoute =
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
package com.example.livingai_lg.ui.navigation
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.navigation
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.example.livingai_lg.ui.screens.SaleArchiveScreen
|
||||
|
|
@ -13,19 +16,16 @@ import com.example.livingai_lg.ui.screens.auth.OtpScreen
|
|||
import com.example.livingai_lg.ui.screens.auth.SignInScreen
|
||||
import com.example.livingai_lg.ui.screens.auth.SignUpScreen
|
||||
|
||||
@Composable
|
||||
fun AuthNavGraph(
|
||||
navController: NavHostController = rememberNavController()
|
||||
) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
fun NavGraphBuilder.authNavGraph(navController: NavController) {
|
||||
navigation(
|
||||
route = Graph.AUTH,
|
||||
startDestination = AppScreen.LANDING
|
||||
) {
|
||||
composable(AppScreen.LANDING) {
|
||||
LandingScreen(
|
||||
onSignUpClick = { navController.navigate(AppScreen.SIGN_UP) },
|
||||
onSignInClick = { navController.navigate(AppScreen.SIGN_IN) },
|
||||
onGuestClick = { navController.navigate(AppScreen.CREATE_PROFILE) }
|
||||
onGuestClick = { navController.navigate(Graph.main(AppScreen.createProfile("guest"))) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +63,8 @@ fun AuthNavGraph(
|
|||
OtpScreen(
|
||||
phoneNumber = backStackEntry.arguments?.getString("phoneNumber") ?: "",
|
||||
name = backStackEntry.arguments?.getString("name") ?: "",
|
||||
onSuccess = { navController.navigate(AppScreen.chooseService("1"))}
|
||||
onCreateProfile = {name -> navController.navigate(Graph.main(AppScreen.createProfile(name)))},
|
||||
onSuccess = { navController.navigate(Graph.auth(AppScreen.chooseService("1")))}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
package com.example.livingai_lg.ui.navigation
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.navigation.NavHostController
|
||||
import android.util.Log
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.compose.navigation
|
||||
import androidx.navigation.navArgument
|
||||
import com.example.livingai_lg.ui.login.SuccessScreen
|
||||
import com.example.farmmarketplace.ui.screens.CallsScreen
|
||||
import com.example.farmmarketplace.ui.screens.ContactsScreen
|
||||
import com.example.livingai_lg.ui.models.profileTypes
|
||||
import com.example.livingai_lg.ui.screens.AnimalProfileScreen
|
||||
import com.example.livingai_lg.ui.screens.BuyScreen
|
||||
import com.example.livingai_lg.ui.screens.ChooseServiceScreen
|
||||
|
|
@ -16,32 +19,39 @@ import com.example.livingai_lg.ui.screens.FilterScreen
|
|||
import com.example.livingai_lg.ui.screens.NewListingScreen
|
||||
import com.example.livingai_lg.ui.screens.PostSaleSurveyScreen
|
||||
import com.example.livingai_lg.ui.screens.SaleArchiveScreen
|
||||
import com.example.livingai_lg.ui.screens.SavedListingsScreen
|
||||
import com.example.livingai_lg.ui.screens.SellerProfileScreen
|
||||
import com.example.livingai_lg.ui.screens.SortScreen
|
||||
import com.example.livingai_lg.ui.screens.chat.ChatScreen
|
||||
import com.example.livingai_lg.ui.screens.chat.ChatsScreen
|
||||
|
||||
@Composable
|
||||
fun MainNavGraph(
|
||||
navController: NavHostController = rememberNavController()
|
||||
) {
|
||||
fun NavGraphBuilder.mainNavGraph(navController: NavController) {
|
||||
val onNavClick: (String) -> Unit = { route ->
|
||||
val currentRoute =
|
||||
navController.currentBackStackEntry?.destination?.route
|
||||
Log.d("Current Route:"," $currentRoute $route")
|
||||
|
||||
if (currentRoute != route) {
|
||||
navController.navigate(route) {
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
popUpTo(navController.graph.startDestinationId) {
|
||||
saveState = true
|
||||
}
|
||||
//restoreState = true
|
||||
// popUpTo(navController.graph.startDestinationId) {
|
||||
// saveState = true
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = AppScreen.BUY_ANIMALS
|
||||
) {
|
||||
navigation(
|
||||
route = Graph.MAIN,
|
||||
startDestination = AppScreen.createProfile("guest")
|
||||
){
|
||||
|
||||
|
||||
// NavHost(
|
||||
// navController = navController,
|
||||
// startDestination = AppScreen.createProfile("guest")
|
||||
// ) {
|
||||
composable(
|
||||
"${AppScreen.CREATE_PROFILE}/{name}",
|
||||
arguments = listOf(navArgument("name") { type = NavType.StringType })
|
||||
|
|
@ -55,12 +65,6 @@ fun MainNavGraph(
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
composable(AppScreen.CHOOSE_SERVICE) {
|
||||
ChooseServiceScreen (
|
||||
onServiceSelected = { navController.navigate(AppScreen.LANDING) },
|
||||
)
|
||||
}
|
||||
composable(
|
||||
"${AppScreen.CHOOSE_SERVICE}/{profileId}",
|
||||
arguments = listOf(navArgument("profileId") { type = NavType.StringType })
|
||||
|
|
@ -82,8 +86,6 @@ fun MainNavGraph(
|
|||
)
|
||||
},
|
||||
onNavClick = onNavClick,
|
||||
onFilterClick = {navController.navigate(AppScreen.BUY_ANIMALS_FILTERS)},
|
||||
onSortClick = {navController.navigate(AppScreen.BUY_ANIMALS_SORT)},
|
||||
onSellerClick = { sellerId ->
|
||||
navController.navigate(
|
||||
AppScreen.sellerProfile(sellerId)
|
||||
|
|
@ -92,20 +94,17 @@ fun MainNavGraph(
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
composable(AppScreen.BUY_ANIMALS_FILTERS) {
|
||||
FilterScreen(
|
||||
onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
||||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onSkipClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
||||
onCancelClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
composable(AppScreen.BUY_ANIMALS_SORT) {
|
||||
|
|
@ -114,9 +113,6 @@ fun MainNavGraph(
|
|||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onSkipClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onCancelClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
|
|
@ -124,13 +120,25 @@ fun MainNavGraph(
|
|||
)
|
||||
}
|
||||
|
||||
composable(AppScreen.SAVED_LISTINGS) {
|
||||
SavedListingsScreen(
|
||||
onNavClick = onNavClick,
|
||||
onBackClick = { navController.popBackStack() })
|
||||
}
|
||||
|
||||
|
||||
composable(AppScreen.CREATE_ANIMAL_LISTING) {
|
||||
NewListingScreen (
|
||||
onSaveClick = {navController.navigate(
|
||||
AppScreen.postSaleSurvey("2")
|
||||
)},
|
||||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
navController.navigate(AppScreen.BUY_ANIMALS){
|
||||
popUpTo(AppScreen.BUY_ANIMALS){
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -175,7 +183,7 @@ fun MainNavGraph(
|
|||
PostSaleSurveyScreen (
|
||||
animalId = animalId,
|
||||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
navController.navigate(AppScreen.CREATE_ANIMAL_LISTING)
|
||||
},
|
||||
onSubmit = {navController.navigate(
|
||||
AppScreen.saleArchive("2")
|
||||
|
|
@ -200,6 +208,7 @@ fun MainNavGraph(
|
|||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onNavClick = onNavClick,
|
||||
onSellerClick = { sellerId ->
|
||||
navController.navigate(
|
||||
AppScreen.sellerProfile(sellerId)
|
||||
|
|
@ -226,5 +235,51 @@ fun MainNavGraph(
|
|||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
}}
|
||||
}
|
||||
|
||||
composable(AppScreen.CONTACTS) {
|
||||
ContactsScreen(
|
||||
onBackClick = {navController.navigate(AppScreen.BUY_ANIMALS)},//{navController.popBackStack()},
|
||||
onTabClick = onNavClick,
|
||||
)
|
||||
}
|
||||
|
||||
composable(AppScreen.CALLS) {
|
||||
CallsScreen(
|
||||
onBackClick = {navController.navigate(AppScreen.BUY_ANIMALS)},//{navController.popBackStack()},
|
||||
onTabClick = onNavClick,
|
||||
)
|
||||
}
|
||||
|
||||
composable(AppScreen.CHATS) {
|
||||
ChatsScreen(
|
||||
onBackClick = {navController.navigate(AppScreen.BUY_ANIMALS)},//{navController.popBackStack()},
|
||||
onTabClick = onNavClick,
|
||||
onChatItemClick = {navController.navigate(AppScreen.chats("2"))}
|
||||
)
|
||||
}
|
||||
|
||||
composable(
|
||||
route = "${AppScreen.CHAT}/{contact}",
|
||||
arguments = listOf(
|
||||
navArgument("contact") { type = NavType.StringType }
|
||||
)
|
||||
) { backStackEntry ->
|
||||
|
||||
val sellerId = backStackEntry
|
||||
.arguments
|
||||
?.getString("contact")
|
||||
?: return@composable
|
||||
|
||||
ChatScreen(
|
||||
sellerId,
|
||||
onBackClick = {
|
||||
navController.navigate(AppScreen.CHATS)
|
||||
//navController.popBackStack()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.example.livingai_lg.ui.screens
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import com.example.livingai_lg.ui.components.ImageCarousel
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -9,11 +10,21 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.StarHalf
|
||||
import androidx.compose.material.icons.filled.Bookmark
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material.icons.filled.StarHalf
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
|
|
@ -26,9 +37,12 @@ import androidx.compose.ui.unit.sp
|
|||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Shadow
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.zIndex
|
||||
import com.example.livingai_lg.ui.models.Animal
|
||||
import com.example.livingai_lg.ui.utils.formatPrice
|
||||
|
|
@ -38,6 +52,11 @@ import com.example.livingai_lg.ui.components.FloatingActionBar
|
|||
import com.example.livingai_lg.ui.models.sampleAnimals
|
||||
import com.example.livingai_lg.ui.utils.formatAge
|
||||
import com.example.livingai_lg.R
|
||||
import com.example.livingai_lg.ui.components.ActionPopup
|
||||
import com.example.livingai_lg.ui.components.RatingStars
|
||||
import com.example.livingai_lg.ui.navigation.AppScreen
|
||||
import com.example.livingai_lg.ui.theme.AppTypography
|
||||
import com.example.livingai_lg.ui.utils.formatDistance
|
||||
|
||||
|
||||
@Composable
|
||||
|
|
@ -45,12 +64,15 @@ fun AnimalProfileScreen(
|
|||
animalId: String,
|
||||
onBackClick: () -> Unit = {},
|
||||
onSellerClick: (sellerId: String) -> Unit = {},
|
||||
onNavClick: (route: String) -> Unit = {}
|
||||
) {
|
||||
var showSavedPopup by remember { mutableStateOf(false) }
|
||||
val animal = sampleAnimals.find { animal -> animal.id == animalId } ?: Animal(id = "null")
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF7F4EE))
|
||||
.padding(12.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -62,7 +84,7 @@ fun AnimalProfileScreen(
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(500.dp)
|
||||
.height(350.dp)
|
||||
.shadow(4.dp)
|
||||
) {
|
||||
// Main image
|
||||
|
|
@ -92,19 +114,31 @@ fun AnimalProfileScreen(
|
|||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart)
|
||||
.padding(start = 16.dp, top = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
.padding(start = 5.dp, top = 5.dp)
|
||||
.shadow(
|
||||
elevation = 6.dp,
|
||||
shape = RoundedCornerShape(50),
|
||||
ambientColor = Color.Black.copy(alpha = 0.4f),
|
||||
spotColor = Color.Black.copy(alpha = 0.4f)
|
||||
)
|
||||
.background(
|
||||
color = Color.Black.copy(alpha = 0.35f), // 👈 light but effective
|
||||
shape = RoundedCornerShape(50)
|
||||
)
|
||||
.padding(horizontal = 6.dp, vertical = 3.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_view),
|
||||
contentDescription = "Views",
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(24.dp)
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = formatViews(animal.views),
|
||||
fontSize = 12.sp,
|
||||
fontSize = AppTypography.BodySmall,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color.White
|
||||
)
|
||||
|
|
@ -120,25 +154,41 @@ fun AnimalProfileScreen(
|
|||
) {
|
||||
Text(
|
||||
text = "${animal.name}, ${formatAge(animal.age)}",
|
||||
fontSize = 30.sp,
|
||||
fontSize = AppTypography.Title,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color.White
|
||||
color = Color.White,
|
||||
style = LocalTextStyle.current.copy(
|
||||
shadow = Shadow(
|
||||
color = Color.Black.copy(alpha = 0.75f),
|
||||
offset = Offset(0f, 2f),
|
||||
blurRadius = 6f
|
||||
)
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = "${animal.breed} • ${animal.location}, ${animal.distance}".uppercase(),
|
||||
fontSize = 12.sp,
|
||||
text = "${animal.breed} • ${animal.location}, ${formatDistance(animal.distance)}".uppercase(),
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color.White.copy(alpha = 0.7f),
|
||||
letterSpacing = 1.2.sp
|
||||
letterSpacing = 1.2.sp,
|
||||
style = LocalTextStyle.current.copy(
|
||||
shadow = Shadow(
|
||||
color = Color.Black.copy(alpha = 0.6f),
|
||||
offset = Offset(0f, 1.5f),
|
||||
blurRadius = 4f
|
||||
)
|
||||
)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
}
|
||||
|
||||
// AI Score badge (bottom left)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.padding(start = 0.dp, bottom = 5.dp)
|
||||
.height(48.dp)
|
||||
.padding(start = 5.dp, bottom = 5.dp)
|
||||
.height(60.dp)
|
||||
.border(2.dp, Color(0xFF717182), CircleShape)
|
||||
.padding(horizontal = 12.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
|
|
@ -147,7 +197,7 @@ fun AnimalProfileScreen(
|
|||
AIScoreCircle(score = animal.aiScore ?: 0f)
|
||||
Text(
|
||||
text = "AI Score",
|
||||
fontSize = 18.sp,
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color.White
|
||||
)
|
||||
|
|
@ -159,11 +209,12 @@ fun AnimalProfileScreen(
|
|||
append("At Display: ")
|
||||
append(animal.displayLocation)
|
||||
},
|
||||
fontSize = 8.sp,
|
||||
fontSize = AppTypography.Caption,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color.White,
|
||||
color = Color.White.copy(alpha = 0.7f),
|
||||
lineHeight = 13.sp,
|
||||
textDecoration = TextDecoration.Underline,
|
||||
textAlign = TextAlign.Right,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(end = 16.dp, bottom = 8.dp)
|
||||
|
|
@ -190,8 +241,8 @@ fun AnimalProfileScreen(
|
|||
) {
|
||||
Text(
|
||||
text = formatPrice(animal.price),
|
||||
fontSize = 30.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = AppTypography.Title,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = Color(0xFF0A0A0A)
|
||||
)
|
||||
|
||||
|
|
@ -204,12 +255,12 @@ fun AnimalProfileScreen(
|
|||
Icon(
|
||||
painter = painterResource(R.drawable.ic_thumbs_up),
|
||||
contentDescription = "Fair Price",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
tint = Color(0xFF00C950),
|
||||
modifier = Modifier.size(15.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Fair Price",
|
||||
fontSize = 12.sp,
|
||||
fontSize = AppTypography.Caption,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF00C950)
|
||||
)
|
||||
|
|
@ -219,7 +270,7 @@ fun AnimalProfileScreen(
|
|||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.End,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
|
|
@ -229,7 +280,7 @@ fun AnimalProfileScreen(
|
|||
{
|
||||
Text(
|
||||
text = "Sold By: ${animal.sellerName}",
|
||||
fontSize = 16.sp,
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF0A0A0A)
|
||||
)
|
||||
|
|
@ -239,19 +290,13 @@ fun AnimalProfileScreen(
|
|||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
repeat(5) { index ->
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_star),
|
||||
contentDescription = null,
|
||||
tint = if (index < (animal.rating ?: 0).toInt()) Color(
|
||||
0xFFDE9A07
|
||||
) else Color(0xFFDE9A07),
|
||||
modifier = Modifier.size(12.dp)
|
||||
)
|
||||
}
|
||||
RatingStars(
|
||||
rating = animal.rating ?: 0f,
|
||||
starSize = 12.dp
|
||||
)
|
||||
Text(
|
||||
text = "${animal.rating} (${animal.ratingCount} Ratings)",
|
||||
fontSize = 10.sp,
|
||||
fontSize = AppTypography.Caption,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF0A0A0A)
|
||||
)
|
||||
|
|
@ -267,13 +312,13 @@ fun AnimalProfileScreen(
|
|||
) {
|
||||
Text(
|
||||
text = "About",
|
||||
fontSize = 16.sp,
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF09090B).copy(alpha = 0.5f)
|
||||
)
|
||||
Text(
|
||||
text = animal.description ?: "",
|
||||
fontSize = 14.sp,
|
||||
fontSize = AppTypography.BodySmall,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF09090B),
|
||||
lineHeight = 24.sp
|
||||
|
|
@ -297,24 +342,43 @@ fun AnimalProfileScreen(
|
|||
// Ad space banner
|
||||
AdSpaceBanner()
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Spacer(modifier = Modifier.height(64.dp))
|
||||
}
|
||||
}
|
||||
FloatingActionBar(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(bottom = 24.dp)
|
||||
.offset(y = (-20).dp)
|
||||
.padding(bottom = 10.dp)
|
||||
.offset(y = (-10).dp)
|
||||
.zIndex(10f), // 👈 ensure it floats above everything
|
||||
onChatClick = { /* TODO */ },
|
||||
onCallClick = { /* TODO */ },
|
||||
onLocationClick = { /* TODO */ },
|
||||
onBookmarkClick = { /* TODO */ }
|
||||
onBookmarkClick = { showSavedPopup = true }
|
||||
)
|
||||
|
||||
ActionPopup(
|
||||
visible = showSavedPopup,
|
||||
text = "Saved",
|
||||
icon = Icons.Default.Bookmark,
|
||||
backgroundColor = Color.Black,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(end = 16.dp, bottom = 96.dp),
|
||||
onClick = {
|
||||
onNavClick(AppScreen.SAVED_LISTINGS)
|
||||
// Navigate to saved items
|
||||
},
|
||||
onDismiss = {
|
||||
showSavedPopup = false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun Modifier.Companion.align(bottomEnd: Alignment) {}
|
||||
|
||||
@Composable
|
||||
fun AIScoreCircle(score: Float) {
|
||||
Box(
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
package com.example.livingai_lg.ui.screens
|
||||
|
||||
import androidx.compose.foundation.Indication
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Bookmark
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -25,6 +33,12 @@ import com.example.livingai_lg.ui.models.mainBottomNavItems
|
|||
import com.example.livingai_lg.ui.models.sampleAnimals
|
||||
import com.example.livingai_lg.ui.models.userProfile
|
||||
import com.example.livingai_lg.R
|
||||
import com.example.livingai_lg.ui.components.ActionPopup
|
||||
import com.example.livingai_lg.ui.components.AddressSelectorOverlay
|
||||
import com.example.livingai_lg.ui.components.NotificationsOverlay
|
||||
import com.example.livingai_lg.ui.components.SortOverlay
|
||||
import com.example.livingai_lg.ui.models.sampleNotifications
|
||||
import com.example.livingai_lg.ui.navigation.AppScreen
|
||||
|
||||
@Composable
|
||||
fun BuyScreen(
|
||||
|
|
@ -37,10 +51,13 @@ fun BuyScreen(
|
|||
) {
|
||||
val selectedAnimalType = remember { mutableStateOf<String?>(null) }
|
||||
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(
|
||||
modifier = Modifier
|
||||
|
|
@ -74,10 +91,8 @@ fun BuyScreen(
|
|||
|
||||
UserLocationHeader(
|
||||
user = userProfile,
|
||||
onAddressSelected = {
|
||||
// optional: reload listings, persist selection, etc.
|
||||
},
|
||||
onAddNewClick = {}
|
||||
onOpenAddressOverlay = { showAddressSelector = true },
|
||||
selectedAddressId = selectedAddressId?:"1",
|
||||
)
|
||||
|
||||
// Right-side actions (notifications, etc.)
|
||||
|
|
@ -86,6 +101,10 @@ fun BuyScreen(
|
|||
contentDescription = "Notifications",
|
||||
tint = Color.Black,
|
||||
modifier = Modifier.size(24.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
){ showNotifications = true }
|
||||
)
|
||||
}
|
||||
// Row(
|
||||
|
|
@ -129,8 +148,10 @@ fun BuyScreen(
|
|||
.padding(horizontal = 22.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
SortButton(onSortClick)
|
||||
FilterButton(onFilterClick)
|
||||
SortButton(
|
||||
onClick = { showSortOverlay.value = true }
|
||||
)
|
||||
FilterButton(onClick = { showFilterOverlay.value = true })
|
||||
}
|
||||
|
||||
sampleAnimals.forEach { animal ->
|
||||
|
|
@ -142,7 +163,8 @@ fun BuyScreen(
|
|||
isSaved = isSaved.value,
|
||||
onSavedChange = { isSaved.value = it },
|
||||
onProductClick = { onProductClick(animal.id)},
|
||||
onSellerClick = onSellerClick
|
||||
onSellerClick = onSellerClick,
|
||||
onBookmarkClick = { showSavedPopup = true }
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
|
@ -160,6 +182,62 @@ fun BuyScreen(
|
|||
}
|
||||
}
|
||||
|
||||
AddressSelectorOverlay(
|
||||
visible = showAddressSelector,
|
||||
addresses = userProfile.addresses,
|
||||
selectedAddressId = selectedAddressId,
|
||||
onSelect = { addressId ->
|
||||
selectedAddressId = addressId
|
||||
showAddressSelector = false
|
||||
},
|
||||
onClose = { showAddressSelector = false }
|
||||
)
|
||||
|
||||
SortOverlay(
|
||||
visible = showSortOverlay.value,
|
||||
onApplyClick = { selected ->
|
||||
// TODO: apply sort
|
||||
},
|
||||
onDismiss = { showSortOverlay.value = false }
|
||||
)
|
||||
|
||||
FilterOverlay(
|
||||
visible = showFilterOverlay.value,
|
||||
onDismiss = { showFilterOverlay.value = false },
|
||||
onSubmitClick = {
|
||||
// apply filters
|
||||
}
|
||||
)
|
||||
|
||||
ActionPopup(
|
||||
visible = showSavedPopup,
|
||||
text = "Saved",
|
||||
icon = Icons.Default.Bookmark,
|
||||
backgroundColor = Color.Black,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(end = 16.dp, bottom = 96.dp),
|
||||
onClick = {
|
||||
onNavClick(AppScreen.SAVED_LISTINGS)
|
||||
// Navigate to saved items
|
||||
},
|
||||
onDismiss = {
|
||||
showSavedPopup = false
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
NotificationsOverlay(
|
||||
visible = showNotifications,
|
||||
notifications = sampleNotifications,
|
||||
onClose = { showNotifications = false },
|
||||
onDismiss = { id ->
|
||||
// remove notification from list
|
||||
},
|
||||
onNotificationClick = { id ->
|
||||
// optional navigation
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,10 +27,13 @@ import androidx.compose.ui.unit.sp
|
|||
import com.example.livingai_lg.ui.components.OptionCard
|
||||
import com.example.livingai_lg.ui.components.backgrounds.StoreBackground
|
||||
import com.example.livingai_lg.R
|
||||
import com.example.livingai_lg.ui.theme.AppTypography
|
||||
|
||||
data class ServiceType(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val icon: Int,
|
||||
val icon: Any,
|
||||
val iconTint: Color,
|
||||
val backgroundColor: Color
|
||||
)
|
||||
|
||||
|
|
@ -46,30 +49,35 @@ fun ChooseServiceScreen(
|
|||
id = "transport",
|
||||
title = "Transport",
|
||||
icon = R.drawable.ic_shop,
|
||||
iconTint = Color.White,
|
||||
backgroundColor = Color(0xFF9D4EDD)
|
||||
),
|
||||
ServiceType(
|
||||
id = "vet",
|
||||
title = "Vet",
|
||||
icon = R.drawable.ic_bag,
|
||||
iconTint = Color.White,
|
||||
backgroundColor = Color(0xFF3A86FF)
|
||||
),
|
||||
ServiceType(
|
||||
id = "feed_supplier",
|
||||
title = "Feed Supplier",
|
||||
icon = R.drawable.ic_spanner,
|
||||
iconTint = Color.White,
|
||||
backgroundColor = Color(0xFFFF5722)
|
||||
),
|
||||
ServiceType(
|
||||
id = "medicine_supplier",
|
||||
title = "Medicine Supplier",
|
||||
icon = R.drawable.ic_shop2,
|
||||
iconTint = Color.White,
|
||||
backgroundColor = Color(0xFF4CAF50)
|
||||
),
|
||||
ServiceType(
|
||||
id = "other",
|
||||
title = "Other",
|
||||
icon = R.drawable.ic_other,
|
||||
iconTint = Color.White,
|
||||
backgroundColor = Color(0xFFD4A942)
|
||||
)
|
||||
)
|
||||
|
|
@ -101,7 +109,7 @@ fun ChooseServiceScreen(
|
|||
) {
|
||||
Text(
|
||||
text = "Choose Service",
|
||||
fontSize = 36.sp,
|
||||
fontSize = AppTypography.Display,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A)
|
||||
)
|
||||
|
|
@ -109,8 +117,8 @@ fun ChooseServiceScreen(
|
|||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Text(
|
||||
text = "Choose Service**",
|
||||
fontSize = 16.sp,
|
||||
text = "Choose a Service",
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A).copy(alpha = 0.8f)
|
||||
)
|
||||
|
|
@ -129,6 +137,7 @@ fun ChooseServiceScreen(
|
|||
OptionCard(
|
||||
label = service.title,
|
||||
icon = service.icon,
|
||||
iconTint = service.iconTint,
|
||||
iconBackgroundColor = service.backgroundColor,
|
||||
onClick = {
|
||||
selectedService.value = service.id
|
||||
|
|
@ -144,84 +153,3 @@ fun ChooseServiceScreen(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ServiceTypeCard(
|
||||
service: ServiceType,
|
||||
isSelected: Boolean,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(90.dp)
|
||||
.shadow(2.dp, RoundedCornerShape(16.dp))
|
||||
.background(Color.White, RoundedCornerShape(16.dp))
|
||||
.border(1.dp, Color(0xFFF3F4F6), RoundedCornerShape(16.dp))
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onClick
|
||||
)
|
||||
.padding(12.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Icon container
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(56.dp)
|
||||
.background(service.backgroundColor, RoundedCornerShape(14.dp)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
when (service.id) {
|
||||
"transport" ->
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_shop),
|
||||
contentDescription = "Transport",
|
||||
)
|
||||
|
||||
"vet" -> Icon(
|
||||
painter = painterResource(id = R.drawable.ic_bag),
|
||||
contentDescription = "Vet",
|
||||
)
|
||||
"feed_supplier" -> Icon(
|
||||
painter = painterResource(id = R.drawable.ic_spanner),
|
||||
contentDescription = "Feed Supplier",
|
||||
)
|
||||
"medicine_supplier" -> Icon(
|
||||
painter = painterResource(id = R.drawable.ic_shop2),
|
||||
contentDescription = "Medicine Supplier",
|
||||
)
|
||||
"other" -> Icon(
|
||||
painter = painterResource(id = R.drawable.ic_other),
|
||||
contentDescription = "Other",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Title
|
||||
Text(
|
||||
text = service.title,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF0A0A0A),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
// Radio button
|
||||
RadioButton(
|
||||
selected = isSelected,
|
||||
onClick = onClick,
|
||||
colors = RadioButtonDefaults.colors(
|
||||
selectedColor = Color(0xFF6B7280),
|
||||
unselectedColor = Color(0xFF6B7280)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Notifications
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
|
@ -24,6 +26,7 @@ import com.example.livingai_lg.ui.components.OptionCard
|
|||
|
||||
import com.example.livingai_lg.ui.components.backgrounds.StoreBackground
|
||||
import com.example.livingai_lg.ui.models.profileTypes
|
||||
import com.example.livingai_lg.ui.theme.AppTypography
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
|
|
@ -75,7 +78,7 @@ fun CreateProfileScreen(
|
|||
) {
|
||||
Text(
|
||||
text = "Create Profile",
|
||||
fontSize = 36.sp,
|
||||
fontSize = AppTypography.Display,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A)
|
||||
)
|
||||
|
|
@ -84,7 +87,7 @@ fun CreateProfileScreen(
|
|||
|
||||
Text(
|
||||
text = "Choose Profile Type",
|
||||
fontSize = 16.sp,
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A).copy(alpha = 0.8f)
|
||||
)
|
||||
|
|
@ -105,7 +108,8 @@ fun CreateProfileScreen(
|
|||
icon = profile.icon,
|
||||
iconBackgroundColor = profile.backgroundColor,
|
||||
onClick = {
|
||||
updateProfile(profile.id)
|
||||
onProfileSelected(profile.id)
|
||||
//updateProfile(profile.id)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
package com.example.livingai_lg.ui.screens
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -24,14 +28,14 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.components.DropdownInput
|
||||
import com.example.livingai_lg.ui.components.RangeFilter
|
||||
import com.example.livingai_lg.ui.theme.AppTypography
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun FilterScreen(
|
||||
onBackClick: () -> Unit = {},
|
||||
onSkipClick: () -> Unit = {},
|
||||
onSubmitClick: () -> Unit = {},
|
||||
onCancelClick: () -> Unit = {}
|
||||
onCancelClick: () -> Unit = {},
|
||||
) {
|
||||
var selectedAnimal by remember { mutableStateOf("") }
|
||||
var selectedBreed by remember { mutableStateOf("") }
|
||||
|
|
@ -104,16 +108,6 @@ fun FilterScreen(
|
|||
color = Color.Black
|
||||
)
|
||||
}
|
||||
|
||||
TextButton(onClick = onSkipClick) {
|
||||
Text(
|
||||
text = "Skip",
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF4A5565),
|
||||
textDecoration = TextDecoration.Underline
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +116,7 @@ fun FilterScreen(
|
|||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(horizontal = 32.dp)
|
||||
.padding(bottom = 24.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||
) {
|
||||
|
|
@ -130,117 +124,43 @@ fun FilterScreen(
|
|||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Animal",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color.Black
|
||||
)
|
||||
|
||||
ExposedDropdownMenuBox(
|
||||
DropdownInput(
|
||||
label = "Animal",
|
||||
selected = selectedAnimal,
|
||||
options = listOf("Cow", "Buffalo", "Goat", "Sheep"),
|
||||
expanded = animalExpanded,
|
||||
onExpandedChange = { animalExpanded = it }
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = selectedAnimal,
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
placeholder = {
|
||||
Text(
|
||||
"Select Animal",
|
||||
color = Color(0xFF717182),
|
||||
fontSize = 16.sp
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.menuAnchor(),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
unfocusedContainerColor = Color.White,
|
||||
focusedContainerColor = Color.White,
|
||||
unfocusedBorderColor = Color(0x1A000000),
|
||||
focusedBorderColor = Color(0x1A000000)
|
||||
)
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
expanded = animalExpanded,
|
||||
onDismissRequest = { animalExpanded = false }
|
||||
) {
|
||||
listOf("Cow", "Buffalo", "Goat", "Sheep").forEach { animal ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(animal) },
|
||||
onClick = {
|
||||
selectedAnimal = animal
|
||||
animalExpanded = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
onExpandedChange = { animalExpanded = it },
|
||||
onSelect = { item ->
|
||||
selectedAnimal = item
|
||||
animalExpanded = false
|
||||
},
|
||||
placeholder = "Select Animal", // <--- half width
|
||||
)
|
||||
}
|
||||
|
||||
// Breed Section
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Breed",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color.Black
|
||||
)
|
||||
|
||||
ExposedDropdownMenuBox(
|
||||
DropdownInput(
|
||||
label = "Breed",
|
||||
selected = selectedBreed,
|
||||
options = listOf("Holstein", "Jersey", "Gir", "Sahiwal"),
|
||||
expanded = breedExpanded,
|
||||
onExpandedChange = { breedExpanded = it }
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = selectedBreed,
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
placeholder = {
|
||||
Text(
|
||||
"Select Breed",
|
||||
color = Color(0xFF717182),
|
||||
fontSize = 16.sp
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.menuAnchor(),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
unfocusedContainerColor = Color.White,
|
||||
focusedContainerColor = Color.White,
|
||||
unfocusedBorderColor = Color(0x1A000000),
|
||||
focusedBorderColor = Color(0x1A000000)
|
||||
)
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
expanded = breedExpanded,
|
||||
onDismissRequest = { breedExpanded = false }
|
||||
) {
|
||||
listOf("Holstein", "Jersey", "Gir", "Sahiwal").forEach { breed ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(breed) },
|
||||
onClick = {
|
||||
selectedBreed = breed
|
||||
breedExpanded = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
onExpandedChange = { breedExpanded = it },
|
||||
onSelect = { item ->
|
||||
selectedBreed = item
|
||||
breedExpanded = false
|
||||
},
|
||||
placeholder = "Select Breed", // <--- half width
|
||||
)
|
||||
}
|
||||
|
||||
// Price and Age Row
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp) // 👈 reduce spacing
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
|
||||
) {
|
||||
RangeFilter(
|
||||
modifier = Modifier.fillMaxWidth(), // 👈 important
|
||||
|
|
@ -257,7 +177,8 @@ fun FilterScreen(
|
|||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
|
||||
) {
|
||||
RangeFilter(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
|
@ -273,17 +194,13 @@ fun FilterScreen(
|
|||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Distance and Gender Row
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(64.dp)
|
||||
) {
|
||||
|
||||
// Distance Section
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(3.dp)
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
DropdownInput(
|
||||
label = "Distance",
|
||||
|
|
@ -301,8 +218,7 @@ fun FilterScreen(
|
|||
|
||||
// Gender Section
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(3.dp)
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
DropdownInput(
|
||||
label = "Gender",
|
||||
|
|
@ -319,7 +235,7 @@ fun FilterScreen(
|
|||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Pregnancy Status Section
|
||||
Column(
|
||||
|
|
@ -327,7 +243,7 @@ fun FilterScreen(
|
|||
) {
|
||||
Text(
|
||||
text = "Pregnancy Status",
|
||||
fontSize = 16.sp,
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color(0xFF364153)
|
||||
)
|
||||
|
|
@ -352,12 +268,9 @@ fun FilterScreen(
|
|||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp) // 👈 reduce spacing
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
RangeFilter(
|
||||
modifier = Modifier.fillMaxWidth(), // 👈 important
|
||||
|
|
@ -374,7 +287,7 @@ fun FilterScreen(
|
|||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
RangeFilter(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
|
@ -390,7 +303,7 @@ fun FilterScreen(
|
|||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Calving Number Section
|
||||
Column(
|
||||
|
|
@ -469,7 +382,7 @@ fun FilterScreen(
|
|||
) {
|
||||
Text(
|
||||
text = "Submit",
|
||||
fontSize = 16.sp,
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
}
|
||||
|
|
@ -492,7 +405,7 @@ fun FilterScreen(
|
|||
) {
|
||||
Text(
|
||||
text = "Cancel",
|
||||
fontSize = 16.sp,
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.components.MediaPickerCard
|
||||
import com.example.livingai_lg.ui.models.NewListingFormState
|
||||
import com.example.livingai_lg.ui.theme.AppTypography
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
|
@ -56,7 +57,7 @@ fun NewListingScreen(
|
|||
title = {
|
||||
Text(
|
||||
text = "New Listing",
|
||||
fontSize = 24.sp,
|
||||
fontSize = AppTypography.Display,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -94,18 +94,6 @@ fun PostSaleSurveyScreen(
|
|||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onBackClick() }
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Skip",
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF4A5565),
|
||||
textDecoration = TextDecoration.Underline,
|
||||
modifier = Modifier.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onSkipClick() }
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(26.dp))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,29 @@
|
|||
package com.example.livingai_lg.ui.screens
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
|
|
@ -39,12 +49,10 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.components.SortItem
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SortScreen(
|
||||
onBackClick: () -> Unit = {},
|
||||
onSkipClick: () -> Unit = {},
|
||||
onApplyClick: (List<SortField>) -> Unit = {},
|
||||
onCancelClick: () -> Unit = {}
|
||||
) {
|
||||
|
|
@ -103,18 +111,14 @@ fun SortScreen(
|
|||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(Icons.Default.ArrowBack, null)
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
tint = Color(0xFF0A0A0A)
|
||||
)
|
||||
}
|
||||
Text("Sort By", fontSize = 32.sp)
|
||||
}
|
||||
|
||||
TextButton(onClick = onSkipClick) {
|
||||
Text(
|
||||
"Skip",
|
||||
textDecoration = TextDecoration.Underline,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ import kotlinx.coroutines.launch
|
|||
@Composable
|
||||
fun SignInScreen(
|
||||
onSignUpClick: () -> Unit = {},
|
||||
onSignInClick: (phone: String, name: String) -> Unit = {},
|
||||
onSignInClick: (phone: String, name: String) -> Unit = {_,_->},
|
||||
) {
|
||||
val phoneNumber = remember { mutableStateOf("") }
|
||||
val isValid = phoneNumber.value.length == 10
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ private fun String.isValidPhoneNumber(): Boolean {
|
|||
@Composable
|
||||
fun SignUpScreen(
|
||||
onSignInClick: () -> Unit = {},
|
||||
onSignUpClick: (phone: String, name: String) -> Unit = {}
|
||||
onSignUpClick: (phone: String, name: String) -> Unit = {_,_->}
|
||||
) {
|
||||
var formData by remember { mutableStateOf(SignUpFormData()) }
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
Loading…
Reference in New Issue