temp #1

Merged
ankit merged 2 commits from temp into dev 2025-12-21 09:59:25 +00:00
18 changed files with 2423 additions and 2118 deletions

View File

@ -1 +0,0 @@
LivingAi_Lg

View File

@ -27,6 +27,7 @@ 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) {
@ -64,7 +65,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("http://10.0.2.2:3000/auth/refresh") { val response: RefreshResponse = client.post("${route}auth/refresh") {
markAsRefreshTokenRequest() markAsRefreshTokenRequest()
contentType(ContentType.Application.Json) contentType(ContentType.Application.Json)
setBody(RefreshRequest(refreshToken)) setBody(RefreshRequest(refreshToken))
@ -84,7 +85,7 @@ class AuthApiClient(private val context: Context) {
} }
defaultRequest { defaultRequest {
url("http://10.0.2.2:3000/") url(route)
} }
} }

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

View File

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

View File

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

View File

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

View File

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

@ -0,0 +1,42 @@
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

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

View File

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

View File

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