Compare commits

..

No commits in common. "c5750e6b5ee7d12868cd6df502f41a2df78d6e5e" and "80cc72bb9d1cb3ea922efe4d9bca3726e2dfe475" have entirely different histories.

18 changed files with 2118 additions and 2423 deletions

1
.idea/.name Normal file
View File

@ -0,0 +1 @@
LivingAi_Lg

View File

@ -27,7 +27,6 @@ class UserNotFoundException(
class AuthApiClient(private val context: Context) { class AuthApiClient(private val context: Context) {
private val route = "http://10.0.2.2:3000/"
private val tokenManager = TokenManager(context) private val tokenManager = TokenManager(context)
val client = HttpClient(CIO) { val client = HttpClient(CIO) {
@ -65,7 +64,7 @@ class AuthApiClient(private val context: Context) {
android.util.Log.d("AuthApiClient", "refreshTokens: Calling /auth/refresh endpoint") android.util.Log.d("AuthApiClient", "refreshTokens: Calling /auth/refresh endpoint")
try { try {
val response: RefreshResponse = client.post("${route}auth/refresh") { val response: RefreshResponse = client.post("http://10.0.2.2:3000/auth/refresh") {
markAsRefreshTokenRequest() markAsRefreshTokenRequest()
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
setBody(RefreshRequest(refreshToken)) setBody(RefreshRequest(refreshToken))
@ -85,7 +84,7 @@ class AuthApiClient(private val context: Context) {
} }
defaultRequest { defaultRequest {
url(route) url("http://10.0.2.2:3000/")
} }
} }

View File

@ -1,87 +1,87 @@
package com.example.livingai_lg.ui.components package com.example.livingai_lg.ui.components
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background import androidx.compose.foundation.background
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.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text import androidx.compose.material3.Text
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.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
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.models.AnimalType import com.example.livingai_lg.ui.models.AnimalType
@Composable @Composable
fun AnimalTypeSelector( fun AnimalTypeSelector(
animalTypes: List<AnimalType>, animalTypes: List<AnimalType>,
selectedAnimalType: String?, selectedAnimalType: MutableState<String?>,
onAnimalTypeSelected: (String) -> Unit onAnimalTypeSelected: (String) -> Unit
) { ) {
val selectedAnimalType: String = selectedAnimalType ?: "" val selectedAnimalType: String = selectedAnimalType.value ?: ""
LazyRow( LazyRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp), .padding(horizontal = 16.dp, vertical = 12.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp) horizontalArrangement = Arrangement.spacedBy(12.dp)
) { ) {
items(animalTypes.size) { index -> items(animalTypes.size) { index ->
AnimalTypeButton( AnimalTypeButton(
animalType = animalTypes[index], animalType = animalTypes[index],
isSelected = selectedAnimalType == animalTypes[index].id, isSelected = selectedAnimalType == animalTypes[index].id,
onClick = { onAnimalTypeSelected(animalTypes[index].id) } onClick = { onAnimalTypeSelected(animalTypes[index].id) }
) )
} }
} }
} }
@Composable @Composable
private fun AnimalTypeButton( private fun AnimalTypeButton(
animalType: AnimalType, animalType: AnimalType,
isSelected: Boolean, isSelected: Boolean,
onClick: () -> Unit onClick: () -> Unit
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.clickable( .clickable(
indication = LocalIndication.current, indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
onClick = onClick onClick = onClick
) )
.padding(4.dp), .padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.size(48.dp) .size(48.dp)
.background( .background(
if (isSelected) Color(0xFFEDE9FE) else Color.White, if (isSelected) Color(0xFFEDE9FE) else Color.White,
RoundedCornerShape(24.dp) RoundedCornerShape(24.dp)
), ),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Text( Text(
text = animalType.emoji, text = animalType.emoji,
fontSize = 24.sp fontSize = 24.sp
) )
} }
Text( Text(
text = animalType.name, text = animalType.name,
fontSize = 12.sp, fontSize = 12.sp,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A), color = Color(0xFF0A0A0A),
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
} }
} }

View File

@ -1,103 +1,93 @@
package com.example.livingai_lg.ui.components package com.example.livingai_lg.ui.components
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border 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.material3.Icon import androidx.compose.material3.Icon
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.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.models.BottomNavItemData
import com.example.livingai_lg.ui.models.BottomNavItemData
@Composable
@Composable fun BottomNavigationBar(
fun BottomNavigationBar( modifier: Modifier = Modifier,
modifier: Modifier = Modifier, items: List<BottomNavItemData>,
items: List<BottomNavItemData>, currentItem: String,
currentItem: String, onItemClick: (route: String) -> Unit = {}
onItemClick: (route: String) -> Unit = {} ) {
) { Column(
Column( modifier = modifier
modifier = modifier .fillMaxWidth()
.fillMaxWidth() .background(Color.White)
.background(Color.White) .border(1.dp, Color(0xFF000000).copy(alpha = 0.1f))
.border(1.dp, Color(0xFF000000).copy(alpha = 0.1f)) ) {
) { Row(
Row( modifier = Modifier
modifier = Modifier .fillMaxWidth()
.fillMaxWidth() .height(66.dp)
.height(66.dp) .padding(horizontal = 8.dp),
.padding(horizontal = 8.dp), horizontalArrangement = Arrangement.SpaceEvenly,
horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically
verticalAlignment = Alignment.CenterVertically ) {
) {
items.forEach { item ->
items.forEach { item -> val isSelected = item.label == currentItem
val isSelected = item.label == currentItem BottomNavItem(
BottomNavItem( label = item.label,
label = item.label, iconRes = item.iconRes,
iconRes = item.iconRes, selected = isSelected,
selected = isSelected, onClick = {
onClick = { if (!isSelected) {
if (!isSelected) { onItemClick(item.route)
onItemClick(item.route) }
} }
} )
) }
} }
} }
} }
}
@Composable
@Composable fun BottomNavItem(
fun BottomNavItem( label: String,
label: String, iconRes: Int,
iconRes: Any, selected: Boolean,
selected: Boolean, onClick: () -> Unit = {}
onClick: () -> Unit = {} ) {
) { val color = if (selected) Color(0xFF1E88E5) else Color(0xFF0A0A0A)
val color = if (selected) Color(0xFF1E88E5) else Color(0xFF0A0A0A)
Column(
Column( horizontalAlignment = Alignment.CenterHorizontally,
horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp), modifier = Modifier
modifier = Modifier .padding(vertical = 4.dp)
.padding(vertical = 4.dp) .clickable(
.clickable( indication = LocalIndication.current,
indication = LocalIndication.current, interactionSource = remember { MutableInteractionSource() },
interactionSource = remember { MutableInteractionSource() }, onClick = onClick)
onClick = onClick) ) {
) { Icon(
if(iconRes is Int) { painter = painterResource(iconRes),
Icon( contentDescription = label,
painter = painterResource(iconRes), tint = color,
contentDescription = label, modifier = Modifier.size(24.dp)
tint = color, )
modifier = Modifier.size(24.dp) Text(
) text = label,
} else if(iconRes is ImageVector) { fontSize = 12.sp,
Icon( fontWeight = FontWeight.Medium,
imageVector = iconRes, color = color
contentDescription = label, )
tint = color, }
modifier = Modifier.size(24.dp) }
)
}
Text(
text = label,
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
color = color
)
}
}

View File

@ -1,118 +1,117 @@
package com.example.livingai_lg.ui.components package com.example.livingai_lg.ui.components
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border 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.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown 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
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.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.AppTypography
import com.example.livingai_lg.ui.theme.FarmTextDark import com.example.livingai_lg.ui.theme.FarmTextDark
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun DropdownInput( fun DropdownInput(
label: String? = null, // optional label label: String? = null, // optional label
selected: String, selected: String,
options: List<String>, options: List<String>,
expanded: Boolean, expanded: Boolean,
onExpandedChange: (Boolean) -> Unit, onExpandedChange: (Boolean) -> Unit,
onSelect: (String) -> Unit, onSelect: (String) -> Unit,
placeholder: String = "Select", // NEW - custom placeholder placeholder: String = "Select", // NEW - custom placeholder
textColor: Color = Color.Black, modifier: Modifier = Modifier // NEW - allows width control
modifier: Modifier = Modifier // NEW - allows width control ) {
) { Column(
Column( modifier = modifier, // <-- now caller can control width
modifier = modifier, // <-- now caller can control width verticalArrangement = Arrangement.spacedBy(8.dp)
verticalArrangement = Arrangement.spacedBy(8.dp) ) {
) { // Optional label
// Optional label if (label != null) {
// if (label != null) { Text(
Text( text = label,
text = label?:" ", fontSize = AppTypography.Body,
fontSize = AppTypography.Body, fontWeight = FontWeight.Medium,
fontWeight = FontWeight.Medium, color = FarmTextDark
color = FarmTextDark )
) } else {
// } else { // Reserve label space so layout doesnt shift
// // Reserve label space so layout doesnt shift Spacer(modifier = Modifier.height(20.dp)) // ← same height as label text line
// Spacer(modifier = Modifier.height(20.dp)) // ← same height as label text line }
// }
ExposedDropdownMenuBox(
ExposedDropdownMenuBox( expanded = expanded,
expanded = expanded, onExpandedChange = { onExpandedChange(!expanded) }
onExpandedChange = { onExpandedChange(!expanded) } ) {
) { // Anchor box
// Anchor box Box(
Box( modifier = Modifier
modifier = Modifier .menuAnchor()
.menuAnchor() .fillMaxWidth()
.fillMaxWidth() .height(52.dp)
.height(52.dp) .shadow(2.dp, RoundedCornerShape(16.dp))
.shadow(2.dp, RoundedCornerShape(16.dp)) .background(Color.White, RoundedCornerShape(16.dp))
.background(Color.White, RoundedCornerShape(16.dp)) .border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(16.dp))
.border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(16.dp)) .clickable(
.clickable( indication = LocalIndication.current,
indication = LocalIndication.current, interactionSource = remember { MutableInteractionSource() }
interactionSource = remember { MutableInteractionSource() } ) {
) { onExpandedChange(true)
onExpandedChange(true) }
} .padding(horizontal = 16.dp),
.padding(horizontal = 16.dp), contentAlignment = Alignment.CenterStart
contentAlignment = Alignment.CenterStart ) {
) { Row(
Row( modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween,
horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically
verticalAlignment = Alignment.CenterVertically ) {
) {
// Custom placeholder support
// Custom placeholder support Text(
Text( text = selected.ifEmpty { placeholder },
text = selected.ifEmpty { placeholder }, fontSize = AppTypography.Body,
fontSize = AppTypography.Body, color = if (selected.isEmpty()) Color(0xFF99A1AF) else FarmTextDark
color = if (selected.isEmpty()) Color(0xFF99A1AF) else textColor )
)
Icon(
Icon( imageVector = Icons.Default.ArrowDropDown,
imageVector = Icons.Default.ArrowDropDown, contentDescription = "Dropdown",
contentDescription = "Dropdown", tint = FarmTextDark
tint = FarmTextDark )
)
}
} }
}
// Material3 Dropdown
// Material3 Dropdown ExposedDropdownMenu(
ExposedDropdownMenu( expanded = expanded,
expanded = expanded, onDismissRequest = { onExpandedChange(false) },
onDismissRequest = { onExpandedChange(false) }, modifier = Modifier.background(Color.White)
modifier = Modifier.background(Color.White) ) {
) { options.forEach { item ->
options.forEach { item -> DropdownMenuItem(
DropdownMenuItem( text = {
text = { Text(item, fontSize = AppTypography.Body, color = FarmTextDark)
Text(item, fontSize = AppTypography.Body, color = FarmTextDark) },
}, onClick = {
onClick = { onSelect(item)
onSelect(item) onExpandedChange(false)
onExpandedChange(false) }
} )
) }
} }
} }
} }
} }
}

