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