View File

@ -1,77 +1,74 @@
package com.example.livingai_lg.ui.components package com.example.livingai_lg.ui.components
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background import androidx.compose.foundation.background
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.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.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.example.livingai_lg.ui.models.FiltersState import com.example.livingai_lg.ui.screens.FilterScreen
import com.example.livingai_lg.ui.screens.FilterScreen
@Composable
@Composable fun FilterOverlay(
fun FilterOverlay( visible: Boolean,
visible: Boolean, onDismiss: () -> Unit,
appliedFilters: FiltersState, onSubmitClick: () -> Unit = {},
onDismiss: () -> Unit, ) {
onSubmitClick: (filters: FiltersState) -> Unit = {}, BackHandler(enabled = visible) { onDismiss() }
) {
BackHandler(enabled = visible) { onDismiss() } Box(
modifier = Modifier.fillMaxSize()
Box( ) {
modifier = Modifier.fillMaxSize() // Dimmed background
) { AnimatedVisibility(
// Dimmed background visible = visible,
AnimatedVisibility( enter = fadeIn(),
visible = visible, exit = fadeOut()
enter = fadeIn(), ) {
exit = fadeOut() Box(
) { modifier = Modifier
Box( .fillMaxSize()
modifier = Modifier .background(Color.Black.copy(alpha = 0.35f))
.fillMaxSize() .clickable(
.background(Color.Black.copy(alpha = 0.35f)) indication = null,
.clickable( interactionSource = remember { MutableInteractionSource() }
indication = null, ) { onDismiss() }
interactionSource = remember { MutableInteractionSource() } )
) { onDismiss() } }
)
} // Sliding panel
AnimatedVisibility(
// Sliding panel visible = visible,
AnimatedVisibility( enter = slideInHorizontally(
visible = visible, initialOffsetX = { it } // from right
enter = slideInHorizontally( ),
initialOffsetX = { it } // from right exit = slideOutHorizontally(
), targetOffsetX = { it } // to right
exit = slideOutHorizontally( ),
targetOffsetX = { it } // to right modifier = Modifier
), .fillMaxHeight()
modifier = Modifier .align(Alignment.CenterEnd)
.fillMaxHeight() ) {
.align(Alignment.CenterEnd) FilterScreen(
) { onBackClick = onDismiss,
FilterScreen( onCancelClick = onDismiss,
appliedFilters, onSubmitClick = {
onBackClick = onDismiss, onSubmitClick()
onCancelClick = onDismiss, onDismiss()
onSubmitClick = { filters -> }
onSubmitClick(filters) )
onDismiss() }
} }
) }
}
}
}

View File

@ -1,142 +1,137 @@
package com.example.livingai_lg.ui.components package com.example.livingai_lg.ui.components
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
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.BasicTextField import androidx.compose.foundation.text.BasicTextField
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.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
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 kotlin.math.roundToInt import kotlin.math.roundToInt
@Composable @Composable
fun RangeFilter( fun RangeFilter(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
label: String, label: String,
min: Int, min: Int,
max: Int, max: Int,
valueFrom: Int, valueFrom: Int,
valueTo: Int, valueTo: Int,
modified: Boolean = false, onValueChange: (from: Int, to: Int) -> Unit,
onValueChange: (from: Int, to: Int) -> Unit, showSlider: Boolean = true,
showSlider: Boolean = true, valueFormatter: (Int) -> String = { it.toString() }
valueFormatter: (Int) -> String = { it.toString() } ) {
) { var fromValue by remember(valueFrom) { mutableStateOf(valueFrom) }
var fromValue by remember(valueFrom) { mutableStateOf(valueFrom) } var toValue by remember(valueTo) { mutableStateOf(valueTo) }
var toValue by remember(valueTo) { mutableStateOf(valueTo) }
Column(modifier = modifier,
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(12.dp)) {
verticalArrangement = Arrangement.spacedBy(12.dp)) {
// Label
// Label Text(
Text( text = label,
text = label, fontSize = 16.sp,
fontSize = 16.sp, fontWeight = FontWeight.SemiBold,
fontWeight = FontWeight.SemiBold, color = Color(0xFF364153)
color = Color(0xFF364153) )
)
// Slider (optional)
// Slider (optional) if (showSlider) {
if (showSlider) { RangeSlider(
RangeSlider( value = fromValue.toFloat()..toValue.toFloat(),
value = fromValue.toFloat()..toValue.toFloat(), onValueChange = { range ->
onValueChange = { range -> fromValue = range.start.roundToInt()
fromValue = range.start.roundToInt() .coerceIn(min, toValue)
.coerceIn(min, toValue) toValue = range.endInclusive.roundToInt()
toValue = range.endInclusive.roundToInt() .coerceIn(fromValue, max)
.coerceIn(fromValue, max)
onValueChange(fromValue, toValue)
onValueChange(fromValue, toValue) },
}, valueRange = min.toFloat()..max.toFloat(),
valueRange = min.toFloat()..max.toFloat(), colors = SliderDefaults.colors(
colors = SliderDefaults.colors( thumbColor = Color(0xFFD9D9D9),
thumbColor = Color(0xFFD9D9D9), activeTrackColor = Color(0xFFD9D9D9),
activeTrackColor = Color(0xFFD9D9D9), inactiveTrackColor = Color(0xFFE5E7EB)
inactiveTrackColor = Color(0xFFE5E7EB) )
) )
) }
}
// Pills
// Pills Row(
Row( modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically
verticalAlignment = Alignment.CenterVertically ) {
) { RangePill(
RangePill( modifier = Modifier.weight(1f),
modifier = Modifier.weight(1f), value = fromValue,
value = fromValue, onValueChange = { newFrom ->
modified = modified, val safeFrom = newFrom.coerceIn(min, toValue)
onValueChange = { newFrom -> fromValue = safeFrom
val safeFrom = newFrom.coerceIn(min, toValue) onValueChange(safeFrom, toValue)
fromValue = safeFrom },
onValueChange(safeFrom, toValue) formatter = valueFormatter
}, )
formatter = valueFormatter
) Text("to", fontSize = 15.sp)
Text("to", fontSize = 15.sp) RangePill(
modifier = Modifier.weight(1f),
RangePill( value = toValue,
modifier = Modifier.weight(1f), onValueChange = { newTo ->
value = toValue, val safeTo = newTo.coerceIn(fromValue, max)
modified = modified, toValue = safeTo
onValueChange = { newTo -> onValueChange(fromValue, safeTo)
val safeTo = newTo.coerceIn(fromValue, max) },
toValue = safeTo formatter = valueFormatter
onValueChange(fromValue, safeTo) )
}, }
formatter = valueFormatter
) }
} }
} @Composable
} private fun RangePill(
modifier: Modifier = Modifier,
@Composable value: Int,
private fun RangePill( onValueChange: (Int) -> Unit,
modifier: Modifier = Modifier, formatter: (Int) -> String
value: Int, ) {
modified: Boolean = false, var text by remember(value) {
onValueChange: (Int) -> Unit, mutableStateOf(formatter(value))
formatter: (Int) -> String }
) {
var text by remember(value) { Box(
mutableStateOf(formatter(value)) modifier = modifier
} .height(30.dp)
.background(Color.White, RoundedCornerShape(16.dp))
Box( .border(1.dp, Color(0x12000000), RoundedCornerShape(16.dp))
modifier = modifier .padding(horizontal = 8.dp),
.height(30.dp) contentAlignment = Alignment.Center
.background(Color.White, RoundedCornerShape(16.dp)) ) {
.border(1.dp, Color(0x12000000), RoundedCornerShape(16.dp)) BasicTextField(
.padding(horizontal = 8.dp), value = text,
contentAlignment = Alignment.Center onValueChange = { input ->
) { val digits = input.filter { it.isDigit() }
var modified = modified; text = digits
BasicTextField( digits.toIntOrNull()?.let(onValueChange)
value = text, },
onValueChange = { input -> singleLine = true,
val digits = input.filter { it.isDigit() } textStyle = TextStyle(
text = digits fontSize = 14.sp,
digits.toIntOrNull()?.let(onValueChange) color = Color(0xFF99A1AF)
}, ),
singleLine = true, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
textStyle = TextStyle( )
fontSize = 14.sp, }
color = if(modified) Color.Black else Color(0xFF99A1AF) }
),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
}
}

View File

@ -1,80 +0,0 @@
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.foundation.background
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.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun WishlistNameOverlay(
onSave: (String) -> Unit,
onDismiss: () -> Unit
) {
var name by remember { mutableStateOf("") }
AnimatedVisibility(
visible = true,
enter = slideInVertically { -it },
exit = slideOutVertically { -it }
) {
Box(
Modifier
.fillMaxWidth()
.background(Color.White)
.padding(16.dp)
) {
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text(
text = "Save Filters",
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold
)
OutlinedTextField(
value = name,
onValueChange = { name = it },
placeholder = { Text("Wishlist name") },
singleLine = true
)
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier.fillMaxWidth()
) {
TextButton(onClick = onDismiss) {
Text("Cancel")
}
Button(
onClick = {
if (name.isNotBlank()) {
onSave(name)
}
}
) {
Text("Save")
}
}
}
}
}
}

View File

@ -1,28 +1,25 @@
package com.example.livingai_lg.ui.models package com.example.livingai_lg.ui.models
import androidx.compose.material.icons.Icons import com.example.livingai_lg.R
import androidx.compose.material.icons.outlined.Build import com.example.livingai_lg.ui.navigation.AppScreen
import androidx.compose.material.icons.outlined.Home
import com.example.livingai_lg.R data class BottomNavItemData(
import com.example.livingai_lg.ui.navigation.AppScreen val label: String,
val iconRes: Int,
data class BottomNavItemData( val route: String,
val label: String, )
val iconRes: Any,
val route: String, val mainBottomNavItems = listOf(
) BottomNavItemData("Home", R.drawable.ic_home , AppScreen.BUY_ANIMALS),
BottomNavItemData("Sell", R.drawable.ic_tag, AppScreen.CREATE_ANIMAL_LISTING),
val mainBottomNavItems = listOf( // TODO:
BottomNavItemData("Home", R.drawable.ic_home , AppScreen.BUY_ANIMALS), BottomNavItemData("Chats", R.drawable.ic_chat, AppScreen.CHATS),
BottomNavItemData("Sell", R.drawable.ic_tag, AppScreen.CREATE_ANIMAL_LISTING), BottomNavItemData("Services", R.drawable.ic_config, AppScreen.CREATE_PROFILE),
// TODO: BottomNavItemData("Mandi", R.drawable.ic_market, AppScreen.CREATE_PROFILE)
BottomNavItemData("Chats", R.drawable.ic_chat, AppScreen.CHATS), )
BottomNavItemData("Services", R.drawable.ic_config, AppScreen.CREATE_PROFILE),
BottomNavItemData("Mandi", R.drawable.ic_shop2, AppScreen.CREATE_PROFILE) val chatBottomNavItems = listOf(
) BottomNavItemData("Contacts", R.drawable.ic_home ,"home"),
BottomNavItemData("Calls", R.drawable.ic_tag, "sell"),
val chatBottomNavItems = listOf( BottomNavItemData("Chats", R.drawable.ic_chat, "chats"),
BottomNavItemData("Contacts", R.drawable.ic_home ,AppScreen.CONTACTS), )
BottomNavItemData("Calls", R.drawable.ic_tag, AppScreen.CALLS),
BottomNavItemData("Chats", R.drawable.ic_chat, AppScreen.CHATS),
)

View File

@ -1,42 +0,0 @@
package com.example.livingai_lg.ui.models
data class TextFilter(
val value: String = "",
val filterSet: Boolean = false
)
data class RangeFilterState(
val min: Int,
val max: Int,
val filterSet: Boolean = false
)
data class FiltersState(
val animal: TextFilter = TextFilter(),
val breed: TextFilter = TextFilter(),
val distance: TextFilter = TextFilter(),
val gender: TextFilter = TextFilter(),
val price: RangeFilterState = RangeFilterState(0, 90_000),
val age: RangeFilterState = RangeFilterState(0, 20),
val weight: RangeFilterState = RangeFilterState(0, 9_000),
val milkYield: RangeFilterState = RangeFilterState(0, 900),
val pregnancyStatuses: Set<String> = emptySet(),
val calving: RangeFilterState = RangeFilterState(0, 10)
)
fun FiltersState.isDefault(): Boolean {
return animal.filterSet.not() &&
breed.filterSet.not() &&
distance.filterSet.not() &&
gender.filterSet.not() &&
price.filterSet.not() &&
age.filterSet.not() &&
weight.filterSet.not() &&
milkYield.filterSet.not() &&
pregnancyStatuses.isEmpty() &&
calving.filterSet.not()
}

View File

@ -1,32 +0,0 @@
package com.example.livingai_lg.ui.models
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.util.UUID
data class WishlistEntry(
val id: String = UUID.randomUUID().toString(),
val name: String,
val filters: FiltersState,
val createdAt: Long = System.currentTimeMillis()
)
object WishlistStore {
private val _wishlist =
MutableStateFlow<List<WishlistEntry>>(emptyList())
val wishlist: StateFlow<List<WishlistEntry>> = _wishlist
fun add(entry: WishlistEntry) {
_wishlist.value = _wishlist.value + entry
}
fun remove(id: String) {
_wishlist.value = _wishlist.value.filterNot { it.id == id }
}
fun clear() {
_wishlist.value = emptyList()
}
}

View File

@ -281,30 +281,30 @@ fun AppNavigation(
) )
} }
// composable(AppScreen.BUY_ANIMALS_FILTERS) { composable(AppScreen.BUY_ANIMALS_FILTERS) {
// FilterScreen( FilterScreen(
// onBackClick = { onBackClick = {
// navController.popBackStack() navController.popBackStack()
// }, },
// onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)}, onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
// onCancelClick = { onCancelClick = {
// navController.popBackStack() navController.popBackStack()
// }, },
// ) )
// } }
// composable(AppScreen.BUY_ANIMALS_SORT) { composable(AppScreen.BUY_ANIMALS_SORT) {
// SortScreen( SortScreen(
// onApplyClick = {navController.navigate(AppScreen.BUY_ANIMALS)}, onApplyClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
// onBackClick = { onBackClick = {
// navController.popBackStack() navController.popBackStack()
// }, },
// onCancelClick = { onCancelClick = {
// navController.popBackStack() navController.popBackStack()
// }, },
//
// ) )
// } }
composable(AppScreen.SAVED_LISTINGS) { composable(AppScreen.SAVED_LISTINGS) {
SavedListingsScreen( SavedListingsScreen(

View File

@ -95,17 +95,17 @@ fun NavGraphBuilder.mainNavGraph(navController: NavController) {
} }
// composable(AppScreen.BUY_ANIMALS_FILTERS) { composable(AppScreen.BUY_ANIMALS_FILTERS) {
// FilterScreen( FilterScreen(
// onBackClick = { onBackClick = {
// navController.popBackStack() navController.popBackStack()
// }, },
// onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)}, onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
// onCancelClick = { onCancelClick = {
// navController.popBackStack() navController.popBackStack()
// }, },
// ) )
// } }
composable(AppScreen.BUY_ANIMALS_SORT) { composable(AppScreen.BUY_ANIMALS_SORT) {
SortScreen( SortScreen(

View File

@ -138,7 +138,7 @@ fun BuyScreen(
// Animal type filter buttons // Animal type filter buttons
AnimalTypeSelector( AnimalTypeSelector(
animalTypes = animalTypes, animalTypes = animalTypes,
selectedAnimalType = selectedAnimalType = selectedAnimalType
) { } ) { }
// Ad space banner // Ad space banner

View File

@ -1,336 +1,327 @@
package com.example.farmmarketplace.ui.screens package com.example.farmmarketplace.ui.screens
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border 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.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape 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.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.CallMade import androidx.compose.material.icons.automirrored.filled.CallMade
import androidx.compose.material.icons.automirrored.filled.CallMissed import androidx.compose.material.icons.automirrored.filled.CallMissed
import androidx.compose.material.icons.automirrored.filled.CallReceived import androidx.compose.material.icons.automirrored.filled.CallReceived
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
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.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
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.layout.BottomNavScaffold
import com.example.livingai_lg.ui.models.chatBottomNavItems data class CallRecord(
val id: String,
data class CallRecord( val name: String,
val id: String, val initials: String,
val name: String, val callType: CallType,
val initials: String, val duration: String,
val callType: CallType, val timestamp: String,
val duration: String, val isVideoCall: Boolean = false
val timestamp: String, )
val isVideoCall: Boolean = false
) enum class CallType {
INCOMING,
enum class CallType { OUTGOING,
INCOMING, MISSED
OUTGOING, }
MISSED
} @Composable
fun CallsScreen(
@Composable onBackClick: () -> Unit = {},
fun CallsScreen( onCallClick: () -> Unit = {},
onBackClick: () -> Unit = {}, onMenuClick: () -> Unit = {},
onCallClick: () -> Unit = {}, onCallItemClick: (String) -> Unit = {},
onMenuClick: () -> Unit = {}, onTabClick: (route: String) -> Unit = {}
onCallItemClick: (String) -> Unit = {}, ) {
onTabClick: (route: String) -> Unit = {} val callHistory = listOf(
) { CallRecord(
val callHistory = listOf( id = "1",
CallRecord( name = "Farmer Kumar",
id = "1", initials = "FK",
name = "Farmer Kumar", callType = CallType.INCOMING,
initials = "FK", duration = "5m 23s",
callType = CallType.INCOMING, timestamp = "Today, 2:30 PM"
duration = "5m 23s", ),
timestamp = "Today, 2:30 PM" CallRecord(
), id = "2",
CallRecord( name = "Seller Raj",
id = "2", initials = "SR",
name = "Seller Raj", callType = CallType.OUTGOING,
initials = "SR", duration = "2m 10s",
callType = CallType.OUTGOING, timestamp = "Today, 11:45 AM"
duration = "2m 10s", ),
timestamp = "Today, 11:45 AM" CallRecord(
), id = "3",
CallRecord( name = "Buyer Priya",
id = "3", initials = "BP",
name = "Buyer Priya", callType = CallType.MISSED,
initials = "BP", duration = "",
callType = CallType.MISSED, timestamp = "Yesterday, 8:15 PM"
duration = "", ),
timestamp = "Yesterday, 8:15 PM" CallRecord(
), id = "4",
CallRecord( name = "Seller 1",
id = "4", initials = "S1",
name = "Seller 1", callType = CallType.OUTGOING,
initials = "S1", duration = "8m 45s",
callType = CallType.OUTGOING, timestamp = "Yesterday, 5:30 PM",
duration = "8m 45s", isVideoCall = true
timestamp = "Yesterday, 5:30 PM", ),
isVideoCall = true CallRecord(
), id = "5",
CallRecord( name = "Veterinarian",
id = "5", initials = "V",
name = "Veterinarian", callType = CallType.INCOMING,
initials = "V", duration = "12m 30s",
callType = CallType.INCOMING, timestamp = "2 days ago"
duration = "12m 30s", )
timestamp = "2 days ago" )
)
) Box(
modifier = Modifier
Box( .fillMaxSize()
modifier = Modifier .background(Color(0xFFFCFBF8))
.fillMaxSize() ) {
.background(Color(0xFFFCFBF8)) Column(
) { modifier = Modifier.fillMaxSize()
BottomNavScaffold( ) {
items = chatBottomNavItems, CallsHeader(
currentItem = "Calls", onBackClick = onBackClick,
onBottomNavItemClick = onTabClick, onCallClick = onCallClick,
) { paddingValues -> onMenuClick = onMenuClick
)
Column(
modifier = Modifier.fillMaxSize().padding(paddingValues) LazyColumn(
) { modifier = Modifier
CallsHeader( .fillMaxWidth()
onBackClick = onBackClick, .weight(1f)
onCallClick = onCallClick, .padding(horizontal = 16.dp, vertical = 16.dp),
onMenuClick = onMenuClick verticalArrangement = Arrangement.spacedBy(12.dp)
) ) {
items(callHistory) { call ->
LazyColumn( CallHistoryItem(
modifier = Modifier call = call,
.fillMaxWidth() onClick = { onCallItemClick(call.id) }
.weight(1f) )
.padding(horizontal = 16.dp, vertical = 16.dp), }
verticalArrangement = Arrangement.spacedBy(12.dp) }
) {
items(callHistory) { call -> ContactsBottomNav(
CallHistoryItem( currentTab = ContactsTab.CALLS,
call = call, onTabClick = onTabClick
onClick = { onCallItemClick(call.id) } )
) }
} }
} }
// ContactsBottomNav( @Composable
// currentTab = ContactsTab.CALLS, fun CallsHeader(
// onTabClick = onTabClick onBackClick: () -> Unit,
// ) onCallClick: () -> Unit,
} onMenuClick: () -> Unit,
} modifier: Modifier = Modifier
} ) {
} Column(
modifier = modifier
@Composable .fillMaxWidth()
fun CallsHeader( .height(65.dp)
onBackClick: () -> Unit, .border(
onCallClick: () -> Unit, width = 1.078.dp,
onMenuClick: () -> Unit, color = Color(0xFF000000).copy(alpha = 0.1f)
modifier: Modifier = Modifier )
) { .background(Color(0xFFFCFBF8))
Column( ) {
modifier = modifier Row(
.fillMaxWidth() modifier = Modifier
.height(65.dp) .fillMaxWidth()
.border( .padding(horizontal = 14.dp, vertical = 10.dp),
width = 1.078.dp, horizontalArrangement = Arrangement.SpaceBetween,
color = Color(0xFF000000).copy(alpha = 0.1f) verticalAlignment = Alignment.CenterVertically
) ) {
.background(Color(0xFFFCFBF8)) Row(
) { verticalAlignment = Alignment.CenterVertically,
Row( horizontalArrangement = Arrangement.spacedBy(12.dp)
modifier = Modifier ) {
.fillMaxWidth() Icon(
.padding(horizontal = 14.dp, vertical = 10.dp), imageVector = Icons.AutoMirrored.Filled.ArrowBack,
horizontalArrangement = Arrangement.SpaceBetween, contentDescription = "Back",
verticalAlignment = Alignment.CenterVertically tint = Color(0xFF0A0A0A),
) { modifier = Modifier
Row( .size(26.dp)
verticalAlignment = Alignment.CenterVertically, .clickable(
horizontalArrangement = Arrangement.spacedBy(12.dp) indication = LocalIndication.current,
) { interactionSource = remember { MutableInteractionSource() }
Icon( ) { onBackClick() }
imageVector = Icons.AutoMirrored.Filled.ArrowBack, )
contentDescription = "Back",
tint = Color(0xFF0A0A0A), Text(
modifier = Modifier text = "Calls",
.size(26.dp) fontSize = 24.sp,
.clickable( fontWeight = FontWeight.Medium,
indication = LocalIndication.current, color = Color(0xFF0A0A0A),
interactionSource = remember { MutableInteractionSource() } lineHeight = 42.sp
) { onBackClick() } )
) }
Text( Row(
text = "Calls", horizontalArrangement = Arrangement.spacedBy(16.dp),
fontSize = 24.sp, verticalAlignment = Alignment.CenterVertically
fontWeight = FontWeight.Medium, ) {
color = Color(0xFF0A0A0A), Icon(
lineHeight = 42.sp imageVector = Icons.Default.Phone,
) contentDescription = "New Call",
} tint = Color(0xFF0A0A0A),
modifier = Modifier
Row( .size(20.dp)
horizontalArrangement = Arrangement.spacedBy(16.dp), .clickable(
verticalAlignment = Alignment.CenterVertically indication = LocalIndication.current,
) { interactionSource = remember { MutableInteractionSource() }
Icon( ) { onCallClick() }
imageVector = Icons.Default.Phone, )
contentDescription = "New Call",
tint = Color(0xFF0A0A0A), Icon(
modifier = Modifier imageVector = Icons.Default.MoreVert,
.size(20.dp) contentDescription = "Menu",
.clickable( tint = Color(0xFF0A0A0A),
indication = LocalIndication.current, modifier = Modifier
interactionSource = remember { MutableInteractionSource() } .size(20.dp)
) { onCallClick() } .clickable(
) indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
Icon( ) { onMenuClick() }
imageVector = Icons.Default.MoreVert, )
contentDescription = "Menu", }
tint = Color(0xFF0A0A0A), }
modifier = Modifier }
.size(20.dp) }
.clickable(
indication = LocalIndication.current, @Composable
interactionSource = remember { MutableInteractionSource() } fun CallHistoryItem(
) { onMenuClick() } call: CallRecord,
) onClick: () -> Unit,
} modifier: Modifier = Modifier
} ) {
} Row(
} modifier = modifier
.fillMaxWidth()
@Composable .height(76.dp)
fun CallHistoryItem( .border(
call: CallRecord, width = 1.078.dp,
onClick: () -> Unit, color = Color(0xFF000000).copy(alpha = 0.1f),
modifier: Modifier = Modifier shape = RoundedCornerShape(12.dp)
) { )
Row( .background(Color(0xFFFCFBF8), RoundedCornerShape(12.dp))
modifier = modifier .clickable(
.fillMaxWidth() indication = LocalIndication.current,
.height(76.dp) interactionSource = remember { MutableInteractionSource() }
.border( ) { onClick() }
width = 1.078.dp, .padding(horizontal = 16.dp, vertical = 14.dp),
color = Color(0xFF000000).copy(alpha = 0.1f), horizontalArrangement = Arrangement.SpaceBetween,
shape = RoundedCornerShape(12.dp) verticalAlignment = Alignment.CenterVertically
) ) {
.background(Color(0xFFFCFBF8), RoundedCornerShape(12.dp)) Row(
.clickable( horizontalArrangement = Arrangement.spacedBy(12.dp),
indication = LocalIndication.current, verticalAlignment = Alignment.CenterVertically,
interactionSource = remember { MutableInteractionSource() } modifier = Modifier.weight(1f)
) { onClick() } ) {
.padding(horizontal = 16.dp, vertical = 14.dp), Box(
horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier
verticalAlignment = Alignment.CenterVertically .size(48.dp)
) { .background(Color(0xFFE5E7EB), CircleShape),
Row( contentAlignment = Alignment.Center
horizontalArrangement = Arrangement.spacedBy(12.dp), ) {
verticalAlignment = Alignment.CenterVertically, Text(
modifier = Modifier.weight(1f) text = call.initials,
) { fontSize = 14.sp,
Box( fontWeight = FontWeight.Medium,
modifier = Modifier color = Color(0xFF0A0A0A),
.size(48.dp) lineHeight = 21.sp
.background(Color(0xFFE5E7EB), CircleShape), )
contentAlignment = Alignment.Center }
) {
Text( Column(
text = call.initials, verticalArrangement = Arrangement.spacedBy(4.dp)
fontSize = 14.sp, ) {
fontWeight = FontWeight.Medium, Text(
color = Color(0xFF0A0A0A), text = call.name,
lineHeight = 21.sp fontSize = 16.sp,
) fontWeight = FontWeight.Medium,
} color = Color(0xFF1E2939),
lineHeight = 20.sp,
Column( letterSpacing = (-0.312).sp
verticalArrangement = Arrangement.spacedBy(4.dp) )
) {
Text( Row(
text = call.name, horizontalArrangement = Arrangement.spacedBy(8.dp),
fontSize = 16.sp, verticalAlignment = Alignment.CenterVertically
fontWeight = FontWeight.Medium, ) {
color = Color(0xFF1E2939), Icon(
lineHeight = 20.sp, imageVector = when (call.callType) {
letterSpacing = (-0.312).sp CallType.INCOMING -> Icons.AutoMirrored.Filled.CallReceived
) CallType.OUTGOING -> Icons.AutoMirrored.Filled.CallMade
CallType.MISSED -> Icons.AutoMirrored.Filled.CallMissed
Row( },
horizontalArrangement = Arrangement.spacedBy(8.dp), contentDescription = call.callType.name,
verticalAlignment = Alignment.CenterVertically tint = when (call.callType) {
) { CallType.INCOMING -> Color(0xFF00A63E)
Icon( CallType.OUTGOING -> Color(0xFF155DFC)
imageVector = when (call.callType) { CallType.MISSED -> Color(0xFFE7000B)
CallType.INCOMING -> Icons.AutoMirrored.Filled.CallReceived },
CallType.OUTGOING -> Icons.AutoMirrored.Filled.CallMade modifier = Modifier.size(20.dp)
CallType.MISSED -> Icons.AutoMirrored.Filled.CallMissed )
},
contentDescription = call.callType.name, Text(
tint = when (call.callType) { text = when (call.callType) {
CallType.INCOMING -> Color(0xFF00A63E) CallType.INCOMING -> "Incoming • ${call.duration}"
CallType.OUTGOING -> Color(0xFF155DFC) CallType.OUTGOING -> "Outgoing • ${call.duration}"
CallType.MISSED -> Color(0xFFE7000B) CallType.MISSED -> "Missed"
}, },
modifier = Modifier.size(20.dp) fontSize = 12.sp,
) fontWeight = FontWeight.Normal,
color = Color(0xFF717182),
Text( lineHeight = 16.sp
text = when (call.callType) { )
CallType.INCOMING -> "Incoming • ${call.duration}" }
CallType.OUTGOING -> "Outgoing • ${call.duration}" }
CallType.MISSED -> "Missed" }
},
fontSize = 12.sp, Column(
fontWeight = FontWeight.Normal, horizontalAlignment = Alignment.End,
color = Color(0xFF717182), verticalArrangement = Arrangement.spacedBy(8.dp)
lineHeight = 16.sp ) {
) Text(
} text = call.timestamp,
} fontSize = 12.sp,
} fontWeight = FontWeight.Normal,
color = Color(0xFF717182),
Column( lineHeight = 16.sp
horizontalAlignment = Alignment.End, )
verticalArrangement = Arrangement.spacedBy(8.dp)
) { Icon(
Text( imageVector = if (call.isVideoCall) Icons.Default.Videocam else Icons.Default.Phone,
text = call.timestamp, contentDescription = if (call.isVideoCall) "Video Call" else "Voice Call",
fontSize = 12.sp, tint = Color(0xFF030213),
fontWeight = FontWeight.Normal, modifier = Modifier.size(24.dp)
color = Color(0xFF717182), )
lineHeight = 16.sp }
) }
}
Icon(
imageVector = if (call.isVideoCall) Icons.Default.Videocam else Icons.Default.Phone,
contentDescription = if (call.isVideoCall) "Video Call" else "Voice Call",
tint = Color(0xFF030213),
modifier = Modifier.size(24.dp)
)
}
}
}

View File

@ -1,341 +1,333 @@
package com.example.livingai_lg.ui.screens.chat package com.example.livingai_lg.ui.screens.chat
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border 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.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape 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.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
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.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
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.farmmarketplace.ui.screens.ContactsBottomNav import com.example.farmmarketplace.ui.screens.ContactsBottomNav
import com.example.farmmarketplace.ui.screens.ContactsTab import com.example.farmmarketplace.ui.screens.ContactsTab
import com.example.livingai_lg.ui.layout.BottomNavScaffold
import com.example.livingai_lg.ui.models.chatBottomNavItems data class ChatPreview(
val id: String,
data class ChatPreview( val name: String,
val id: String, val initials: String,
val name: String, val lastMessage: String,
val initials: String, val timestamp: String,
val lastMessage: String, val isOnline: Boolean = false,
val timestamp: String, val unreadCount: Int = 0
val isOnline: Boolean = false, )
val unreadCount: Int = 0
) @Composable
fun ChatsScreen(
@Composable onBackClick: () -> Unit = {},
fun ChatsScreen( onNewChatClick: () -> Unit = {},
onBackClick: () -> Unit = {}, onMenuClick: () -> Unit = {},
onNewChatClick: () -> Unit = {}, onChatItemClick: (String) -> Unit = {},
onMenuClick: () -> Unit = {}, onTabClick: (route: String) -> Unit = {}
onChatItemClick: (String) -> Unit = {}, ) {
onTabClick: (route: String) -> Unit = {} val chatList = listOf(
) { ChatPreview(
val chatList = listOf( id = "1",
ChatPreview( name = "Farmer Kumar",
id = "1", initials = "FK",
name = "Farmer Kumar", lastMessage = "The cows are healthy and ready for viewing",
initials = "FK", timestamp = "Today, 2:30 PM",
lastMessage = "The cows are healthy and ready for viewing", isOnline = true,
timestamp = "Today, 2:30 PM", unreadCount = 2
isOnline = true, ),
unreadCount = 2 ChatPreview(
), id = "2",
ChatPreview( name = "Seller Raj",
id = "2", initials = "SR",
name = "Seller Raj", lastMessage = "You: Can you send more photos?",
initials = "SR", timestamp = "Today, 11:45 AM",
lastMessage = "You: Can you send more photos?", isOnline = true,
timestamp = "Today, 11:45 AM", unreadCount = 0
isOnline = true, ),
unreadCount = 0 ChatPreview(
), id = "3",
ChatPreview( name = "Buyer Priya",
id = "3", initials = "BP",
name = "Buyer Priya", lastMessage = "What's the best time to visit?",
initials = "BP", timestamp = "Yesterday, 8:15 PM",
lastMessage = "What's the best time to visit?", isOnline = false,
timestamp = "Yesterday, 8:15 PM", unreadCount = 1
isOnline = false, ),
unreadCount = 1 ChatPreview(
), id = "4",
ChatPreview( name = "Seller 1",
id = "4", initials = "S1",
name = "Seller 1", lastMessage = "You: Thanks for the information",
initials = "S1", timestamp = "Yesterday, 5:30 PM",
lastMessage = "You: Thanks for the information", isOnline = true,
timestamp = "Yesterday, 5:30 PM", unreadCount = 0
isOnline = true, ),
unreadCount = 0 ChatPreview(
), id = "5",
ChatPreview( name = "Veterinarian",
id = "5", initials = "V",
name = "Veterinarian", lastMessage = "The animal health check is complete",
initials = "V", timestamp = "2 days ago",
lastMessage = "The animal health check is complete", isOnline = false,
timestamp = "2 days ago", unreadCount = 0
isOnline = false, ),
unreadCount = 0 ChatPreview(
), id = "6",
ChatPreview( name = "Market Vendor",
id = "6", initials = "MV",
name = "Market Vendor", lastMessage = "You: Available this weekend?",
initials = "MV", timestamp = "3 days ago",
lastMessage = "You: Available this weekend?", isOnline = false,
timestamp = "3 days ago", unreadCount = 0
isOnline = false, )
unreadCount = 0 )
)
) Box(
modifier = Modifier
Box( .fillMaxSize()
modifier = Modifier .background(Color(0xFFFCFBF8))
.fillMaxSize() ) {
.background(Color(0xFFFCFBF8)) Column(
) { modifier = Modifier.fillMaxSize()
BottomNavScaffold( ) {
items = chatBottomNavItems, ChatsHeader(
currentItem = "Chats", onBackClick = onBackClick,
onBottomNavItemClick = onTabClick, onNewChatClick = onNewChatClick,
) { paddingValues -> onMenuClick = onMenuClick
Column( )
modifier = Modifier.fillMaxSize().padding(paddingValues)
) { LazyColumn(
ChatsHeader( modifier = Modifier
onBackClick = onBackClick, .fillMaxWidth()
onNewChatClick = onNewChatClick, .weight(1f)
onMenuClick = onMenuClick .padding(horizontal = 16.dp, vertical = 16.dp),
) verticalArrangement = Arrangement.spacedBy(12.dp)
) {
LazyColumn( items(chatList) { chat ->
modifier = Modifier ChatListItem(
.fillMaxWidth() chat = chat,
.weight(1f) onClick = { onChatItemClick(chat.id) }
.padding(horizontal = 16.dp, vertical = 16.dp), )
verticalArrangement = Arrangement.spacedBy(12.dp) }
) { }
items(chatList) { chat ->
ChatListItem( ContactsBottomNav(
chat = chat, currentTab = ContactsTab.CHATS,
onClick = { onChatItemClick(chat.id) } onTabClick = onTabClick
) )
} }
} }
}
// ContactsBottomNav(
// currentTab = ContactsTab.CHATS, @Composable
// onTabClick = onTabClick fun ChatsHeader(
// ) onBackClick: () -> Unit,
} onNewChatClick: () -> Unit,
} onMenuClick: () -> Unit,
} modifier: Modifier = Modifier
} ) {
Column(
@Composable modifier = modifier
fun ChatsHeader( .fillMaxWidth()
onBackClick: () -> Unit, .height(65.dp)
onNewChatClick: () -> Unit, .border(
onMenuClick: () -> Unit, width = 1.078.dp,
modifier: Modifier = Modifier color = Color(0xFF000000).copy(alpha = 0.1f)
) { )
Column( .background(Color(0xFFFCFBF8))
modifier = modifier ) {
.fillMaxWidth() Row(
.height(65.dp) modifier = Modifier
.border( .fillMaxWidth()
width = 1.078.dp, .padding(horizontal = 14.dp, vertical = 10.dp),
color = Color(0xFF000000).copy(alpha = 0.1f) horizontalArrangement = Arrangement.SpaceBetween,
) verticalAlignment = Alignment.CenterVertically
.background(Color(0xFFFCFBF8)) ) {
) { Row(
Row( verticalAlignment = Alignment.CenterVertically,
modifier = Modifier horizontalArrangement = Arrangement.spacedBy(12.dp)
.fillMaxWidth() ) {
.padding(horizontal = 14.dp, vertical = 10.dp), Icon(
horizontalArrangement = Arrangement.SpaceBetween, imageVector = Icons.AutoMirrored.Filled.ArrowBack,
verticalAlignment = Alignment.CenterVertically contentDescription = "Back",
) { tint = Color(0xFF0A0A0A),
Row( modifier = Modifier
verticalAlignment = Alignment.CenterVertically, .size(26.dp)
horizontalArrangement = Arrangement.spacedBy(12.dp) .clickable(
) { indication = LocalIndication.current,
Icon( interactionSource = remember { MutableInteractionSource() }
imageVector = Icons.AutoMirrored.Filled.ArrowBack, ) { onBackClick() }
contentDescription = "Back", )
tint = Color(0xFF0A0A0A),
modifier = Modifier Text(
.size(26.dp) text = "Chats",
.clickable( fontSize = 24.sp,
indication = LocalIndication.current, fontWeight = FontWeight.Medium,
interactionSource = remember { MutableInteractionSource() } color = Color(0xFF0A0A0A),
) { onBackClick() } lineHeight = 42.sp
) )
}
Text(
text = "Chats", Row(
fontSize = 24.sp, horizontalArrangement = Arrangement.spacedBy(16.dp),
fontWeight = FontWeight.Medium, verticalAlignment = Alignment.CenterVertically
color = Color(0xFF0A0A0A), ) {
lineHeight = 42.sp Icon(
) imageVector = Icons.Default.Edit,
} contentDescription = "New Chat",
tint = Color(0xFF0A0A0A),
Row( modifier = Modifier
horizontalArrangement = Arrangement.spacedBy(16.dp), .size(20.dp)
verticalAlignment = Alignment.CenterVertically .clickable(
) { indication = LocalIndication.current,
Icon( interactionSource = remember { MutableInteractionSource() }
imageVector = Icons.Default.Edit, ) { onNewChatClick() }
contentDescription = "New Chat", )
tint = Color(0xFF0A0A0A),
modifier = Modifier Icon(
.size(20.dp) imageVector = Icons.Default.MoreVert,
.clickable( contentDescription = "Menu",
indication = LocalIndication.current, tint = Color(0xFF0A0A0A),
interactionSource = remember { MutableInteractionSource() } modifier = Modifier
) { onNewChatClick() } .size(20.dp)
) .clickable(
indication = LocalIndication.current,
Icon( interactionSource = remember { MutableInteractionSource() }
imageVector = Icons.Default.MoreVert, ) { onMenuClick() }
contentDescription = "Menu", )
tint = Color(0xFF0A0A0A), }
modifier = Modifier }
.size(20.dp) }
.clickable( }
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() } @Composable
) { onMenuClick() } fun ChatListItem(
) chat: ChatPreview,
} onClick: () -> Unit,
} modifier: Modifier = Modifier
} ) {
} Row(
modifier = modifier
@Composable .fillMaxWidth()
fun ChatListItem( .height(76.dp)
chat: ChatPreview, .border(
onClick: () -> Unit, width = 1.078.dp,
modifier: Modifier = Modifier color = Color(0xFF000000).copy(alpha = 0.1f),
) { shape = RoundedCornerShape(12.dp)
Row( )
modifier = modifier .background(Color(0xFFFCFBF8), RoundedCornerShape(12.dp))
.fillMaxWidth() .clickable(
.height(76.dp) indication = LocalIndication.current,
.border( interactionSource = remember { MutableInteractionSource() }
width = 1.078.dp, ) { onClick() }
color = Color(0xFF000000).copy(alpha = 0.1f), .padding(horizontal = 16.dp, vertical = 14.dp),
shape = RoundedCornerShape(12.dp) horizontalArrangement = Arrangement.SpaceBetween,
) verticalAlignment = Alignment.CenterVertically
.background(Color(0xFFFCFBF8), RoundedCornerShape(12.dp)) ) {
.clickable( Row(
indication = LocalIndication.current, horizontalArrangement = Arrangement.spacedBy(12.dp),
interactionSource = remember { MutableInteractionSource() } verticalAlignment = Alignment.CenterVertically,
) { onClick() } modifier = Modifier.weight(1f)
.padding(horizontal = 16.dp, vertical = 14.dp), ) {
horizontalArrangement = Arrangement.SpaceBetween, Box(
verticalAlignment = Alignment.CenterVertically modifier = Modifier
) { .size(48.dp)
Row( .background(Color(0xFFE5E7EB), CircleShape),
horizontalArrangement = Arrangement.spacedBy(12.dp), contentAlignment = Alignment.Center
verticalAlignment = Alignment.CenterVertically, ) {
modifier = Modifier.weight(1f) Text(
) { text = chat.initials,
Box( fontSize = 14.sp,
modifier = Modifier fontWeight = FontWeight.Medium,
.size(48.dp) color = Color(0xFF0A0A0A),
.background(Color(0xFFE5E7EB), CircleShape), lineHeight = 21.sp
contentAlignment = Alignment.Center )
) {
Text( if (chat.isOnline) {
text = chat.initials, Box(
fontSize = 14.sp, modifier = Modifier
fontWeight = FontWeight.Medium, .align(Alignment.BottomEnd)
color = Color(0xFF0A0A0A), .size(12.dp)
lineHeight = 21.sp .background(Color(0xFF00A63E), CircleShape)
) .border(2.dp, Color.White, CircleShape)
)
if (chat.isOnline) { }
Box( }
modifier = Modifier
.align(Alignment.BottomEnd) Column(
.size(12.dp) verticalArrangement = Arrangement.spacedBy(4.dp),
.background(Color(0xFF00A63E), CircleShape) modifier = Modifier.weight(1f)
.border(2.dp, Color.White, CircleShape) ) {
) Text(
} text = chat.name,
} fontSize = 16.sp,
fontWeight = FontWeight.Medium,
Column( color = Color(0xFF1E2939),
verticalArrangement = Arrangement.spacedBy(4.dp), lineHeight = 20.sp,
modifier = Modifier.weight(1f) letterSpacing = (-0.312).sp
) { )
Text(
text = chat.name, Text(
fontSize = 16.sp, text = chat.lastMessage,
fontWeight = FontWeight.Medium, fontSize = 12.sp,
color = Color(0xFF1E2939), fontWeight = FontWeight.Normal,
lineHeight = 20.sp, color = Color(0xFF717182),
letterSpacing = (-0.312).sp lineHeight = 16.sp,
) maxLines = 1,
overflow = TextOverflow.Ellipsis
Text( )
text = chat.lastMessage, }
fontSize = 12.sp, }
fontWeight = FontWeight.Normal,
color = Color(0xFF717182), Column(
lineHeight = 16.sp, horizontalAlignment = Alignment.End,
maxLines = 1, verticalArrangement = Arrangement.spacedBy(8.dp)
overflow = TextOverflow.Ellipsis ) {
) Text(
} text = chat.timestamp,
} fontSize = 12.sp,
fontWeight = FontWeight.Normal,
Column( color = Color(0xFF717182),
horizontalAlignment = Alignment.End, lineHeight = 16.sp
verticalArrangement = Arrangement.spacedBy(8.dp) )
) {
Text( if (chat.unreadCount > 0) {
text = chat.timestamp, Box(
fontSize = 12.sp, modifier = Modifier
fontWeight = FontWeight.Normal, .size(24.dp)
color = Color(0xFF717182), .background(Color(0xFF155DFC), CircleShape),
lineHeight = 16.sp contentAlignment = Alignment.Center
) ) {
Text(
if (chat.unreadCount > 0) { text = chat.unreadCount.toString(),
Box( fontSize = 10.sp,
modifier = Modifier fontWeight = FontWeight.Medium,
.size(24.dp) color = Color.White,
.background(Color(0xFF155DFC), CircleShape), lineHeight = 14.sp
contentAlignment = Alignment.Center )
) { }
Text( }
text = chat.unreadCount.toString(), }
fontSize = 10.sp, }
fontWeight = FontWeight.Medium,
color = Color.White,
lineHeight = 14.sp
)
}
}
}
}
} }

View File

@ -1,432 +1,422 @@
package com.example.farmmarketplace.ui.screens package com.example.farmmarketplace.ui.screens
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border 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.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
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.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.Chat import androidx.compose.material.icons.automirrored.filled.Chat
import androidx.compose.material.icons.automirrored.filled.Message import androidx.compose.material.icons.automirrored.filled.Message
import androidx.compose.material.icons.automirrored.outlined.Message import androidx.compose.material.icons.automirrored.outlined.Message
import androidx.compose.material.icons.filled.Chat import androidx.compose.material.icons.filled.Chat
import androidx.compose.material.icons.filled.Contacts import androidx.compose.material.icons.filled.Contacts
import androidx.compose.material.icons.filled.Message import androidx.compose.material.icons.filled.Message
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Phone import androidx.compose.material.icons.filled.Phone
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.outlined.Phone import androidx.compose.material.icons.outlined.Phone
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
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.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.graphics.vector.path
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
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.layout.BottomNavScaffold import com.example.livingai_lg.ui.navigation.AppScreen
import com.example.livingai_lg.ui.models.chatBottomNavItems
import com.example.livingai_lg.ui.models.mainBottomNavItems data class Contact(
import com.example.livingai_lg.ui.navigation.AppScreen val id: String,
val name: String,
data class Contact( val initials: String,
val id: String, val status: String,
val name: String, val isOnline: Boolean = false,
val initials: String, val phoneNumber: String? = null
val status: String, )
val isOnline: Boolean = false,
val phoneNumber: String? = null enum class ContactsTab {
) CONTACTS,
CALLS,
enum class ContactsTab { CHATS
CONTACTS, }
CALLS,
CHATS @Composable
} fun ContactsScreen(
onBackClick: () -> Unit = {},
@Composable onSearchClick: () -> Unit = {},
fun ContactsScreen( onMenuClick: () -> Unit = {},
onBackClick: () -> Unit = {}, onContactClick: (String) -> Unit = {},
onSearchClick: () -> Unit = {}, onCallClick: (String) -> Unit = {},
onMenuClick: () -> Unit = {}, onMessageClick: (String) -> Unit = {},
onContactClick: (String) -> Unit = {}, onTabClick: (route: String) -> Unit = {}
onCallClick: (String) -> Unit = {}, ) {
onMessageClick: (String) -> Unit = {}, val contacts = listOf(
onTabClick: (route: String) -> Unit = {} Contact(
) { id = "1",
val contacts = listOf( name = "Farmer Kumar",
Contact( initials = "FK",
id = "1", status = "Online",
name = "Farmer Kumar", isOnline = true
initials = "FK", ),
status = "Online", Contact(
isOnline = true id = "2",
), name = "Seller Raj",
Contact( initials = "SR",
id = "2", status = "+91 98765 43211",
name = "Seller Raj", isOnline = false,
initials = "SR", phoneNumber = "+91 98765 43211"
status = "+91 98765 43211", ),
isOnline = false, Contact(
phoneNumber = "+91 98765 43211" id = "3",
), name = "Buyer Priya",
Contact( initials = "BP",
id = "3", status = "Online",
name = "Buyer Priya", isOnline = true
initials = "BP", ),
status = "Online", Contact(
isOnline = true id = "4",
), name = "Seller 1",
Contact( initials = "S1",
id = "4", status = "+91 98765 43213",
name = "Seller 1", isOnline = false,
initials = "S1", phoneNumber = "+91 98765 43213"
status = "+91 98765 43213", ),
isOnline = false, Contact(
phoneNumber = "+91 98765 43213" id = "5",
), name = "Veterinarian",
Contact( initials = "V",
id = "5", status = "Online",
name = "Veterinarian", isOnline = true
initials = "V", ),
status = "Online", Contact(
isOnline = true id = "6",
), name = "Farm Supply",
Contact( initials = "FS",
id = "6", status = "+91 98765 43215",
name = "Farm Supply", isOnline = false,
initials = "FS", phoneNumber = "+91 98765 43215"
status = "+91 98765 43215", ),
isOnline = false, Contact(
phoneNumber = "+91 98765 43215" id = "7",
), name = "Transport Co.",
Contact( initials = "TC",
id = "7", status = "+91 98765 43216",
name = "Transport Co.", isOnline = false,
initials = "TC", phoneNumber = "+91 98765 43216"
status = "+91 98765 43216", )
isOnline = false, )
phoneNumber = "+91 98765 43216"
) Box(
) modifier = Modifier
.fillMaxSize()
Box( .background(Color(0xFFFCFBF8))
modifier = Modifier ) {
.fillMaxSize() Column(
.background(Color(0xFFFCFBF8)) modifier = Modifier.fillMaxSize()
) { ) {
BottomNavScaffold( ContactsHeader(
items = chatBottomNavItems, onBackClick = onBackClick,
currentItem = "Contacts", onSearchClick = onSearchClick,
onBottomNavItemClick = onTabClick, onMenuClick = onMenuClick
) { paddingValues -> )
Column( LazyColumn(
modifier = Modifier.fillMaxSize().padding(paddingValues) modifier = Modifier
) { .fillMaxWidth()
ContactsHeader( .weight(1f)
onBackClick = onBackClick, ) {
onSearchClick = onSearchClick, items(contacts) { contact ->
onMenuClick = onMenuClick ContactItem(
) contact = contact,
onContactClick = { onContactClick(contact.id) },
LazyColumn( onCallClick = { onCallClick(contact.id) },
modifier = Modifier onMessageClick = { onMessageClick(contact.id) }
.fillMaxWidth() )
.weight(1f) }
) { }
items(contacts) { contact ->
ContactItem( ContactsBottomNav(
contact = contact, currentTab = ContactsTab.CONTACTS,
onContactClick = { onContactClick(contact.id) }, onTabClick = onTabClick
onCallClick = { onCallClick(contact.id) }, )
onMessageClick = { onMessageClick(contact.id) } }
) }
} }
}
@Composable
// ContactsBottomNav( fun ContactsHeader(
// currentTab = ContactsTab.CONTACTS, onBackClick: () -> Unit,
// onTabClick = onTabClick onSearchClick: () -> Unit,
// ) onMenuClick: () -> Unit,
} modifier: Modifier = Modifier
} ) {
} Column(
} modifier = modifier
.fillMaxWidth()
@Composable .height(65.dp)
fun ContactsHeader( .border(
onBackClick: () -> Unit, width = 1.078.dp,
onSearchClick: () -> Unit, color = Color(0xFF000000).copy(alpha = 0.1f)
onMenuClick: () -> Unit, )
modifier: Modifier = Modifier .background(Color(0xFFFCFBF8))
) { ) {
Column( Row(
modifier = modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(65.dp) .padding(horizontal = 16.dp, vertical = 12.dp),
.border( horizontalArrangement = Arrangement.SpaceBetween,
width = 1.078.dp, verticalAlignment = Alignment.CenterVertically
color = Color(0xFF000000).copy(alpha = 0.1f) ) {
) Row(
.background(Color(0xFFFCFBF8)) verticalAlignment = Alignment.CenterVertically,
) { horizontalArrangement = Arrangement.spacedBy(12.dp)
Row( ) {
modifier = Modifier Icon(
.fillMaxWidth() imageVector = Icons.AutoMirrored.Default.ArrowBack,
.padding(horizontal = 16.dp, vertical = 12.dp), contentDescription = "Back",
horizontalArrangement = Arrangement.SpaceBetween, tint = Color(0xFF0A0A0A),
verticalAlignment = Alignment.CenterVertically modifier = Modifier
) { .size(26.dp)
Row( .clickable(
verticalAlignment = Alignment.CenterVertically, indication = LocalIndication.current,
horizontalArrangement = Arrangement.spacedBy(12.dp) interactionSource = remember { MutableInteractionSource() }
) { ) { onBackClick() }
Icon( )
imageVector = Icons.AutoMirrored.Default.ArrowBack,
contentDescription = "Back", Text(
tint = Color(0xFF0A0A0A), text = "Contacts",
modifier = Modifier fontSize = 24.sp,
.size(26.dp) fontWeight = FontWeight.Medium,
.clickable( color = Color(0xFF0A0A0A),
indication = LocalIndication.current, lineHeight = 42.sp
interactionSource = remember { MutableInteractionSource() } )
) { onBackClick() } }
)
Row(
Text( horizontalArrangement = Arrangement.spacedBy(16.dp),
text = "Contacts", verticalAlignment = Alignment.CenterVertically
fontSize = 24.sp, ) {
fontWeight = FontWeight.Medium, Icon(
color = Color(0xFF0A0A0A), imageVector = Icons.Default.Search,
lineHeight = 42.sp contentDescription = "Search",
) tint = Color(0xFF0A0A0A),
} modifier = Modifier
.size(20.dp)
Row( .clickable(
horizontalArrangement = Arrangement.spacedBy(16.dp), indication = LocalIndication.current,
verticalAlignment = Alignment.CenterVertically interactionSource = remember { MutableInteractionSource() }
) { ) { onSearchClick() }
Icon( )
imageVector = Icons.Default.Search,
contentDescription = "Search", Icon(
tint = Color(0xFF0A0A0A), imageVector = Icons.Default.MoreVert,
modifier = Modifier contentDescription = "Menu",
.size(20.dp) tint = Color(0xFF0A0A0A),
.clickable( modifier = Modifier
indication = LocalIndication.current, .size(20.dp)
interactionSource = remember { MutableInteractionSource() } .clickable(
) { onSearchClick() } indication = LocalIndication.current,
) interactionSource = remember { MutableInteractionSource() }
) { onMenuClick() }
Icon( )
imageVector = Icons.Default.MoreVert, }
contentDescription = "Menu", }
tint = Color(0xFF0A0A0A), }
modifier = Modifier }
.size(20.dp)
.clickable( @Composable
indication = LocalIndication.current, fun ContactItem(
interactionSource = remember { MutableInteractionSource() } contact: Contact,
) { onMenuClick() } onContactClick: () -> Unit,
) onCallClick: () -> Unit,
} onMessageClick: () -> Unit,
} modifier: Modifier = Modifier
} ) {
} Row(
modifier = modifier
@Composable .fillMaxWidth()
fun ContactItem( .height(73.dp)
contact: Contact, .border(
onContactClick: () -> Unit, width = 1.078.dp,
onCallClick: () -> Unit, color = Color(0xFF000000).copy(alpha = 0.05f)
onMessageClick: () -> Unit, )
modifier: Modifier = Modifier .background(Color(0xFFFCFBF8))
) { .clickable(
Row( indication = LocalIndication.current,
modifier = modifier interactionSource = remember { MutableInteractionSource() }
.fillMaxWidth() ) { onContactClick() }
.height(73.dp) .padding(horizontal = 16.dp, vertical = 12.dp),
.border( horizontalArrangement = Arrangement.SpaceBetween,
width = 1.078.dp, verticalAlignment = Alignment.CenterVertically
color = Color(0xFF000000).copy(alpha = 0.05f) ) {
) Row(
.background(Color(0xFFFCFBF8)) horizontalArrangement = Arrangement.spacedBy(12.dp),
.clickable( verticalAlignment = Alignment.CenterVertically
indication = LocalIndication.current, ) {
interactionSource = remember { MutableInteractionSource() } Box(
) { onContactClick() } modifier = Modifier
.padding(horizontal = 16.dp, vertical = 12.dp), .size(48.dp)
horizontalArrangement = Arrangement.SpaceBetween, .background(Color(0xFFE5E7EB), CircleShape),
verticalAlignment = Alignment.CenterVertically contentAlignment = Alignment.Center
) { ) {
Row( Text(
horizontalArrangement = Arrangement.spacedBy(12.dp), text = contact.initials,
verticalAlignment = Alignment.CenterVertically fontSize = 14.sp,
) { fontWeight = FontWeight.Medium,
Box( color = Color(0xFF0A0A0A),
modifier = Modifier lineHeight = 21.sp
.size(48.dp) )
.background(Color(0xFFE5E7EB), CircleShape), }
contentAlignment = Alignment.Center
) { Column(
Text( verticalArrangement = Arrangement.spacedBy(4.dp)
text = contact.initials, ) {
fontSize = 14.sp, Text(
fontWeight = FontWeight.Medium, text = contact.name,
color = Color(0xFF0A0A0A), fontSize = 16.sp,
lineHeight = 21.sp fontWeight = FontWeight.Medium,
) color = Color(0xFF1E2939),
} lineHeight = 20.sp,
letterSpacing = (-0.312).sp
Column( )
verticalArrangement = Arrangement.spacedBy(4.dp)
) { Text(
Text( text = contact.status,
text = contact.name, fontSize = 12.sp,
fontSize = 16.sp, fontWeight = FontWeight.Normal,
fontWeight = FontWeight.Medium, color = Color(0xFF717182),
color = Color(0xFF1E2939), lineHeight = 16.sp
lineHeight = 20.sp, )
letterSpacing = (-0.312).sp }
) }
Text( Row(
text = contact.status, horizontalArrangement = Arrangement.spacedBy(12.dp),
fontSize = 12.sp, verticalAlignment = Alignment.CenterVertically
fontWeight = FontWeight.Normal, ) {
color = Color(0xFF717182), Box(
lineHeight = 16.sp modifier = Modifier
) .size(36.dp)
} .clickable(
} indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
Row( ) { onCallClick() },
horizontalArrangement = Arrangement.spacedBy(12.dp), contentAlignment = Alignment.Center
verticalAlignment = Alignment.CenterVertically ) {
) { Icon(
Box( imageVector = Icons.Outlined.Phone,
modifier = Modifier contentDescription = "Call",
.size(36.dp) tint = Color(0xFF030213),
.clickable( modifier = Modifier.size(20.dp)
indication = LocalIndication.current, )
interactionSource = remember { MutableInteractionSource() } }
) { onCallClick() },
contentAlignment = Alignment.Center Box(
) { modifier = Modifier
Icon( .size(36.dp)
imageVector = Icons.Outlined.Phone, .clickable(
contentDescription = "Call", indication = LocalIndication.current,
tint = Color(0xFF030213), interactionSource = remember { MutableInteractionSource() }
modifier = Modifier.size(20.dp) ) { onMessageClick() },
) contentAlignment = Alignment.Center
} ) {
Icon(
Box( imageVector = Icons.AutoMirrored.Outlined.Message,
modifier = Modifier contentDescription = "Message",
.size(36.dp) tint = Color(0xFF030213),
.clickable( modifier = Modifier.size(20.dp)
indication = LocalIndication.current, )
interactionSource = remember { MutableInteractionSource() } }
) { onMessageClick() }, }
contentAlignment = Alignment.Center }
) { }
Icon(
imageVector = Icons.AutoMirrored.Outlined.Message, @Composable
contentDescription = "Message", fun ContactsBottomNav(
tint = Color(0xFF030213), currentTab: ContactsTab,
modifier = Modifier.size(20.dp) onTabClick: (route: String) -> Unit,
) modifier: Modifier = Modifier
} ) {
} Row(
} modifier = modifier
} .fillMaxWidth()
.height(65.dp)
@Composable .border(
fun ContactsBottomNav( width = 1.078.dp,
currentTab: ContactsTab, color = Color(0xFF000000).copy(alpha = 0.1f)
onTabClick: (route: String) -> Unit, )
modifier: Modifier = Modifier .background(Color(0xFFFCFBF8))
) { .padding(horizontal = 32.dp),
Row( horizontalArrangement = Arrangement.SpaceBetween,
modifier = modifier verticalAlignment = Alignment.CenterVertically
.fillMaxWidth() ) {
.height(65.dp)
.border( ContactsTabItem(
width = 1.078.dp, icon = Icons.AutoMirrored.Filled.Chat,
color = Color(0xFF000000).copy(alpha = 0.1f) label = "Chats",
) isSelected = currentTab == ContactsTab.CHATS,
.background(Color(0xFFFCFBF8)) onClick = { onTabClick(AppScreen.CHATS) }
.padding(horizontal = 32.dp), )
horizontalArrangement = Arrangement.SpaceBetween, ContactsTabItem(
verticalAlignment = Alignment.CenterVertically icon = Icons.Default.Phone,
) { label = "Calls",
isSelected = currentTab == ContactsTab.CALLS,
ContactsTabItem( onClick = { onTabClick(AppScreen.CALLS) }
icon = Icons.AutoMirrored.Filled.Chat, )
label = "Chats", ContactsTabItem(
isSelected = currentTab == ContactsTab.CHATS, icon = Icons.Default.Contacts,
onClick = { onTabClick(AppScreen.CHATS) } label = "Contacts",
) isSelected = currentTab == ContactsTab.CONTACTS,
ContactsTabItem( onClick = { onTabClick(AppScreen.CONTACTS) }
icon = Icons.Default.Phone, )
label = "Calls",
isSelected = currentTab == ContactsTab.CALLS,
onClick = { onTabClick(AppScreen.CALLS) } }
) }
ContactsTabItem(
icon = Icons.Default.Contacts, @Composable
label = "Contacts", fun ContactsTabItem(
isSelected = currentTab == ContactsTab.CONTACTS, icon: ImageVector,
onClick = { onTabClick(AppScreen.CONTACTS) } label: String,
) isSelected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
} ) {
} Column(
modifier = modifier
@Composable .clickable(
fun ContactsTabItem( indication = LocalIndication.current,
icon: ImageVector, interactionSource = remember { MutableInteractionSource() }
label: String, ) { onClick() }
isSelected: Boolean, .padding(4.dp),
onClick: () -> Unit, horizontalAlignment = Alignment.CenterHorizontally,
modifier: Modifier = Modifier verticalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
Column( Icon(
modifier = modifier imageVector = icon,
.clickable( contentDescription = label,
indication = LocalIndication.current, tint = Color(0xFF0A0A0A),
interactionSource = remember { MutableInteractionSource() } modifier = Modifier.size(24.dp)
) { onClick() } )
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally, Text(
verticalArrangement = Arrangement.spacedBy(4.dp) text = label,
) { fontSize = 12.sp,
Icon( fontWeight = FontWeight.Medium,
imageVector = icon, color = Color(0xFF0A0A0A),
contentDescription = label, lineHeight = 16.sp,
tint = Color(0xFF0A0A0A), textAlign = TextAlign.Center
modifier = Modifier.size(24.dp) )
) }
Text(
text = label,
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A),
lineHeight = 16.sp,
textAlign = TextAlign.Center
)
}
} }