moved the UI repo

integrated existing logic with new screens
This commit is contained in:
ankitsaraf 2025-12-16 20:12:51 +05:30
parent 1def19003b
commit 4f8ae88820
93 changed files with 8632 additions and 125 deletions

1
.idea/.name Normal file
View File

@ -0,0 +1 @@
LivingAi_Lg

View File

@ -72,4 +72,19 @@ dependencies {
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
//UI
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.09.00"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
implementation("io.coil-kt:coil-compose:2.6.0")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material:material-icons-extended")
implementation("androidx.media3:media3-exoplayer:1.3.1")
implementation("androidx.media3:media3-ui:1.3.1")
}

View File

@ -25,6 +25,15 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>

View File

@ -23,30 +23,32 @@ import com.example.livingai_lg.ui.AuthState
import com.example.livingai_lg.ui.MainViewModel
import com.example.livingai_lg.ui.MainViewModelFactory
import com.example.livingai_lg.ui.login.*
import com.example.livingai_lg.ui.theme.LivingAi_LgTheme
import com.example.livingai_lg.ui.navigation.AppNavigation
import com.example.livingai_lg.ui.theme.FarmMarketplaceTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
LivingAi_LgTheme {
FarmMarketplaceTheme {
val mainViewModel: MainViewModel = viewModel(factory = MainViewModelFactory(LocalContext.current))
val authState by mainViewModel.authState.collectAsState()
when (authState) {
is AuthState.Unknown -> {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CircularProgressIndicator()
}
}
is AuthState.Authenticated -> {
SuccessScreen(mainViewModel)
}
is AuthState.Unauthenticated -> {
AuthNavigation()
}
}
AppNavigation(authState)
// when (authState) {
// is AuthState.Unknown -> {
// Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
// CircularProgressIndicator()
// }
// }
// is AuthState.Authenticated -> {
// SuccessScreen(mainViewModel)
// }
// is AuthState.Unauthenticated -> {
// AuthNavigation()
// }
// }
}
}
}

View File

@ -0,0 +1,42 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun AdSpaceBanner(
modifier: Modifier = Modifier,
text: String = "AD SPACE"
) {
Box(
modifier = modifier
.fillMaxWidth()
.height(44.dp)
.border(
width = 1.078.dp,
color = Color(0xFFFF0000).copy(alpha = 0.97f),
shape = RoundedCornerShape(8.dp)
)
.background(Color.White, RoundedCornerShape(8.dp)),
contentAlignment = Alignment.Center
) {
Text(
text = text,
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A)
)
}
}

View File

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

View File

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

View File

@ -0,0 +1,225 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex
import com.example.livingai_lg.ui.screens.*
import com.example.livingai_lg.ui.models.Animal
import com.example.livingai_lg.ui.utils.formatDistance
import com.example.livingai_lg.ui.utils.formatPrice
import com.example.livingai_lg.ui.utils.formatViews
import com.example.livingai_lg.R
@Composable
fun BuyAnimalCard(
product: Animal,
isSaved: Boolean,
onSavedChange: (Boolean) -> Unit,
onProductClick: () -> Unit,
onSellerClick:(sellerId: String)-> Unit,
) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp)
.shadow(1.078.dp, RoundedCornerShape(14.dp))
.background(Color.White, RoundedCornerShape(14.dp))
.border(
1.078.dp,
Color(0xFF000000).copy(alpha = 0.1f),
RoundedCornerShape(14.dp)
)
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() },
onClick = onProductClick
)
) {
Column {
// Image
Box(
modifier = Modifier
.fillMaxWidth()
.height(257.dp)
) {
ImageCarousel(
imageUrls = product.imageUrl ?: emptyList(),
modifier = Modifier.fillMaxSize()
)
// Views
Row(
modifier = Modifier
.align(Alignment.TopStart)
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
painter = painterResource(R.drawable.ic_view),
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(16.dp)
)
Spacer(Modifier.width(4.dp))
Text(formatViews(product.views), fontSize = 10.sp, color = Color.White)
}
// Distance
Row(
modifier = Modifier
.align(Alignment.BottomStart)
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
painter = painterResource(R.drawable.ic_location),
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(12.dp)
)
Spacer(Modifier.width(4.dp))
Text(formatDistance( product.distance), fontSize = 16.sp, color = Color.White)
}
}
// Content
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Column {
Text(
product.name?: "",
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
Text(
formatPrice(product.price),
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
}
Column(horizontalAlignment = Alignment.End,modifier = Modifier.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) {
onSellerClick(product.sellerId?:"")
}) {
Text("Sold By: ${product.sellerName}", fontSize = 13.sp)
Text(product.sellerType?: "???", fontSize = 13.sp)
}
}
// Rating
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
painter = painterResource(R.drawable.ic_star),
contentDescription = null,
tint = Color(0xFFDE9A07),
modifier = Modifier.size(8.dp)
)
Spacer(Modifier.width(4.dp))
Text(
"${product.rating} (${product.ratingCount} Ratings)",
fontSize = 8.sp
)
}
// Badges
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
Box(
modifier = Modifier
.background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp))
.padding(horizontal = 8.dp, vertical = 6.dp)
) {
val scoreString = "AI Score: ${product.aiScore?: 0}"
Text(scoreString, fontSize = 12.sp)
}
Box(
modifier = Modifier
.background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp))
.padding(horizontal = 8.dp, vertical = 6.dp)
) {
Text("placeholder", fontSize = 12.sp)
}
if(product.milkCapacity != null) {
Box(
modifier = Modifier
.background(Color(0xFFF5F5F5), RoundedCornerShape(8.dp))
.padding(horizontal = 8.dp, vertical = 6.dp)
) {
Text("Milk Capacity: ${product.milkCapacity}L", fontSize = 12.sp)
}
}
}
// Description
Text(
product.description?: "",
fontSize = 14.sp,
color = Color(0xFF717182),
lineHeight = 20.sp
)
// Actions
// Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
// ActionButton(R.drawable.ic_chat, "Chat")
// ActionButton(R.drawable.ic_phone, "Call")
// ActionButton(R.drawable.ic_location, "Location")
// ActionButton(R.drawable.ic_bookmark_plus, "Bookmark")
// }
FloatingActionBar(
modifier = Modifier
.padding(bottom = 12.dp)
.zIndex(10f), // 👈 ensure it floats above everything
onChatClick = { /* TODO */ },
onCallClick = { /* TODO */ },
onLocationClick = { /* TODO */ },
onBookmarkClick = { /* TODO */ }
)
}
}
}
}
@Composable
fun ActionButton(icon: Int, label: String) {
Box(
modifier = Modifier
.size(48.dp)
.background(Color(0xFFF5F5F5), RoundedCornerShape(24.dp)),
contentAlignment = Alignment.Center
) {
Icon(
painter = painterResource(icon),
contentDescription = label,
tint = Color(0xFF0A0A0A),
modifier = Modifier.size(20.dp)
)
}
}

View File

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

View File

@ -0,0 +1,40 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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
import com.example.livingai_lg.ui.theme.FarmTextDark
import com.example.livingai_lg.ui.theme.FarmTextNormal
@Composable
fun FarmHeader() {
Row {
Text(
text = "Farm",
fontSize = 32.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFFE17100)
)
Text(
text = "Market",
fontSize = 32.sp,
fontWeight = FontWeight.Medium,
color = FarmTextDark
)
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Find your perfect livestock",
fontSize = 16.sp,
color = FarmTextNormal
)
}

View File

@ -0,0 +1,58 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.R
@Composable
fun FilterButton(
onFilterClick: () -> Unit
) {
Row(
modifier = Modifier
.height(36.dp)
.border(1.078.dp, Color(0xFF000000).copy(alpha = 0.1f), RoundedCornerShape(8.dp))
.background(Color.White, RoundedCornerShape(8.dp))
.padding(horizontal = 8.dp)
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() },
onClick = onFilterClick,
),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Icon(
painter = painterResource(R.drawable.ic_filter),
contentDescription = "Filter",
tint = Color(0xFF0A0A0A),
modifier = Modifier.size(16.dp)
)
Text(
text = "Filter",
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A)
)
}
}

View File

@ -0,0 +1,77 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.example.livingai_lg.R
@Composable
fun FloatingActionBar(
modifier: Modifier = Modifier,
onChatClick: () -> Unit = {},
onCallClick: () -> Unit = {},
onLocationClick: () -> Unit = {},
onBookmarkClick: () -> Unit = {}
) {
Box(
modifier = modifier
.fillMaxWidth()
//.padding(horizontal = 24.dp)
.shadow(
elevation = 12.dp,
shape = RoundedCornerShape(50), // pill shape
clip = false
)
.background(
color = Color.White,
shape = RoundedCornerShape(50)
)
.padding(vertical = 12.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically
) {
FloatingActionIcon(R.drawable.ic_chat, onChatClick)
FloatingActionIcon(R.drawable.ic_phone, onCallClick)
FloatingActionIcon(R.drawable.ic_location, onLocationClick)
FloatingActionIcon(R.drawable.ic_bookmark_plus, onBookmarkClick)
}
}
}
@Composable
private fun FloatingActionIcon(
iconRes: Int,
onClick: () -> Unit
) {
Box(
modifier = Modifier
.size(48.dp)
.shadow(
elevation = 6.dp,
shape = RoundedCornerShape(24.dp),
clip = false
)
.background(Color.White, RoundedCornerShape(24.dp)),
contentAlignment = Alignment.Center
) {
Icon(
painter = painterResource(iconRes),
contentDescription = null,
tint = Color(0xFF0A0A0A),
modifier = Modifier.size(22.dp)
)
}
}

View File

@ -0,0 +1,33 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@Composable
fun IconCircle(
backgroundColor: Color,
size: Dp,
content: @Composable () -> Unit
) {
Box(
modifier = Modifier
.size(size)
.shadow(
elevation = 4.dp,
shape = CircleShape
)
.background(backgroundColor, shape = CircleShape),
contentAlignment = Alignment.Center
) {
content()
}
}

View File

@ -0,0 +1,146 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.sp
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ImageCarousel(
imageUrls: List<String>,
modifier: Modifier = Modifier
) {
when {
imageUrls.isEmpty() -> {
Box(
modifier = modifier
.background(Color.LightGray),
contentAlignment = Alignment.Center
) {
Text("No images", color = Color.White)
}
}
imageUrls.size == 1 -> {
AsyncImage(
model = imageUrls.first(),
contentDescription = null,
modifier = modifier,
contentScale = ContentScale.Crop
)
}
else -> {
val pagerState = rememberPagerState { imageUrls.size }
Box(modifier = modifier) {
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxSize()
) { page ->
AsyncImage(
model = imageUrls[page],
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
}
// Page Indicator (inside image)
Row(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 8.dp),
horizontalArrangement = Arrangement.spacedBy(6.dp)
) {
repeat(imageUrls.size) { index ->
val isSelected = pagerState.currentPage == index
Box(
modifier = Modifier
.height(6.dp)
.width(if (isSelected) 18.dp else 6.dp)
.background(
Color.White,
RoundedCornerShape(50)
)
)
}
}
}
}
}
}
@Composable
fun PageIndicator(
pageCount: Int,
currentPage: Int,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier
.background(
color = Color.Black.copy(alpha = 0.3f),
shape = RoundedCornerShape(12.dp)
)
.padding(horizontal = 8.dp, vertical = 6.dp),
horizontalArrangement = Arrangement.spacedBy(6.dp),
verticalAlignment = Alignment.CenterVertically
) {
repeat(pageCount) { index ->
if (index == currentPage) {
Box(
modifier = Modifier
.width(18.dp)
.height(6.dp)
.background(Color.White, RoundedCornerShape(3.dp))
)
} else {
Box(
modifier = Modifier
.size(6.dp)
.background(Color.White.copy(alpha = 0.6f), CircleShape)
)
}
}
}
}
@Composable
fun NoImagePlaceholder(
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
.fillMaxWidth()
.background(Color.LightGray),
contentAlignment = Alignment.Center
) {
Text(
text = "No Images",
color = Color.DarkGray,
fontSize = 14.sp,
fontWeight = FontWeight.Medium
)
}
}

View File

@ -0,0 +1,67 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.ui.theme.FarmTextDark
@Composable
fun InputField(
label: String,
value: String,
placeholder: String,
onChange: (String) -> Unit
) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(
text = label,
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = FarmTextDark
)
Box(
modifier = Modifier
.fillMaxWidth()
.height(52.dp)
.shadow(2.dp, RoundedCornerShape(16.dp))
.background(Color.White)
.border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(16.dp))
.padding(16.dp),
contentAlignment = Alignment.CenterStart
) {
BasicTextField(
value = value,
onValueChange = onChange,
textStyle = TextStyle(
fontSize = 16.sp,
color = FarmTextDark
),
singleLine = true,
decorationBox = { inner ->
if (value.isEmpty()) {
Text(
text = placeholder,
fontSize = 16.sp,
color = Color(0xFF99A1AF)
)
}
inner()
}
)
}
}
}

View File

@ -0,0 +1,280 @@
package com.example.livingai_lg.ui.components
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.outlined.CameraAlt
import androidx.compose.material.icons.outlined.Videocam
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.example.livingai_lg.ui.models.MediaType
import com.example.livingai_lg.ui.models.MediaUpload
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import com.example.livingai_lg.ui.utils.createMediaUri
import com.example.livingai_lg.ui.utils.getVideoThumbnail
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun MediaPickerCard(
upload: MediaUpload,
modifier: Modifier = Modifier,
onUriSelected: (Uri?) -> Unit
) {
val context = LocalContext.current
var showSheet by remember { mutableStateOf(false) }
var cameraUri by remember { mutableStateOf<Uri?>(null) }
var showVideoPlayer by remember { mutableStateOf(false) }
// Gallery
val galleryLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.GetContent()
) { uri ->
if (uri != null) onUriSelected(uri)
}
// Camera (photo)
val imageCameraLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.TakePicture()
) { success ->
if (success) onUriSelected(cameraUri) else cameraUri = null
}
// Camera (video)
val videoCameraLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.CaptureVideo()
) { success ->
if (success) onUriSelected(cameraUri) else cameraUri = null
}
/* ---------- Picker Sheet (NO delete here anymore) ---------- */
if (showSheet) {
ModalBottomSheet(onDismissRequest = { showSheet = false }) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
text = "Select Media",
style = MaterialTheme.typography.titleMedium
)
ListItem(
headlineContent = { Text("Camera") },
leadingContent = { Icon(Icons.Outlined.CameraAlt, null) },
modifier = Modifier.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) {
showSheet = false
cameraUri = createMediaUri(context, upload.type)
if (upload.type == MediaType.PHOTO) {
imageCameraLauncher.launch(cameraUri!!)
} else {
videoCameraLauncher.launch(cameraUri!!)
}
}
)
ListItem(
headlineContent = { Text("Gallery") },
leadingContent = {
Icon(
if (upload.type == MediaType.PHOTO)
Icons.Outlined.CameraAlt
else
Icons.Outlined.Videocam,
null
)
},
modifier = Modifier.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) {
showSheet = false
galleryLauncher.launch(
if (upload.type == MediaType.PHOTO) "image/*" else "video/*"
)
}
)
}
}
}
/* ---------- Card ---------- */
Box(
modifier = modifier
.height(204.dp)
.shadow(0.5.dp, RoundedCornerShape(8.dp))
.background(Color.White, RoundedCornerShape(8.dp))
.border(1.dp, Color(0x1A000000), RoundedCornerShape(8.dp))
.combinedClickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() },
onClick = {
if (upload.uri != null && upload.type == MediaType.VIDEO) {
showVideoPlayer = true
} else {
showSheet = true
}
},
onLongClick = {
if (upload.uri != null) {
showSheet = true
}
}
)
.padding(8.dp),
contentAlignment = Alignment.Center
) {
/* ---------- Media Preview ---------- */
if (upload.uri != null) {
Box(modifier = Modifier.fillMaxSize()) {
if (upload.type == MediaType.VIDEO) {
val thumbnail = remember(upload.uri) {
getVideoThumbnail(context, upload.uri!!)
}
if (thumbnail != null) {
Image(
bitmap = thumbnail.asImageBitmap(),
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
}
// ▶ Play overlay
Box(
modifier = Modifier
.align(Alignment.Center)
.size(56.dp)
.background(
Color.Black.copy(alpha = 0.6f),
RoundedCornerShape(28.dp)
),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = Icons.Default.PlayArrow,
contentDescription = "Play Video",
tint = Color.White,
modifier = Modifier.size(32.dp)
)
}
} else {
// Photo
AsyncImage(
model = upload.uri,
contentDescription = upload.label,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
}
/* ---------- ❌ Remove Button ---------- */
Icon(
imageVector = Icons.Default.Close,
contentDescription = "Remove media",
tint = Color.White,
modifier = Modifier
.align(Alignment.TopEnd)
.padding(6.dp)
.size(24.dp)
.background(
Color.Black.copy(alpha = 0.6f),
RoundedCornerShape(12.dp)
)
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) {
onUriSelected(null)
}
)
}
} else {
/* ---------- Empty State ---------- */
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Icon(
imageVector = if (upload.type == MediaType.PHOTO)
Icons.Outlined.CameraAlt
else
Icons.Outlined.Videocam,
contentDescription = null,
tint = Color(0xFF717182),
modifier = Modifier.size(32.dp)
)
Text(
text = if (upload.type == MediaType.PHOTO) "Add Photo" else "Add Video",
color = Color(0xFF717182)
)
if (upload.label.isNotEmpty()) {
Text(upload.label, color = Color(0xFF717182))
}
}
}
/* ---------- Video Player ---------- */
if (showVideoPlayer && upload.uri != null) {
VideoPlayerDialog(
uri = upload.uri!!,
onDismiss = { showVideoPlayer = false }
)
}
}
}

View File

@ -0,0 +1,81 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun OptionCard(
label: String,
icon: Int,
iconBackgroundColor: Color,
onClick: () -> Unit
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(90.dp)
.shadow(2.dp, RoundedCornerShape(16.dp))
.background(Color.White, RoundedCornerShape(16.dp))
.border(1.dp, Color(0xFFF3F4F6), RoundedCornerShape(16.dp))
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() },
onClick = onClick
)
.padding(12.dp)
) {
Row(
modifier = Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
// Icon container
Box(
modifier = Modifier
.size(56.dp)
.background(iconBackgroundColor, RoundedCornerShape(14.dp)),
contentAlignment = Alignment.Center
) {
Icon(
painter = painterResource(id = icon),
contentDescription = label,
tint = Color.Unspecified
)
}
// Label
Text(
text = label,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A),
modifier = Modifier.weight(1f)
)
// Dot indicator
Box(
modifier = Modifier
.size(10.dp)
.background(Color(0xFF6B7280), CircleShape)
)
}
}
}

View File

@ -0,0 +1,108 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.Text
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Phone
import com.example.livingai_lg.ui.theme.FarmTextDark
@Composable
fun PhoneNumberInput(
phone: String,
onChange: (String) -> Unit
) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(
text = "Enter Phone Number*",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = FarmTextDark
)
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.height(52.dp)
) {
// Country code box
Box(
modifier = Modifier
.width(65.dp)
.height(52.dp)
.shadow(2.dp, RoundedCornerShape(16.dp))
.background(Color.White)
.border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(16.dp)),
contentAlignment = Alignment.Center
) {
Text("+91", fontSize = 16.sp, color = FarmTextDark)
}
// Phone input field
Box(
modifier = Modifier
.weight(1f)
.height(52.dp)
.shadow(2.dp, RoundedCornerShape(16.dp))
.background(Color.White)
.border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(16.dp))
.padding(12.dp),
contentAlignment = Alignment.CenterStart
) {
Row(
Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Icon(
imageVector = Icons.Outlined.Phone,
contentDescription = "Phone",
tint = Color(0xFF99A1AF),
modifier = Modifier.size(18.dp)
)
BasicTextField(
value = phone,
onValueChange = onChange,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number
),
textStyle = TextStyle(
fontSize = 15.sp,
color = FarmTextDark
),
singleLine = true,
decorationBox = { inner ->
if (phone.isEmpty()) {
Text(
"Enter your Phone Number",
fontSize = 15.sp,
color = Color(0xFF99A1AF)
)
}
inner()
}
)
}
}
}
}
}

View File

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

View File

@ -0,0 +1,56 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.R
@Composable
fun SortButton(onSortClick: () -> Unit) {
Row(
modifier = Modifier
.height(36.dp)
.border(1.078.dp, Color(0xFF000000).copy(alpha = 0.1f), RoundedCornerShape(8.dp))
.background(Color.White, RoundedCornerShape(8.dp))
.padding(horizontal = 8.dp)
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() },
onClick = onSortClick,
),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Icon(
painter = painterResource(R.drawable.ic_sort),
contentDescription = "Sort",
tint = Color(0xFF0A0A0A),
modifier = Modifier.size(16.dp)
)
Text(
text = "Sort by",
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A)
)
}
}

View File

@ -0,0 +1,103 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDownward
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowUpward
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.ui.models.SortDirection
import com.example.livingai_lg.ui.models.SortField
@Composable
fun SortItem(
field: SortField,
onToggle: () -> Unit,
modifier: Modifier = Modifier
) {
val icon = when (field.direction) {
SortDirection.ASC -> Icons.Default.ArrowUpward
SortDirection.DESC -> Icons.Default.ArrowDownward
SortDirection.NONE -> Icons.Default.ArrowDropDown
}
Box(
modifier = modifier
.fillMaxWidth()
.height(56.dp)
.background(Color.White, RoundedCornerShape(12.dp))
.border(1.dp, Color(0x1A000000), RoundedCornerShape(12.dp))
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) { onToggle() }
.padding(horizontal = 16.dp),
contentAlignment = Alignment.Center
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = field.label,
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF364153)
)
// Sort priority indicator
field.order?.let {
Spacer(modifier = Modifier.width(8.dp))
Box(
modifier = Modifier
.size(18.dp)
.background(Color.Black, RoundedCornerShape(9.dp)),
contentAlignment = Alignment.Center
) {
Text(
text = it.toString(),
color = Color.White,
fontSize = 11.sp,
fontWeight = FontWeight.Bold
)
}
}
}
Icon(
imageVector = icon,
contentDescription = null,
tint = if (field.direction == SortDirection.NONE)
Color(0xFF9CA3AF)
else
Color.Black
)
}
}
}

View File

@ -0,0 +1,153 @@
package com.example.livingai_lg.ui.components
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material3.Divider
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage
import com.example.livingai_lg.ui.models.UserAddress
import com.example.livingai_lg.ui.models.UserProfile
import com.example.livingai_lg.R
@Composable
fun UserLocationHeader(
user: UserProfile,
modifier: Modifier = Modifier,
onAddressSelected: (UserAddress) -> Unit = {},
onAddNewClick: () -> Unit = {} // future navigation hook
) {
var expanded by remember { mutableStateOf(false) }
var selectedAddress by remember {
mutableStateOf(
user.addresses.firstOrNull { it.isPrimary }
?: user.addresses.first()
)
}
Row(
modifier = modifier.wrapContentWidth(),
verticalAlignment = Alignment.CenterVertically
) {
// Profile image
Box(
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
.background(Color.Black),
contentAlignment = Alignment.Center
) {
if (user.profileImageUrl != null) {
AsyncImage(
model = user.profileImageUrl,
contentDescription = null,
modifier = Modifier.fillMaxSize()
)
} else {
Icon(
painter = painterResource(R.drawable.ic_profile),
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(26.dp)
)
}
}
Spacer(modifier = Modifier.width(12.dp))
// Anchor ONLY the text section
Box {
Column(
modifier = Modifier
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) { expanded = true }
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = selectedAddress.name,
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
color = Color.Black
)
Icon(
imageVector = Icons.Default.KeyboardArrowDown,
contentDescription = null,
tint = Color.Black,
modifier = Modifier.size(18.dp)
)
}
Text(
text = selectedAddress.address,
fontSize = 13.sp,
color = Color.Black.copy(alpha = 0.7f),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
// Dropdown appears BELOW name/address
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
user.addresses.forEach { address ->
DropdownMenuItem(
text = {
Text(
text = address.name,
fontWeight = if (address == selectedAddress)
FontWeight.SemiBold
else FontWeight.Normal
)
},
onClick = {
selectedAddress = address
expanded = false
onAddressSelected(address)
}
)
}
Divider()
// Add New option
DropdownMenuItem(
text = {
Text(
text = "Add New +",
fontWeight = FontWeight.Medium,
color = Color(0xFF007BFF)
)
},
onClick = {
expanded = false
onAddNewClick()
}
)
}
}
}
}

View File

@ -0,0 +1,60 @@
package com.example.livingai_lg.ui.components
import android.net.Uri
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
@Composable
fun VideoPlayerDialog(
uri: Uri,
onDismiss: () -> Unit
) {
val context = LocalContext.current
val player = remember {
ExoPlayer.Builder(context).build().apply {
setMediaItem(MediaItem.fromUri(uri))
prepare()
playWhenReady = true
}
}
DisposableEffect(Unit) {
onDispose {
player.release()
}
}
Dialog(onDismissRequest = onDismiss) {
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(16 / 9f)
.background(Color.Black)
) {
AndroidView(
factory = {
PlayerView(it).apply {
this.player = player
useController = true
}
},
modifier = Modifier.fillMaxSize()
)
}
}
}

View File

@ -0,0 +1,106 @@
package com.example.livingai_lg.ui.components.backgrounds
import android.annotation.SuppressLint
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.TextUnit
import kotlin.math.min
import com.example.livingai_lg.R
@Composable
fun DecorativeBackground() {
BoxWithConstraints(
modifier = Modifier.fillMaxSize()
) {
val screenW = maxWidth.value
val screenH = maxHeight.value
// Original bg image design size (YOUR assets intended size)
val designW = 393f
val designH = 852f
// Scale factor that preserves aspect ratio
val scale = min(screenW / designW, screenH / designH)
//------------------------------
// Helper to scale dp offsets
//------------------------------
fun s(value: Float) = (value * scale).dp
//------------------------------
// Background Image
//------------------------------
Image(
painter = painterResource(R.drawable.bg),
contentDescription = null,
modifier = Modifier
.fillMaxSize(),
contentScale = ContentScale.Crop // ensures full-screen coverage
)
//------------------------------
// Decorative Elements (scaled)
//------------------------------
// 🐐 Goat
ScaledEmoji(
emoji = "🐐",
baseFontSize = 48.sp,
offsetX = 250f,
offsetY = 160f,
scale = scale,
alpha = 0.10f
)
// 🐄 Cow
ScaledEmoji(
emoji = "🐄",
baseFontSize = 60.sp,
offsetX = 64f,
offsetY = 569f,
scale = scale,
alpha = 0.12f
)
// 🌾 Wheat
ScaledEmoji(
emoji = "🌾🌾🌾",
baseFontSize = 32.sp,
offsetX = 48f,
offsetY = 730f,
scale = scale,
alpha = 0.15f
)
}
}
@Composable
private fun ScaledEmoji(
emoji: String,
baseFontSize: TextUnit,
offsetX: Float,
offsetY: Float,
scale: Float,
alpha: Float
) {
Text(
text = emoji,
fontSize = (baseFontSize.value * scale).sp,
modifier = Modifier
.offset(
x = (offsetX * scale).dp,
y = (offsetY * scale).dp
)
.alpha(alpha)
)
}

View File

@ -0,0 +1,50 @@
package com.example.livingai_lg.ui.components.backgrounds
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import kotlin.math.min
import com.example.livingai_lg.R
@Composable
fun StoreBackground() {
BoxWithConstraints(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF7F4EE))
) {
val screenW = maxWidth.value
val screenH = maxHeight.value
// Original bg image design size (YOUR assets intended size)
val designW = 393f
val designH = 852f
// Scale factor that preserves aspect ratio
val scale = min(screenW / designW, screenH / designH)
//------------------------------
// Helper to scale dp offsets
//------------------------------
fun s(value: Float) = (value * scale).dp
//------------------------------
// Background Image
//------------------------------
Image(
painter = painterResource(R.drawable.bg_shop),
contentDescription = null,
modifier = Modifier
.fillMaxSize(),
contentScale = ContentScale.Crop // ensures full-screen coverage
)
}
}

View File

@ -0,0 +1,38 @@
package com.example.livingai_lg.ui.layout
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.zIndex
import com.example.livingai_lg.ui.components.BottomNavigationBar
import com.example.livingai_lg.ui.models.BottomNavItemData
@Composable
fun BottomNavScaffold(
modifier: Modifier = Modifier,
items: List<BottomNavItemData>,
currentItem: String,
onBottomNavItemClick: (route: String) -> Unit = {},
content: @Composable (paddingValues: PaddingValues) -> Unit
) {
Scaffold(
modifier = modifier,
bottomBar = {
BottomNavigationBar(
modifier = Modifier
.zIndex(1f)
.shadow(8.dp),
items,
currentItem,
onItemClick = onBottomNavItemClick
)
},
containerColor = Color.Transparent
) { paddingValues ->
content(paddingValues)
}
}

View File

@ -52,11 +52,7 @@ fun CreateProfileScreen(navController: NavController, name: String) {
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.linearGradient(
colors = listOf(LightCream, LighterCream, LightestGreen)
)
)
) {
Column(
modifier = Modifier
@ -105,7 +101,7 @@ fun ProfileTypeItem(text: String, icon: Int, onClick: () -> Unit) {
@Preview(showBackground = true)
@Composable
fun CreateProfileScreenPreview() {
LivingAi_LgTheme {
FarmMarketplaceTheme {
CreateProfileScreen(rememberNavController(), "John Doe")
}
}

View File

@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
@ -29,11 +30,11 @@ fun LoginScreen(navController: NavController) {
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.linearGradient(
colors = listOf(LightCream, LighterCream, LightestGreen)
)
)
// .background(
// brush = Brush.linearGradient(
// colors = listOf(LightCream, LighterCream, LightestGreen)
// )
// )
) {
// Decorative elements...
@ -55,7 +56,7 @@ fun LoginScreen(navController: NavController) {
Box(
modifier = Modifier
.size(56.dp)
.background(Gold, RoundedCornerShape(28.dp)),
.background(Color.Yellow, RoundedCornerShape(28.dp)),
contentAlignment = Alignment.Center
) {
Text(text = "🌾", fontSize = 24.sp)
@ -64,7 +65,7 @@ fun LoginScreen(navController: NavController) {
Box(
modifier = Modifier
.size(72.dp)
.background(LightOrange.copy(alpha = 0.72f), RoundedCornerShape(36.dp)),
.background(Color.Yellow.copy(alpha = 0.72f), RoundedCornerShape(36.dp)),
contentAlignment = Alignment.Center
) {
Text(text = "🌱", fontSize = 32.sp)
@ -73,7 +74,7 @@ fun LoginScreen(navController: NavController) {
Box(
modifier = Modifier
.size(56.dp)
.background(DarkOrange.copy(alpha = 0.67f), RoundedCornerShape(28.dp)),
.background(Color.Yellow.copy(alpha = 0.67f), RoundedCornerShape(28.dp)),
contentAlignment = Alignment.Center
) {
Text(text = "☀️", fontSize = 24.sp)
@ -86,13 +87,13 @@ fun LoginScreen(navController: NavController) {
text = "Welcome!",
fontSize = 24.sp,
fontWeight = FontWeight.Medium,
color = DarkBrown
color = Color.Yellow
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Join the farm marketplace community",
fontSize = 16.sp,
color = MidBrown,
color = Color.Yellow,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(48.dp))
@ -101,12 +102,12 @@ fun LoginScreen(navController: NavController) {
Button(
onClick = { navController.navigate("signup") },
shape = RoundedCornerShape(16.dp),
colors = ButtonDefaults.buttonColors(containerColor = LightOrange),
colors = ButtonDefaults.buttonColors(containerColor = Color.Yellow),
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
) {
Text(text = "New user? Sign up", color = DarkerBrown, fontSize = 16.sp, fontWeight = FontWeight.Medium)
Text(text = "New user? Sign up", color = Color.Yellow, fontSize = 16.sp, fontWeight = FontWeight.Medium)
}
Spacer(modifier = Modifier.height(16.dp))
@ -115,12 +116,12 @@ fun LoginScreen(navController: NavController) {
Button(
onClick = { navController.navigate("signin") },
shape = RoundedCornerShape(16.dp),
colors = ButtonDefaults.buttonColors(containerColor = TerraCotta),
colors = ButtonDefaults.buttonColors(containerColor = Color.Yellow),
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
) {
Text(text = "Already a user? Sign in", color = DarkerBrown, fontSize = 16.sp, fontWeight = FontWeight.Medium)
Text(text = "Already a user? Sign in", color = Color.Yellow, fontSize = 16.sp, fontWeight = FontWeight.Medium)
}
Spacer(modifier = Modifier.height(24.dp))
@ -128,7 +129,7 @@ fun LoginScreen(navController: NavController) {
// Guest Button
Text(
text = "Continue as Guest",
color = MidBrown,
color = Color.Yellow,
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.clickable { Toast.makeText(context, "Guest mode is not yet available", Toast.LENGTH_SHORT).show() }
@ -142,7 +143,7 @@ fun LoginScreen(navController: NavController) {
@Preview(showBackground = true)
@Composable
fun LoginScreenPreview() {
LivingAi_LgTheme {
FarmMarketplaceTheme() {
LoginScreen(rememberNavController())
}
}

View File

@ -40,11 +40,11 @@ fun OtpScreen(navController: NavController, phoneNumber: String, name: String) {
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.linearGradient(
colors = listOf(LightCream, LighterCream, LightestGreen)
)
)
// .background(
// brush = Brush.linearGradient(
// colors = listOf(LightCream, LighterCream, LightestGreen)
// )
// )
) {
Column(
modifier = Modifier
@ -60,7 +60,7 @@ fun OtpScreen(navController: NavController, phoneNumber: String, name: String) {
TextField(
value = otp.value,
onValueChange = { if (it.length <= 6) otp.value = it },
onValueChange = { if (it.length <= 6) otp.value = it },
modifier = Modifier
.fillMaxWidth()
.height(60.dp)
@ -117,7 +117,7 @@ fun OtpScreen(navController: NavController, phoneNumber: String, name: String) {
@Preview(showBackground = true)
@Composable
fun OtpScreenPreview() {
LivingAi_LgTheme {
FarmMarketplaceTheme() {
OtpScreen(rememberNavController(), "+919876543210", "John Doe")
}
}

View File

@ -42,11 +42,11 @@ fun SignInScreen(navController: NavController) {
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.linearGradient(
colors = listOf(LightCream, LighterCream, LightestGreen)
)
)
// .background(
// brush = Brush.linearGradient(
// colors = listOf(LightCream, LighterCream, LightestGreen)
// )
// )
) {
Column(
modifier = Modifier
@ -161,7 +161,7 @@ fun SignInScreen(navController: NavController) {
@Preview(showBackground = true)
@Composable
fun SignInScreenPreview() {
LivingAi_LgTheme {
FarmMarketplaceTheme() {
SignInScreen(rememberNavController())
}
}

View File

@ -45,11 +45,11 @@ fun SignUpScreen(navController: NavController) {
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.linearGradient(
colors = listOf(LightCream, LighterCream, LightestGreen)
)
)
// .background(
// brush = Brush.linearGradient(
// colors = listOf(LightCream, LighterCream, LightestGreen)
// )
// )
) {
Column(
modifier = Modifier
@ -190,7 +190,7 @@ fun SignUpScreen(navController: NavController) {
@Preview(showBackground = true)
@Composable
fun SignUpScreenPreview() {
LivingAi_LgTheme {
FarmMarketplaceTheme {
SignUpScreen(rememberNavController())
}
}

View File

@ -14,9 +14,6 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.livingai_lg.ui.MainViewModel
import com.example.livingai_lg.ui.UserState
import com.example.livingai_lg.ui.theme.LightCream
import com.example.livingai_lg.ui.theme.LighterCream
import com.example.livingai_lg.ui.theme.LightestGreen
import com.example.livingai_lg.ui.MainViewModelFactory // <-- This was the missing import
@Composable
@ -28,11 +25,12 @@ fun SuccessScreen(mainViewModel: MainViewModel = viewModel(factory = MainViewMod
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.linearGradient(
colors = listOf(LightCream, LighterCream, LightestGreen)
)
),
// .background(
// brush = Brush.linearGradient(
// colors = listOf(LightCream, LighterCream, LightestGreen)
// )
// )
,
contentAlignment = Alignment.Center
) {
when (val state = userState) {

View File

@ -0,0 +1,74 @@
package com.example.livingai_lg.ui.models
data class Animal(
val id: String,
val name: String? = null,
val age: Int? = null,
val breed: String? = null,
val price: Long? = null,
val isFairPrice: Boolean? = null,
val imageUrl: List<String>? = null,
val location: String? = null,
val displayLocation: String? = null,
val distance: Long? = null,
val views: Long? = null,
val sellerId: String? = null,
val sellerName: String? = null,
val sellerType: String? = null,
val aiScore: Float? = null,
val rating: Float? = null,
val ratingCount: Int? = null,
val description: String? = null,
val milkCapacity: Float? = null,
)
val sampleAnimals = listOf(
Animal(
id = "1",
name = "Golden Retriever",
price = 80000,
imageUrl = listOf("https://api.builder.io/api/v1/image/assets/TEMP/8b3d82a183db9ded7741b4b8bf0cd81fb2733b89?width=686"),
distance = 2500L,// miles away
views = 94,//Views
sellerId = "1",
sellerName = "Seller 1",
sellerType = "Wholeseller",
rating = 4.5f,
ratingCount = 2076,
description = "Friendly and energetic companion looking for an active family.",
),
Animal(
id = "2",
name = "Mudkip",
price = 999999999,
imageUrl = listOf("https://img.pokemondb.net/artwork/large/mudkip.jpg", "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fcdna.artstation.com%2Fp%2Fassets%2Fimages%2Fimages%2F012%2F524%2F022%2Flarge%2Fanna-ben-david-poster-size-mudkip.jpg%3F1535218851&f=1&nofb=1&ipt=aac92abbad440468461cbec5d9208d4258e5f4ed72e18a9ae17cfd6e0da1ce9f"),
distance = 0L,
views = 100000,
sellerId = "1",
sellerName = "Seller ???",
sellerType = "Wholeseller",
rating = 5f,
ratingCount = 2076,
description = "Friendly and energetic companion looking for an active family.",
),
Animal(
id = "3",
name = "Bessie",
age = 48,
breed = "Holstein Friesian",
location = "Punjab",
distance = 12000,
imageUrl = listOf("https://api.builder.io/api/v1/image/assets/TEMP/885e24e34ede6a39f708df13dabc4c1683c3e976?width=786"),
views = 94,
aiScore = 0.80f,
price = 80000,
isFairPrice = true,
rating = 4.5f,
ratingCount = 2076,
sellerId = "1",
sellerName = "Seller 1",
description = "Premium Holstein Friesian dairy cow in excellent health condition. Bessie is a high-yielding milk producer with consistent output and gentle temperament, making her ideal for commercial dairy operations or family farms.",
displayLocation = "Mediatek Pashu Mela, Marathalli, Bangalore (13 km)",
milkCapacity = 2.3f
)
)

View File

@ -0,0 +1,18 @@
package com.example.livingai_lg.ui.models
data class AnimalType(
val id: String,
val name: String,
val emoji: String
)
val animalTypes = listOf(
AnimalType("cows", "Cows", "🐄"),
AnimalType("buffalo", "Buffalo", "🐃"),
AnimalType("goat", "Goat", "🐐"),
AnimalType("bull", "Bull", "🐂"),
AnimalType("baby_cow", "Baby Cow", "🐮"),
AnimalType("dog", "Dog", "🐕"),
AnimalType("cat", "Cat", "🐱"),
AnimalType("others", "Others", "🦜")
)

View File

@ -0,0 +1,27 @@
package com.example.livingai_lg.ui.models
import com.example.livingai_lg.R
import com.example.livingai_lg.ui.navigation.AppScreen
data class BottomNavItemData(
val label: String,
val iconRes: Int,
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),
// TODO:
BottomNavItemData("Chats", R.drawable.ic_chat, AppScreen.CREATE_PROFILE),
BottomNavItemData("Services", R.drawable.ic_config, AppScreen.CREATE_PROFILE),
BottomNavItemData("Mandi", R.drawable.ic_market, AppScreen.CREATE_PROFILE)
)
val chatBottomNavItems = listOf(
BottomNavItemData("Home", R.drawable.ic_home ,"home"),
BottomNavItemData("Sell", R.drawable.ic_tag, "sell"),
BottomNavItemData("Chats", R.drawable.ic_chat, "chats"),
BottomNavItemData("Services", R.drawable.ic_config, "services"),
BottomNavItemData("Mandi", R.drawable.ic_market, "mandi")
)

View File

@ -0,0 +1,28 @@
package com.example.livingai_lg.ui.models
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
class NewListingFormState {
val name = mutableStateOf("")
val animal = mutableStateOf("")
val breed = mutableStateOf("")
val age = mutableStateOf("")
val milkYield = mutableStateOf("")
val calvingNumber = mutableStateOf("")
val reproductiveStatus = mutableStateOf("")
val description = mutableStateOf("")
val animalExpanded = mutableStateOf(false)
val breedExpanded = mutableStateOf(false)
val mediaUploads = mutableStateListOf(
MediaUpload("left-view", "Left View", MediaType.PHOTO),
MediaUpload("right-view", "Right View", MediaType.PHOTO),
MediaUpload("left-angle", "Left Angle View", MediaType.PHOTO),
MediaUpload("right-angle", "Right Angle View", MediaType.PHOTO),
MediaUpload("angle", "Angle View", MediaType.PHOTO),
MediaUpload("video", "", MediaType.VIDEO)
)
}

View File

@ -0,0 +1,38 @@
package com.example.livingai_lg.ui.models
import androidx.compose.ui.graphics.Color
import com.example.livingai_lg.R
data class ProfileType(
val id: String,
val title: String,
val icon: Int,
val backgroundColor: Color
)
val profileTypes = listOf(
ProfileType(
id = "buyer_seller",
title = "I'm a Buyer/Seller",
icon = R.drawable.ic_shop,
backgroundColor = Color(0xFF9D4EDD)
),
ProfileType(
id = "wholesale_trader",
title = "I'm a Wholesale Trader",
icon = R.drawable.ic_bag,
backgroundColor = Color(0xFF3A86FF)
),
ProfileType(
id = "service_provider",
title = "I'm a Service Provider",
icon = R.drawable.ic_spanner,
backgroundColor = Color(0xFFFF5722)
),
ProfileType(
id = "mandi_host",
title = "I'm a Mandi Host",
icon = R.drawable.ic_shop2,
backgroundColor = Color(0xFF4CAF50)
)
)

View File

@ -0,0 +1,4 @@
package com.example.livingai_lg.ui.models
class Seller {
}

View File

@ -0,0 +1,14 @@
package com.example.livingai_lg.ui.models
enum class SortDirection {
NONE,
ASC,
DESC
}
data class SortField(
val key: String,
val label: String,
val direction: SortDirection = SortDirection.NONE,
val order: Int? = null
)

View File

@ -0,0 +1,30 @@
package com.example.livingai_lg.ui.models
data class UserAddress(
val name: String,
val address: String,
val isPrimary: Boolean = false
)
data class UserProfile(
val name: String,
val profileImageUrl: String? = null,
val addresses: List<UserAddress>
)
val userProfile =
UserProfile(
name = "John Doe",
profileImageUrl = "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fstatic.vecteezy.com%2Fsystem%2Fresources%2Fpreviews%2F014%2F016%2F808%2Fnon_2x%2Findian-farmer-face-vector.jpg&f=1&nofb=1&ipt=c352fec591428aebefe6cd263d2958765e85d4da69cce3c46b725ba2ff7d3448",
addresses = listOf(
UserAddress(
name = "Home",
address = "205 1st floor 7th cross 27th main, PW",
isPrimary = true
),
UserAddress(
name = "Farm",
address = "2nd block, MG Farms"
)
)
)

View File

@ -0,0 +1,19 @@
package com.example.livingai_lg.ui.models
import android.net.Uri
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
class MediaUpload(
val id: String,
val label: String,
val type: MediaType,
initialUri: Uri? = null
) {
var uri by mutableStateOf(initialUri)
}
enum class MediaType {
PHOTO, VIDEO
}

View File

@ -0,0 +1,338 @@
package com.example.livingai_lg.ui.navigation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.example.livingai_lg.ui.AuthState
import com.example.livingai_lg.ui.screens.AnimalProfileScreen
import com.example.livingai_lg.ui.screens.BuyScreen
import com.example.livingai_lg.ui.screens.ChooseServiceScreen
import com.example.livingai_lg.ui.screens.CreateProfileScreen
import com.example.livingai_lg.ui.screens.FilterScreen
import com.example.livingai_lg.ui.screens.NewListingScreen
import com.example.livingai_lg.ui.screens.PostSaleSurveyScreen
import com.example.livingai_lg.ui.screens.SaleArchiveScreen
import com.example.livingai_lg.ui.screens.SellerProfileScreen
import com.example.livingai_lg.ui.screens.SortScreen
import com.example.livingai_lg.ui.screens.auth.LandingScreen
import com.example.livingai_lg.ui.screens.auth.OtpScreen
import com.example.livingai_lg.ui.screens.auth.SignInScreen
import com.example.livingai_lg.ui.screens.auth.SignUpScreen
object AppScreen {
const val LANDING = "landing"
const val SIGN_IN = "sign_in"
const val SIGN_UP = "sign_up"
const val OTP = "otp"
const val CHOOSE_SERVICE = "choose_service"
const val CREATE_PROFILE = "create_profile"
const val BUY_ANIMALS = "buy_animals"
const val ANIMAL_PROFILE = "animal_profile"
const val CREATE_ANIMAL_LISTING = "create_animal_listing"
const val BUY_ANIMALS_FILTERS = "buy_animals_filters"
const val BUY_ANIMALS_SORT = "buy_animals_sort"
const val SELLER_PROFILE = "seller_profile"
const val SALE_ARCHIVE = "sale_archive"
const val POST_SALE_SURVEY = "post_sale_survey"
fun otp(phone: String, name: String) =
"$OTP/$phone/$name"
fun createProfile(name: String) =
"$CREATE_PROFILE/$name"
fun chooseService(profileId: String) =
"$CHOOSE_SERVICE/$profileId"
fun postSaleSurvey(animalId: String) =
"$POST_SALE_SURVEY/$animalId"
fun animalProfile(animalId: String) =
"$ANIMAL_PROFILE/$animalId"
fun sellerProfile(sellerId: String) =
"$SELLER_PROFILE/$sellerId"
fun saleArchive(saleId: String) =
"$SALE_ARCHIVE/$saleId"
}
@Composable
fun AppNavigation(
authState: AuthState
) {
when (authState) {
is AuthState.Unauthenticated -> {AuthNavGraph()}
is AuthState.Authenticated -> {MainNavGraph()}
is AuthState.Unknown -> {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CircularProgressIndicator()
}
}
// AuthState.Loading -> SplashScreen()
}
// val navController = rememberNavController()
// val onNavClick: (String) -> Unit = { route ->
// val currentRoute =
// navController.currentBackStackEntry?.destination?.route
//
// if (currentRoute != route) {
// navController.navigate(route) {
// launchSingleTop = true
// restoreState = true
// popUpTo(navController.graph.startDestinationId) {
// saveState = true
// }
// }
// }
// }
// NavHost(
// navController = navController,
// startDestination = AppScreen.LANDING,
//
// ) {
// composable(AppScreen.LANDING) {
// LandingScreen(
// onSignUpClick = { navController.navigate(AppScreen.SIGN_UP) },
// onSignInClick = { navController.navigate(AppScreen.SIGN_IN) },
// onGuestClick = { navController.navigate(AppScreen.CREATE_PROFILE) }
// )
// }
//
// composable(AppScreen.SIGN_IN) {
// SignInScreen(
// onSignUpClick = { navController.navigate(AppScreen.SIGN_UP) },
// onSignInClick = {
// navController.navigate(AppScreen.OTP)
// }
// )
// }
//
// composable(AppScreen.SIGN_UP) {
// SignUpScreen(
// onSignUpClick = {
// navController.navigate(AppScreen.OTP)
// },
// onSignInClick = {
// navController.navigate(AppScreen.SIGN_IN) {
// popUpTo(AppScreen.SIGN_UP) { inclusive = true }
// }
// }
// )
// }
//
// composable(AppScreen.OTP) {
// OtpScreen(
// onContinue = { navController.navigate(AppScreen.CREATE_PROFILE) }
// )
// }
// composable(AppScreen.CREATE_PROFILE) {
// CreateProfileScreen (
// onProfileSelected = { profileId ->
// if (profileId == "buyer_seller") {
// navController.navigate(AppScreen.BUY_ANIMALS)
// } else {
// navController.navigate(AppScreen.CHOOSE_SERVICE)
// } },
// )
// }
//
// composable(AppScreen.CHOOSE_SERVICE) {
// ChooseServiceScreen (
// onServiceSelected = { navController.navigate(AppScreen.LANDING) },
// )
// }
//
// composable(AppScreen.BUY_ANIMALS) {
// BuyScreen(
// onBackClick = {
// navController.popBackStack()
// },
// onProductClick = { animalId ->
// navController.navigate(
// AppScreen.animalProfile(animalId)
// )
// },
// onNavClick = onNavClick,
// onFilterClick = {navController.navigate(AppScreen.BUY_ANIMALS_FILTERS)},
// onSortClick = {navController.navigate(AppScreen.BUY_ANIMALS_SORT)},
// onSellerClick = { sellerId ->
// navController.navigate(
// AppScreen.sellerProfile(sellerId)
// )
// },
// )
// }
//
// composable(AppScreen.BUY_ANIMALS_FILTERS) {
// FilterScreen(
// onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
// onBackClick = {
// navController.popBackStack()
// },
// onSkipClick = {
// navController.popBackStack()
// },
// onCancelClick = {
// navController.popBackStack()
// },
//
// )
// }
//
// composable(AppScreen.BUY_ANIMALS_SORT) {
// SortScreen(
// onApplyClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
// onBackClick = {
// navController.popBackStack()
// },
// onSkipClick = {
// navController.popBackStack()
// },
// onCancelClick = {
// navController.popBackStack()
// },
//
// )
// }
//
// composable(AppScreen.CREATE_ANIMAL_LISTING) {
// NewListingScreen (
// onSaveClick = {navController.navigate(
// AppScreen.postSaleSurvey("2")
// )},
// onBackClick = {
// navController.popBackStack()
// }
// )
// }
//
// composable(
// route = "${AppScreen.SALE_ARCHIVE}/{saleId}",
// arguments = listOf(
// navArgument("saleId") { type = NavType.StringType }
// )
// ) { backStackEntry ->
//
// val saleId = backStackEntry
// .arguments
// ?.getString("saleId")
// ?: return@composable
//
// SaleArchiveScreen(
// saleId = saleId,
// onBackClick = {
// navController.popBackStack()
// },
// onSaleSurveyClick = { saleId ->
// navController.navigate(
// AppScreen.sellerProfile(saleId)
// )
// },
// )
// }
//
// composable(
// route = "${AppScreen.POST_SALE_SURVEY}/{animalId}",
// arguments = listOf(
// navArgument("animalId") { type = NavType.StringType }
// )
// ) { backStackEntry ->
//
// val animalId = backStackEntry
// .arguments
// ?.getString("animalId")
// ?: return@composable
//
// PostSaleSurveyScreen (
// animalId = animalId,
// onBackClick = {
// navController.popBackStack()
// },
// onSubmit = {navController.navigate(
// AppScreen.saleArchive("2")
// )}
// )
// }
//
// composable(
// route = "${AppScreen.ANIMAL_PROFILE}/{animalId}",
// arguments = listOf(
// navArgument("animalId") { type = NavType.StringType }
// )
// ) { backStackEntry ->
//
// val animalId = backStackEntry
// .arguments
// ?.getString("animalId")
// ?: return@composable
//
// AnimalProfileScreen(
// animalId = animalId,
// onBackClick = {
// navController.popBackStack()
// },
// onSellerClick = { sellerId ->
// navController.navigate(
// AppScreen.sellerProfile(sellerId)
// )
// },
// )
// }
//
// composable(
// route = "${AppScreen.SELLER_PROFILE}/{sellerId}",
// arguments = listOf(
// navArgument("sellerId") { type = NavType.StringType }
// )
// ) { backStackEntry ->
//
// val sellerId = backStackEntry
// .arguments
// ?.getString("sellerId")
// ?: return@composable
//
// SellerProfileScreen(
// sellerId = sellerId,
// onBackClick = {
// navController.popBackStack()
// }
// )
// }
// composable(AppScreen.SELLER_PROFILE) {
// SellerProfileScreen (
// onBackClick = {
// navController.popBackStack()
// }
// )
// }
// }
}

View File

@ -0,0 +1,70 @@
package com.example.livingai_lg.ui.navigation
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.example.livingai_lg.ui.screens.SaleArchiveScreen
import com.example.livingai_lg.ui.screens.auth.LandingScreen
import com.example.livingai_lg.ui.screens.auth.OtpScreen
import com.example.livingai_lg.ui.screens.auth.SignInScreen
import com.example.livingai_lg.ui.screens.auth.SignUpScreen
@Composable
fun AuthNavGraph(
navController: NavHostController = rememberNavController()
) {
NavHost(
navController = navController,
startDestination = AppScreen.LANDING
) {
composable(AppScreen.LANDING) {
LandingScreen(
onSignUpClick = { navController.navigate(AppScreen.SIGN_UP) },
onSignInClick = { navController.navigate(AppScreen.SIGN_IN) },
onGuestClick = { navController.navigate(AppScreen.CREATE_PROFILE) }
)
}
composable(AppScreen.SIGN_IN) {
SignInScreen(
onSignUpClick = { navController.navigate(AppScreen.SIGN_UP){
popUpTo(AppScreen.SIGN_IN) { inclusive = true }
} },
onSignInClick = { phone, name ->
navController.navigate(AppScreen.otp(phone,name))
}
)
}
composable(AppScreen.SIGN_UP) {
SignUpScreen(
onSignUpClick = { phone, name ->
navController.navigate(AppScreen.otp(phone,name))
},
onSignInClick = {
navController.navigate(AppScreen.SIGN_IN) {
popUpTo(AppScreen.SIGN_UP) { inclusive = true }
}
}
)
}
composable(
"${AppScreen.OTP}/{phoneNumber}/{name}",
arguments = listOf(
navArgument("phoneNumber") { type = NavType.StringType },
navArgument("name") { type = NavType.StringType }
)
) { backStackEntry ->
OtpScreen(
phoneNumber = backStackEntry.arguments?.getString("phoneNumber") ?: "",
name = backStackEntry.arguments?.getString("name") ?: "",
onSuccess = { navController.navigate(AppScreen.chooseService("1"))}
)
}
}
}

View File

@ -0,0 +1,230 @@
package com.example.livingai_lg.ui.navigation
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.example.livingai_lg.ui.login.SuccessScreen
import com.example.livingai_lg.ui.screens.AnimalProfileScreen
import com.example.livingai_lg.ui.screens.BuyScreen
import com.example.livingai_lg.ui.screens.ChooseServiceScreen
import com.example.livingai_lg.ui.screens.CreateProfileScreen
import com.example.livingai_lg.ui.screens.FilterScreen
import com.example.livingai_lg.ui.screens.NewListingScreen
import com.example.livingai_lg.ui.screens.PostSaleSurveyScreen
import com.example.livingai_lg.ui.screens.SaleArchiveScreen
import com.example.livingai_lg.ui.screens.SellerProfileScreen
import com.example.livingai_lg.ui.screens.SortScreen
@Composable
fun MainNavGraph(
navController: NavHostController = rememberNavController()
) {
val onNavClick: (String) -> Unit = { route ->
val currentRoute =
navController.currentBackStackEntry?.destination?.route
if (currentRoute != route) {
navController.navigate(route) {
launchSingleTop = true
restoreState = true
popUpTo(navController.graph.startDestinationId) {
saveState = true
}
}
}
}
NavHost(
navController = navController,
startDestination = AppScreen.BUY_ANIMALS
) {
composable(
"${AppScreen.CREATE_PROFILE}/{name}",
arguments = listOf(navArgument("name") { type = NavType.StringType })
) { backStackEntry ->
CreateProfileScreen(
name = backStackEntry.arguments?.getString("name") ?: "",
onProfileSelected = { profileId ->
navController.navigate(AppScreen.chooseService(profileId))
}
)
}
composable(AppScreen.CHOOSE_SERVICE) {
ChooseServiceScreen (
onServiceSelected = { navController.navigate(AppScreen.LANDING) },
)
}
composable(
"${AppScreen.CHOOSE_SERVICE}/{profileId}",
arguments = listOf(navArgument("profileId") { type = NavType.StringType })
) { backStackEntry ->
ChooseServiceScreen (
profileId = backStackEntry.arguments?.getString("profileId") ?: "",
onServiceSelected = { navController.navigate(AppScreen.BUY_ANIMALS) },
)
}
composable(AppScreen.BUY_ANIMALS) {
BuyScreen(
onBackClick = {
navController.popBackStack()
},
onProductClick = { animalId ->
navController.navigate(
AppScreen.animalProfile(animalId)
)
},
onNavClick = onNavClick,
onFilterClick = {navController.navigate(AppScreen.BUY_ANIMALS_FILTERS)},
onSortClick = {navController.navigate(AppScreen.BUY_ANIMALS_SORT)},
onSellerClick = { sellerId ->
navController.navigate(
AppScreen.sellerProfile(sellerId)
)
},
)
}
composable(AppScreen.BUY_ANIMALS_FILTERS) {
FilterScreen(
onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
onBackClick = {
navController.popBackStack()
},
onSkipClick = {
navController.popBackStack()
},
onCancelClick = {
navController.popBackStack()
},
)
}
composable(AppScreen.BUY_ANIMALS_SORT) {
SortScreen(
onApplyClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
onBackClick = {
navController.popBackStack()
},
onSkipClick = {
navController.popBackStack()
},
onCancelClick = {
navController.popBackStack()
},
)
}
composable(AppScreen.CREATE_ANIMAL_LISTING) {
NewListingScreen (
onSaveClick = {navController.navigate(
AppScreen.postSaleSurvey("2")
)},
onBackClick = {
navController.popBackStack()
}
)
}
composable(
route = "${AppScreen.SALE_ARCHIVE}/{saleId}",
arguments = listOf(
navArgument("saleId") { type = NavType.StringType }
)
) { backStackEntry ->
val saleId = backStackEntry
.arguments
?.getString("saleId")
?: return@composable
SaleArchiveScreen(
saleId = saleId,
onBackClick = {
navController.popBackStack()
},
onSaleSurveyClick = { saleId ->
navController.navigate(
AppScreen.sellerProfile(saleId)
)
},
)
}
composable(
route = "${AppScreen.POST_SALE_SURVEY}/{animalId}",
arguments = listOf(
navArgument("animalId") { type = NavType.StringType }
)
) { backStackEntry ->
val animalId = backStackEntry
.arguments
?.getString("animalId")
?: return@composable
PostSaleSurveyScreen (
animalId = animalId,
onBackClick = {
navController.popBackStack()
},
onSubmit = {navController.navigate(
AppScreen.saleArchive("2")
)}
)
}
composable(
route = "${AppScreen.ANIMAL_PROFILE}/{animalId}",
arguments = listOf(
navArgument("animalId") { type = NavType.StringType }
)
) { backStackEntry ->
val animalId = backStackEntry
.arguments
?.getString("animalId")
?: return@composable
AnimalProfileScreen(
animalId = animalId,
onBackClick = {
navController.popBackStack()
},
onSellerClick = { sellerId ->
navController.navigate(
AppScreen.sellerProfile(sellerId)
)
},
)
}
composable(
route = "${AppScreen.SELLER_PROFILE}/{sellerId}",
arguments = listOf(
navArgument("sellerId") { type = NavType.StringType }
)
) { backStackEntry ->
val sellerId = backStackEntry
.arguments
?.getString("sellerId")
?: return@composable
SellerProfileScreen(
sellerId = sellerId,
onBackClick = {
navController.popBackStack()
}
)
}}
}

View File

@ -0,0 +1,371 @@
package com.example.livingai_lg.ui.screens
import com.example.livingai_lg.ui.components.ImageCarousel
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.zIndex
import com.example.livingai_lg.ui.models.Animal
import com.example.livingai_lg.ui.utils.formatPrice
import com.example.livingai_lg.ui.utils.formatViews
import com.example.livingai_lg.ui.components.AdSpaceBanner
import com.example.livingai_lg.ui.components.FloatingActionBar
import com.example.livingai_lg.ui.models.sampleAnimals
import com.example.livingai_lg.ui.utils.formatAge
import com.example.livingai_lg.R
@Composable
fun AnimalProfileScreen(
animalId: String,
onBackClick: () -> Unit = {},
onSellerClick: (sellerId: String) -> Unit = {},
) {
val animal = sampleAnimals.find { animal -> animal.id == animalId } ?: Animal(id = "null")
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF7F4EE))
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF7F4EE))
.verticalScroll(rememberScrollState())
) {
// Photo section with overlays
Box(
modifier = Modifier
.fillMaxWidth()
.height(500.dp)
.shadow(4.dp)
) {
// Main image
val product = null
ImageCarousel(
imageUrls = animal.imageUrl ?: emptyList(),
modifier = Modifier.fillMaxSize()
)
// Gradient overlay at bottom
Box(
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.align(Alignment.BottomCenter)
.background(
Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Black.copy(alpha = 0.6f)
)
)
)
)
// Views indicator (top left)
Row(
modifier = Modifier
.align(Alignment.TopStart)
.padding(start = 16.dp, top = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
painter = painterResource(R.drawable.ic_view),
contentDescription = "Views",
tint = Color.White,
modifier = Modifier.size(24.dp)
)
Text(
text = formatViews(animal.views),
fontSize = 12.sp,
fontWeight = FontWeight.Normal,
color = Color.White
)
}
// Animal info (centered bottom)
Column(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 68.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = "${animal.name}, ${formatAge(animal.age)}",
fontSize = 30.sp,
fontWeight = FontWeight.Normal,
color = Color.White
)
Text(
text = "${animal.breed}${animal.location}, ${animal.distance}".uppercase(),
fontSize = 12.sp,
fontWeight = FontWeight.Normal,
color = Color.White.copy(alpha = 0.7f),
letterSpacing = 1.2.sp
)
}
// AI Score badge (bottom left)
Row(
modifier = Modifier
.align(Alignment.BottomStart)
.padding(start = 0.dp, bottom = 5.dp)
.height(48.dp)
.border(2.dp, Color(0xFF717182), CircleShape)
.padding(horizontal = 12.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
AIScoreCircle(score = animal.aiScore ?: 0f)
Text(
text = "AI Score",
fontSize = 18.sp,
fontWeight = FontWeight.Normal,
color = Color.White
)
}
// Display location (bottom right)
Text(
text = buildString {
append("At Display: ")
append(animal.displayLocation)
},
fontSize = 8.sp,
fontWeight = FontWeight.Normal,
color = Color.White,
lineHeight = 13.sp,
textDecoration = TextDecoration.Underline,
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(end = 16.dp, bottom = 8.dp)
.width(98.dp)
)
}
// Info card section
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp)
) {
Spacer(modifier = Modifier.height(20.dp))
// Price and seller info
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.Top
) {
Column(
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = formatPrice(animal.price),
fontSize = 30.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A)
)
if (animal.isFairPrice ?: false) {
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(start = 0.dp)
) {
Icon(
painter = painterResource(R.drawable.ic_thumbs_up),
contentDescription = "Fair Price",
tint = Color(0xFF0A0A0A),
modifier = Modifier.size(15.dp)
)
Text(
text = "Fair Price",
fontSize = 12.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF00C950)
)
}
}
}
Column(
horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) {
onSellerClick(animal.sellerId?:"")
})
{
Text(
text = "Sold By: ${animal.sellerName}",
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A)
)
// Star rating
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
repeat(5) { index ->
Icon(
painter = painterResource(R.drawable.ic_star),
contentDescription = null,
tint = if (index < (animal.rating ?: 0).toInt()) Color(
0xFFDE9A07
) else Color(0xFFDE9A07),
modifier = Modifier.size(12.dp)
)
}
Text(
text = "${animal.rating} (${animal.ratingCount} Ratings)",
fontSize = 10.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A)
)
}
}
}
Spacer(modifier = Modifier.height(24.dp))
// About section
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = "About",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF09090B).copy(alpha = 0.5f)
)
Text(
text = animal.description ?: "",
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF09090B),
lineHeight = 24.sp
)
}
// Spacer(modifier = Modifier.height(24.dp))
//
//
// FloatingActionBar(
// modifier = Modifier,
// // .offset(y = (-40).dp), // 👈 hover effect
// onChatClick = { /* TODO */ },
// onCallClick = { /* TODO */ },
// onLocationClick = { /* TODO */ },
// onBookmarkClick = { /* TODO */ }
// )
Spacer(modifier = Modifier.height(24.dp))
// Ad space banner
AdSpaceBanner()
Spacer(modifier = Modifier.height(32.dp))
}
}
FloatingActionBar(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 24.dp)
.offset(y = (-20).dp)
.zIndex(10f), // 👈 ensure it floats above everything
onChatClick = { /* TODO */ },
onCallClick = { /* TODO */ },
onLocationClick = { /* TODO */ },
onBookmarkClick = { /* TODO */ }
)
}
}
@Composable
fun AIScoreCircle(score: Float) {
Box(
modifier = Modifier
.size(40.dp)
.drawBehind {
// Background circle
drawCircle(
color = Color(0xFFDD88CF),
radius = size.minDimension / 2,
alpha = 0.3f,
style = Stroke(width = 4.dp.toPx())
)
// Progress arc
val sweepAngle = 360f * score
drawArc(
color = Color(0xFF9AFF9A),
startAngle = -90f,
sweepAngle = sweepAngle,
useCenter = false,
style = Stroke(width = 4.dp.toPx(), cap = StrokeCap.Round),
size = Size(size.width, size.height),
topLeft = Offset.Zero
)
},
contentAlignment = Alignment.Center
) {
Text(
text = "${(score * 100).toInt()}%",
fontSize = 12.sp,
fontWeight = FontWeight.Normal,
color = Color.White
)
}
}
@Composable
fun ActionButtonLarge(icon: Int, label: String) {
Box(
modifier = Modifier
.size(48.dp)
.shadow(8.dp, CircleShape)
.background(Color.White, CircleShape),
contentAlignment = Alignment.Center
) {
Icon(
painter = painterResource(icon),
contentDescription = label,
tint = Color(0xFF0A0A0A),
modifier = Modifier.size(20.dp)
)
}
}

View File

@ -0,0 +1,170 @@
package com.example.livingai_lg.ui.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.example.livingai_lg.ui.components.AdSpaceBanner
import com.example.livingai_lg.ui.components.AnimalTypeSelector
import com.example.livingai_lg.ui.components.BuyAnimalCard
import com.example.livingai_lg.ui.models.animalTypes
import com.example.livingai_lg.ui.components.FilterButton
import com.example.livingai_lg.ui.components.SortButton
import com.example.livingai_lg.ui.components.UserLocationHeader
import com.example.livingai_lg.ui.layout.BottomNavScaffold
import com.example.livingai_lg.ui.models.mainBottomNavItems
import com.example.livingai_lg.ui.models.sampleAnimals
import com.example.livingai_lg.ui.models.userProfile
import com.example.livingai_lg.R
@Composable
fun BuyScreen(
onProductClick: (productId: String) -> Unit = {},
onBackClick: () -> Unit = {},
onNavClick: (route: String) -> Unit = {},
onFilterClick: () -> Unit = {},
onSortClick: () -> Unit = {},
onSellerClick: (sellerId: String) -> Unit = {},
) {
val selectedAnimalType = remember { mutableStateOf<String?>(null) }
val isSaved = remember { mutableStateOf(false) }
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF7F4EE))
) {
BottomNavScaffold(
items = mainBottomNavItems,
currentItem = "Home",
onBottomNavItemClick = onNavClick,
) { paddingValues ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF7F4EE))
.padding(paddingValues)
) {
item {
// Header with profile and notification
// Top header strip
Row(
modifier = Modifier
.fillMaxWidth()
.background(Color(0xFFF7F4EE))
.padding(horizontal = 16.dp, vertical = 12.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
UserLocationHeader(
user = userProfile,
onAddressSelected = {
// optional: reload listings, persist selection, etc.
},
onAddNewClick = {}
)
// Right-side actions (notifications, etc.)
Icon(
painter = painterResource(R.drawable.ic_notification_unread),
contentDescription = "Notifications",
tint = Color.Black,
modifier = Modifier.size(24.dp)
)
}
// Row(
// modifier = Modifier
// .fillMaxWidth()
// .padding(horizontal = 16.dp, vertical = 12.dp),
// horizontalArrangement = Arrangement.SpaceBetween,
// verticalAlignment = Alignment.CenterVertically
// ) {
// UserLocationHeader(
// user = userProfile,
// modifier = Modifier.padding(horizontal = 16.dp)
// )
//
// Icon(
// painter = painterResource(R.drawable.ic_notification_unread),
// contentDescription = "Notifications",
// tint = Color.Black,
// modifier = Modifier.size(24.dp)
// )
// }
// Animal type filter buttons
AnimalTypeSelector(
animalTypes = animalTypes,
selectedAnimalType = selectedAnimalType
) { }
// Ad space banner
AdSpaceBanner(
modifier = Modifier.padding(horizontal = 22.dp)
)
Spacer(modifier = Modifier.height(16.dp))
// Sort and Filter buttons
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 22.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
SortButton(onSortClick)
FilterButton(onFilterClick)
}
sampleAnimals.forEach { animal ->
Spacer(modifier = Modifier.height(16.dp))
// Product card
BuyAnimalCard(
product = animal,
isSaved = isSaved.value,
onSavedChange = { isSaved.value = it },
onProductClick = { onProductClick(animal.id)},
onSellerClick = onSellerClick
)
Spacer(modifier = Modifier.height(16.dp))
// Ad space banner at bottom
AdSpaceBanner(
modifier = Modifier.padding(horizontal = 22.dp)
)
}
Spacer(modifier = Modifier.height(80.dp))
}
}
}
}
}

View File

@ -0,0 +1,227 @@
package com.example.livingai_lg.ui.screens
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.RadioButton
import androidx.compose.material3.RadioButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.ui.components.OptionCard
import com.example.livingai_lg.ui.components.backgrounds.StoreBackground
import com.example.livingai_lg.R
data class ServiceType(
val id: String,
val title: String,
val icon: Int,
val backgroundColor: Color
)
@Composable
fun ChooseServiceScreen(
profileId: String? = null,
onServiceSelected: (serviceType: String) -> Unit = {}
) {
val selectedService = remember { mutableStateOf<String?>(null) }
val serviceTypes = listOf(
ServiceType(
id = "transport",
title = "Transport",
icon = R.drawable.ic_shop,
backgroundColor = Color(0xFF9D4EDD)
),
ServiceType(
id = "vet",
title = "Vet",
icon = R.drawable.ic_bag,
backgroundColor = Color(0xFF3A86FF)
),
ServiceType(
id = "feed_supplier",
title = "Feed Supplier",
icon = R.drawable.ic_spanner,
backgroundColor = Color(0xFFFF5722)
),
ServiceType(
id = "medicine_supplier",
title = "Medicine Supplier",
icon = R.drawable.ic_shop2,
backgroundColor = Color(0xFF4CAF50)
),
ServiceType(
id = "other",
title = "Other",
icon = R.drawable.ic_other,
backgroundColor = Color(0xFFD4A942)
)
)
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF5F1E8))
) {
// Decorative background
StoreBackground()
// Main content
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(horizontal = 24.dp, vertical = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(32.dp))
// Header
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Choose Service",
fontSize = 36.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A)
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Choose Service**",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A).copy(alpha = 0.8f)
)
}
Spacer(modifier = Modifier.height(48.dp))
// Service selection cards
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
serviceTypes.forEach { service ->
OptionCard(
label = service.title,
icon = service.icon,
iconBackgroundColor = service.backgroundColor,
onClick = {
selectedService.value = service.id
onServiceSelected(service.id)
}
)
}
}
Spacer(modifier = Modifier.height(48.dp))
}
}
}
@Composable
fun ServiceTypeCard(
service: ServiceType,
isSelected: Boolean,
onClick: () -> Unit
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(90.dp)
.shadow(2.dp, RoundedCornerShape(16.dp))
.background(Color.White, RoundedCornerShape(16.dp))
.border(1.dp, Color(0xFFF3F4F6), RoundedCornerShape(16.dp))
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() },
onClick = onClick
)
.padding(12.dp)
) {
Row(
modifier = Modifier
.fillMaxSize(),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Icon container
Box(
modifier = Modifier
.size(56.dp)
.background(service.backgroundColor, RoundedCornerShape(14.dp)),
contentAlignment = Alignment.Center
) {
when (service.id) {
"transport" ->
Icon(
painter = painterResource(id = R.drawable.ic_shop),
contentDescription = "Transport",
)
"vet" -> Icon(
painter = painterResource(id = R.drawable.ic_bag),
contentDescription = "Vet",
)
"feed_supplier" -> Icon(
painter = painterResource(id = R.drawable.ic_spanner),
contentDescription = "Feed Supplier",
)
"medicine_supplier" -> Icon(
painter = painterResource(id = R.drawable.ic_shop2),
contentDescription = "Medicine Supplier",
)
"other" -> Icon(
painter = painterResource(id = R.drawable.ic_other),
contentDescription = "Other",
)
}
}
// Title
Text(
text = service.title,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A),
modifier = Modifier.weight(1f)
)
// Radio button
RadioButton(
selected = isSelected,
onClick = onClick,
colors = RadioButtonDefaults.colors(
selectedColor = Color(0xFF6B7280),
unselectedColor = Color(0xFF6B7280)
)
)
}
}
}

View File

@ -0,0 +1,117 @@
package com.example.livingai_lg.ui.screens
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.api.AuthApiClient
import com.example.livingai_lg.api.AuthManager
import com.example.livingai_lg.api.TokenManager
import com.example.livingai_lg.ui.components.OptionCard
import com.example.livingai_lg.ui.components.backgrounds.StoreBackground
import com.example.livingai_lg.ui.models.profileTypes
import kotlinx.coroutines.launch
@Composable
fun CreateProfileScreen(
name: String,
onProfileSelected: (profileId: String) -> Unit = {}
) {
val selectedProfile = remember { mutableStateOf<String>(profileTypes[0].id) }
val context = LocalContext.current.applicationContext
val scope = rememberCoroutineScope()
val authManager = remember { AuthManager(context, AuthApiClient(context), TokenManager(context)) }
fun updateProfile(profileId: String) {
scope.launch {
authManager.updateProfile(name, profileId)
.onSuccess {
onProfileSelected(profileId)
}
.onFailure {
Toast.makeText(context, "Failed to update profile: ${it.message}", Toast.LENGTH_LONG).show()
}
}
}
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF5F1E8))
) {
// Decorative background
StoreBackground()
// Main content
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(horizontal = 24.dp, vertical = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(32.dp))
// Header
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Create Profile",
fontSize = 36.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A)
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Choose Profile Type",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A).copy(alpha = 0.8f)
)
}
Spacer(modifier = Modifier.height(48.dp))
// Profile selection cards
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
profileTypes.forEach { profile ->
OptionCard(
label = profile.title,
icon = profile.icon,
iconBackgroundColor = profile.backgroundColor,
onClick = {
updateProfile(profile.id)
}
)
}
}
Spacer(modifier = Modifier.height(48.dp))
}
}
}

View File

@ -0,0 +1,552 @@
package com.example.livingai_lg.ui.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.ui.components.DropdownInput
import com.example.livingai_lg.ui.components.RangeFilter
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FilterScreen(
onBackClick: () -> Unit = {},
onSkipClick: () -> Unit = {},
onSubmitClick: () -> Unit = {},
onCancelClick: () -> Unit = {}
) {
var selectedAnimal by remember { mutableStateOf("") }
var selectedBreed by remember { mutableStateOf("") }
var selectedDistance by remember { mutableStateOf("") }
var selectedGender by remember { mutableStateOf("") }
var animalExpanded by remember { mutableStateOf(false) }
var breedExpanded by remember { mutableStateOf(false) }
var distanceExpanded by remember { mutableStateOf(false) }
var genderExpanded by remember { mutableStateOf(false) }
var priceFrom by remember { mutableStateOf("0") }
var priceTo by remember { mutableStateOf("90,000") }
var priceSliderValue by remember { mutableFloatStateOf(0f) }
var ageFrom by remember { mutableStateOf("1") }
var ageTo by remember { mutableStateOf("20") }
var selectedPregnancyStatus by remember { mutableStateOf(setOf<String>()) }
var weightFrom by remember { mutableStateOf("0") }
var weightTo by remember { mutableStateOf("9000") }
var milkYieldFrom by remember { mutableStateOf("0") }
var milkYieldTo by remember { mutableStateOf("900") }
var calvingFrom by remember { mutableStateOf(0) }
var calvingTo by remember { mutableStateOf(10) }
var calvingFromExpanded by remember { mutableStateOf(false) }
var calvingToExpanded by remember { mutableStateOf(false) }
val maxCalving = 10
val calvingFromOptions = (0..maxCalving).map { it.toString() }
val calvingToOptions = (calvingFrom..maxCalving).map { it.toString() }
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF7F4EE))
) {
// Header
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 16.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
IconButton(onClick = onBackClick) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "Back",
tint = Color(0xFF0A0A0A)
)
}
Text(
text = "Filters",
fontSize = 32.sp,
fontWeight = FontWeight.Normal,
color = Color.Black
)
}
TextButton(onClick = onSkipClick) {
Text(
text = "Skip",
fontSize = 24.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF4A5565),
textDecoration = TextDecoration.Underline
)
}
}
}
// Scrollable Content
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(horizontal = 16.dp)
.padding(bottom = 24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
// Animal Section
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = "Animal",
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color.Black
)
ExposedDropdownMenuBox(
expanded = animalExpanded,
onExpandedChange = { animalExpanded = it }
) {
OutlinedTextField(
value = selectedAnimal,
onValueChange = {},
readOnly = true,
placeholder = {
Text(
"Select Animal",
color = Color(0xFF717182),
fontSize = 16.sp
)
},
modifier = Modifier
.fillMaxWidth()
.menuAnchor(),
shape = RoundedCornerShape(8.dp),
colors = OutlinedTextFieldDefaults.colors(
unfocusedContainerColor = Color.White,
focusedContainerColor = Color.White,
unfocusedBorderColor = Color(0x1A000000),
focusedBorderColor = Color(0x1A000000)
)
)
ExposedDropdownMenu(
expanded = animalExpanded,
onDismissRequest = { animalExpanded = false }
) {
listOf("Cow", "Buffalo", "Goat", "Sheep").forEach { animal ->
DropdownMenuItem(
text = { Text(animal) },
onClick = {
selectedAnimal = animal
animalExpanded = false
}
)
}
}
}
}
// Breed Section
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = "Breed",
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color.Black
)
ExposedDropdownMenuBox(
expanded = breedExpanded,
onExpandedChange = { breedExpanded = it }
) {
OutlinedTextField(
value = selectedBreed,
onValueChange = {},
readOnly = true,
placeholder = {
Text(
"Select Breed",
color = Color(0xFF717182),
fontSize = 16.sp
)
},
modifier = Modifier
.fillMaxWidth()
.menuAnchor(),
shape = RoundedCornerShape(8.dp),
colors = OutlinedTextFieldDefaults.colors(
unfocusedContainerColor = Color.White,
focusedContainerColor = Color.White,
unfocusedBorderColor = Color(0x1A000000),
focusedBorderColor = Color(0x1A000000)
)
)
ExposedDropdownMenu(
expanded = breedExpanded,
onDismissRequest = { breedExpanded = false }
) {
listOf("Holstein", "Jersey", "Gir", "Sahiwal").forEach { breed ->
DropdownMenuItem(
text = { Text(breed) },
onClick = {
selectedBreed = breed
breedExpanded = false
}
)
}
}
}
}
// Price and Age Row
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp) // 👈 reduce spacing
) {
Column(
modifier = Modifier.weight(1f)
) {
RangeFilter(
modifier = Modifier.fillMaxWidth(), // 👈 important
label = "Price",
min = 0,
max = 90_000,
valueFrom = priceFrom.toInt(),
valueTo = priceTo.replace(",", "").toInt(),
onValueChange = { from, to ->
priceFrom = from.toString()
priceTo = to.toString()
}
)
}
Column(
modifier = Modifier.weight(1f)
) {
RangeFilter(
modifier = Modifier.fillMaxWidth(),
label = "Age",
min = 0,
max = 20,
valueFrom = ageFrom.toInt(),
valueTo = ageTo.replace(",", "").toInt(),
showSlider = false,
onValueChange = { from, to ->
ageFrom = from.toString()
ageTo = to.toString()
}
)
}
}
// Distance and Gender Row
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(64.dp)
) {
// Distance Section
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(3.dp)
) {
DropdownInput(
label = "Distance",
selected = selectedDistance,
options = listOf("0-5 km", "5-10 km", "10-20 km", "20+ km"),
expanded = distanceExpanded,
onExpandedChange = { distanceExpanded = it },
onSelect = { item ->
selectedDistance = item
distanceExpanded = false
},
placeholder = "Choose Distance", // <--- half width
)
}
// Gender Section
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(3.dp)
) {
DropdownInput(
label = "Gender",
selected = selectedGender,
options = listOf("Male", "Female"),
expanded = genderExpanded,
onExpandedChange = { genderExpanded = it },
onSelect = { item ->
selectedGender = item
genderExpanded = false
},
placeholder = "Choose Gender", // <--- half width
)
}
}
// Pregnancy Status Section
Column(
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
text = "Pregnancy Status",
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
color = Color(0xFF364153)
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(20.dp)
) {
listOf("Pregnant", "Calved", "Option").forEach { status ->
PregnancyStatusChip(
label = status,
isSelected = selectedPregnancyStatus.contains(status),
onClick = {
selectedPregnancyStatus = if (selectedPregnancyStatus.contains(status)) {
selectedPregnancyStatus - status
} else {
selectedPregnancyStatus + status
}
}
)
}
}
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp) // 👈 reduce spacing
) {
Column(
modifier = Modifier.weight(1f)
) {
RangeFilter(
modifier = Modifier.fillMaxWidth(), // 👈 important
label = "Weight",
min = 0,
max = 9000,
valueFrom = weightFrom.toInt(),
valueTo = weightTo.replace(",", "").toInt(),
onValueChange = { from, to ->
weightFrom = from.toString()
weightTo = to.toString()
}
)
}
Column(
modifier = Modifier.weight(1f)
) {
RangeFilter(
modifier = Modifier.fillMaxWidth(),
label = "Milk Yield",
min = 0,
max = 900,
valueFrom = milkYieldFrom.toInt(),
valueTo = milkYieldTo.replace(",", "").toInt(),
showSlider = true,
onValueChange = { from, to ->
milkYieldFrom = from.toString()
milkYieldTo = to.toString()
}
)
}
}
// Calving Number Section
Column(
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
// FROM
DropdownInput(
label = "Calving Number",
selected = calvingFrom.toString(),
options = calvingFromOptions,
expanded = calvingFromExpanded,
onExpandedChange = { calvingFromExpanded = it },
onSelect = { value ->
val newFrom = value.toInt()
calvingFrom = newFrom
// 👇 enforce invariant
if (calvingTo < newFrom) {
calvingTo = newFrom
}
calvingFromExpanded = false
},
placeholder = "From",
modifier = Modifier.weight(1f)
)
Text(
text = "to",
fontSize = 20.sp,
color = Color.Black
)
// TO
DropdownInput(
selected = calvingTo.toString(),
options = calvingToOptions, // 👈 constrained options
expanded = calvingToExpanded,
onExpandedChange = { calvingToExpanded = it },
onSelect = { value ->
calvingTo = value.toInt()
calvingToExpanded = false
},
placeholder = "To",
modifier = Modifier.weight(1f)
)
}
}
Spacer(modifier = Modifier.height(24.dp))
// Action Buttons
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// Submit Button
Button(
onClick = onSubmitClick,
modifier = Modifier
.width(173.dp)
.height(50.dp),
shape = RoundedCornerShape(25.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color.Black,
contentColor = Color.White
)
) {
Text(
text = "Submit",
fontSize = 16.sp,
fontWeight = FontWeight.Normal
)
}
// Cancel Button
OutlinedButton(
onClick = onCancelClick,
modifier = Modifier
.width(173.dp)
.height(50.dp),
shape = RoundedCornerShape(25.dp),
border = BorderStroke(
1.dp,
Color(0x1A000000)
),
colors = ButtonDefaults.outlinedButtonColors(
containerColor = Color.Transparent,
contentColor = Color(0xFF0A0A0A)
)
) {
Text(
text = "Cancel",
fontSize = 16.sp,
fontWeight = FontWeight.Normal
)
}
}
}
}
}
@Composable
fun PregnancyStatusChip(
label: String,
isSelected: Boolean,
onClick: () -> Unit
) {
Box(
modifier = Modifier
.height(29.dp)
.widthIn(min = 76.dp)
.background(
if (isSelected) Color(0xFF007BFF) else Color.Transparent,
RoundedCornerShape(24.dp)
)
.border(
1.dp,
Color(0xFFE5E7EB),
RoundedCornerShape(24.dp)
)
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() },
onClick = onClick)
.padding(horizontal = 12.dp),
contentAlignment = Alignment.Center
) {
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = label,
fontSize = 10.sp,
fontWeight = FontWeight.Normal,
color = if (isSelected) Color(0xFFF4F4F4) else Color.Black,
textDecoration = TextDecoration.Underline
)
if (isSelected) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = "Selected",
tint = Color(0xFFE3E3E3),
modifier = Modifier.size(24.dp)
)
}
}
}
}

View File

@ -0,0 +1,473 @@
package com.example.livingai_lg.ui.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.ui.components.MediaPickerCard
import com.example.livingai_lg.ui.models.NewListingFormState
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NewListingScreen(
onBackClick: () -> Unit = {},
onSaveClick: () -> Unit = {},
formState: NewListingFormState = remember { NewListingFormState() }
) {
var name by formState.name
var animal by formState.animal
var breed by formState.breed
var age by formState.age
var milkYield by formState.milkYield
var calvingNumber by formState.calvingNumber
var reproductiveStatus by formState.reproductiveStatus
var description by formState.description
var animalExpanded by formState.animalExpanded
var breedExpanded by formState.breedExpanded
val mediaUploads = formState.mediaUploads
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF7F4EE))
) {
// Header
TopAppBar(
title = {
Text(
text = "New Listing",
fontSize = 24.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A)
)
},
navigationIcon = {
IconButton(onClick = onBackClick) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "Back",
tint = Color(0xFF0A0A0A)
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color(0xFFF7F4EE)
)
)
// Form Content
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(horizontal = 16.dp)
.padding(bottom = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// Name Input
OutlinedTextField(
value = name,
onValueChange = { name = it },
placeholder = {
Text(
"Name",
color = Color(0xFF717182),
fontSize = 16.sp
)
},
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
shape = RoundedCornerShape(8.dp),
colors = OutlinedTextFieldDefaults.colors(
unfocusedContainerColor = Color.White,
focusedContainerColor = Color.White,
unfocusedBorderColor = Color(0x1A000000),
focusedBorderColor = Color(0x1A000000)
),
singleLine = true
)
// Animal and Breed Row
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
// Animal Dropdown
Box(
modifier = Modifier
.weight(1f)
.height(50.dp)
) {
ExposedDropdownMenuBox(
expanded = animalExpanded,
onExpandedChange = { animalExpanded = it }
) {
OutlinedTextField(
value = animal,
onValueChange = {},
readOnly = true,
placeholder = {
Text(
"Animal",
color = Color(0xFF717182),
fontSize = 16.sp
)
},
trailingIcon = {
Icon(
imageVector = Icons.Default.ArrowDropDown,
contentDescription = "Dropdown",
tint = Color(0xFFA5A5A5)
)
},
modifier = Modifier
.fillMaxWidth()
.menuAnchor(),
shape = RoundedCornerShape(8.dp),
colors = OutlinedTextFieldDefaults.colors(
unfocusedContainerColor = Color.White,
focusedContainerColor = Color.White,
unfocusedBorderColor = Color(0x1A000000),
focusedBorderColor = Color(0x1A000000)
),
singleLine = true
)
ExposedDropdownMenu(
expanded = animalExpanded,
onDismissRequest = { animalExpanded = false }
) {
listOf("Cow", "Buffalo", "Goat", "Sheep").forEach { option ->
DropdownMenuItem(
text = { Text(option) },
onClick = {
animal = option
animalExpanded = false
}
)
}
}
}
}
// Breed Dropdown
Box(
modifier = Modifier
.weight(1f)
.height(50.dp)
) {
ExposedDropdownMenuBox(
expanded = breedExpanded,
onExpandedChange = { breedExpanded = it }
) {
OutlinedTextField(
value = breed,
onValueChange = {},
readOnly = true,
placeholder = {
Text(
"Breed",
color = Color(0xFF717182),
fontSize = 16.sp
)
},
trailingIcon = {
Icon(
imageVector = Icons.Default.ArrowDropDown,
contentDescription = "Dropdown",
tint = Color(0xFFA5A5A5)
)
},
modifier = Modifier
.fillMaxWidth()
.menuAnchor(),
shape = RoundedCornerShape(8.dp),
colors = OutlinedTextFieldDefaults.colors(
unfocusedContainerColor = Color.White,
focusedContainerColor = Color.White,
unfocusedBorderColor = Color(0x1A000000),
focusedBorderColor = Color(0x1A000000)
),
singleLine = true
)
ExposedDropdownMenu(
expanded = breedExpanded,
onDismissRequest = { breedExpanded = false }
) {
listOf("Holstein", "Jersey", "Gir", "Sahiwal").forEach { option ->
DropdownMenuItem(
text = { Text(option) },
onClick = {
breed = option
breedExpanded = false
}
)
}
}
}
}
}
// Age and Milk Yield Row
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
OutlinedTextField(
value = age,
onValueChange = { age = it },
placeholder = {
Text(
"Age (Years)",
color = Color(0xFF717182),
fontSize = 16.sp
)
},
trailingIcon = {
Icon(
imageVector = Icons.Filled.ArrowDropDown,
contentDescription = "Sort",
tint = Color(0xFF353535)
)
},
modifier = Modifier
.weight(1f)
.height(50.dp),
shape = RoundedCornerShape(8.dp),
colors = OutlinedTextFieldDefaults.colors(
unfocusedContainerColor = Color.White,
focusedContainerColor = Color.White,
unfocusedBorderColor = Color(0x1A000000),
focusedBorderColor = Color(0x1A000000)
),
singleLine = true
)
OutlinedTextField(
value = milkYield,
onValueChange = { milkYield = it },
placeholder = {
Text(
"Average Milk Yield (L)",
color = Color(0xFF717182),
fontSize = 16.sp
)
},
modifier = Modifier
.weight(1f)
.height(50.dp),
shape = RoundedCornerShape(8.dp),
colors = OutlinedTextFieldDefaults.colors(
unfocusedContainerColor = Color.White,
focusedContainerColor = Color.White,
unfocusedBorderColor = Color(0x1A000000),
focusedBorderColor = Color(0x1A000000)
),
singleLine = true
)
}
// Calving Number
OutlinedTextField(
value = calvingNumber,
onValueChange = { calvingNumber = it },
placeholder = {
Text(
"Calving Number",
color = Color(0xFF717182),
fontSize = 16.sp
)
},
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
shape = RoundedCornerShape(8.dp),
colors = OutlinedTextFieldDefaults.colors(
unfocusedContainerColor = Color.White,
focusedContainerColor = Color.White,
unfocusedBorderColor = Color(0x1A000000),
focusedBorderColor = Color(0x1A000000)
),
singleLine = true
)
// Reproductive Status
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = "Reproductive Status",
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A)
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
listOf("Pregnant", "Calved", "None").forEach { status ->
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
){
reproductiveStatus = status
}
) {
RadioButton(
selected = reproductiveStatus == status,
onClick = { reproductiveStatus = status },
colors = RadioButtonDefaults.colors(
selectedColor = Color(0xFF717182),
unselectedColor = Color(0xFF717182)
)
)
Text(
text = status,
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A)
)
}
}
}
}
// Description
OutlinedTextField(
value = description,
onValueChange = { description = it },
placeholder = {
Text(
"Description",
color = Color(0xFF717182),
fontSize = 16.sp
)
},
modifier = Modifier
.fillMaxWidth()
.height(74.dp),
shape = RoundedCornerShape(8.dp),
colors = OutlinedTextFieldDefaults.colors(
unfocusedContainerColor = Color.White,
focusedContainerColor = Color.White,
unfocusedBorderColor = Color(0x1A000000),
focusedBorderColor = Color(0x1A000000)
),
maxLines = 3
)
// Upload Media Section
Text(
text = "Upload Media",
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold,
color = Color(0xFF000000),
modifier = Modifier.padding(top = 8.dp)
)
// Photo Uploads Grid
Column(
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
mediaUploads.chunked(2).forEach { rowItems ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
rowItems.forEach { upload ->
MediaPickerCard(
upload = upload,
modifier = Modifier.weight(1f),
onUriSelected = { uri ->
upload.uri = uri
}
)
}
// Fill empty space if odd number of items
if (rowItems.size == 1) {
Spacer(modifier = Modifier.weight(1f))
}
}
}
}
// Action Buttons
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
// Cancel Button
OutlinedButton(
onClick = onBackClick,
modifier = Modifier
.weight(1f)
.height(50.dp),
shape = RoundedCornerShape(25.dp),
border = BorderStroke(
1.dp,
Color(0x1A000000)
),
colors = ButtonDefaults.outlinedButtonColors(
containerColor = Color.Transparent,
contentColor = Color(0xFF0A0A0A)
)
) {
Text(
text = "Cancel",
fontSize = 16.sp,
fontWeight = FontWeight.Normal
)
}
// Save Profile Button
Button(
onClick = onSaveClick,
modifier = Modifier
.weight(1f)
.height(50.dp),
shape = RoundedCornerShape(25.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color.Black,
contentColor = Color.White
)
) {
Text(
text = "Save Profile",
fontSize = 16.sp,
fontWeight = FontWeight.Normal
)
}
}
Spacer(modifier = Modifier.height(16.dp))
}
}
}

View File

@ -0,0 +1,523 @@
package com.example.livingai_lg.ui.screens
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.CalendarToday
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
data class SaleSurveyData(
val buyerName: String = "",
val salePrice: String = "",
val saleDate: String = "",
val saleLocation: String = "",
val notes: String = "",
val attachments: List<SurveyAttachment> = emptyList()
)
data class SurveyAttachment(
val id: String,
val name: String,
val type: SurveyAttachmentType
)
enum class SurveyAttachmentType {
DOCUMENT,
PHOTO,
ADD_NEW
}
@Composable
fun PostSaleSurveyScreen(
animalId: String? = null,
onBackClick: () -> Unit = {},
onSkipClick: () -> Unit = {},
onSubmit: (SaleSurveyData) -> Unit = {},
onCancel: () -> Unit = {}
) {
var surveyData by remember { mutableStateOf(SaleSurveyData()) }
var buyerNameExpanded by remember { mutableStateOf(false) }
val defaultAttachments = listOf(
SurveyAttachment("1", "Sale Papers 1", SurveyAttachmentType.DOCUMENT),
SurveyAttachment("2", "Photo 2", SurveyAttachmentType.PHOTO),
SurveyAttachment("3", "Photo 1", SurveyAttachmentType.PHOTO),
SurveyAttachment("4", "Add new", SurveyAttachmentType.ADD_NEW)
)
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF7F4EE))
) {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(horizontal = 16.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "Back",
tint = Color(0xFF0A0A0A),
modifier = Modifier
.size(36.dp)
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) { onBackClick() }
)
Text(
text = "Skip",
fontSize = 24.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF4A5565),
textDecoration = TextDecoration.Underline,
modifier = Modifier.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) { onSkipClick() }
)
}
Spacer(modifier = Modifier.height(26.dp))
FormField(
label = "Sold to*",
isRequired = true
) {
DropdownField(
placeholder = "Buyer Name",
value = surveyData.buyerName,
expanded = buyerNameExpanded,
onExpandedChange = { buyerNameExpanded = it },
onValueChange = { surveyData = surveyData.copy(buyerName = it) }
)
}
Spacer(modifier = Modifier.height(29.dp))
FormField(
label = "Sale Price*",
isRequired = true
) {
TextInputField(
placeholder = "Price",
value = surveyData.salePrice,
onValueChange = { surveyData = surveyData.copy(salePrice = it) }
)
}
Spacer(modifier = Modifier.height(21.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
FormField(
label = "Sale Date*",
isRequired = true,
modifier = Modifier.weight(1f)
) {
DatePickerField(
placeholder = "Date",
value = surveyData.saleDate,
onValueChange = { surveyData = surveyData.copy(saleDate = it) }
)
}
FormField(
label = "Sale Location",
isRequired = false,
modifier = Modifier.weight(1f)
) {
TextInputField(
placeholder = "Location",
value = surveyData.saleLocation,
onValueChange = { surveyData = surveyData.copy(saleLocation = it) }
)
}
}
Spacer(modifier = Modifier.height(34.dp))
MultilineTextInputField(
placeholder = "Notes",
value = surveyData.notes,
onValueChange = { surveyData = surveyData.copy(notes = it) },
modifier = Modifier
.fillMaxWidth()
.height(104.dp)
)
Spacer(modifier = Modifier.height(31.dp))
Text(
text = "Attachments",
fontSize = 24.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF000000),
lineHeight = 29.sp,
modifier = Modifier.padding(start = 11.dp)
)
Spacer(modifier = Modifier.height(31.dp))
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(19.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
AttachmentChip(
name = defaultAttachments[0].name,
type = defaultAttachments[0].type,
modifier = Modifier.weight(1f)
)
AttachmentChip(
name = defaultAttachments[1].name,
type = defaultAttachments[1].type,
modifier = Modifier.weight(1f)
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
AttachmentChip(
name = defaultAttachments[2].name,
type = defaultAttachments[2].type,
modifier = Modifier.weight(1f)
)
AttachmentChip(
name = defaultAttachments[3].name,
type = defaultAttachments[3].type,
isAddNew = true,
modifier = Modifier.weight(1f)
)
}
}
Spacer(modifier = Modifier.height(53.dp))
PrimaryButton(
text = "Submit",
onClick = { onSubmit(surveyData) },
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.height(20.dp))
SecondaryButton(
text = "Cancel",
onClick = onCancel,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.height(32.dp))
}
}
}
@Composable
fun FormField(
label: String,
isRequired: Boolean,
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(3.dp)
) {
Text(
text = label,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF000000),
lineHeight = 29.sp
)
content()
}
}
@Composable
fun TextInputField(
placeholder: String,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
.fillMaxWidth()
.height(50.dp)
.border(
width = 1.078.dp,
color = Color(0xFF000000).copy(alpha = 0.1f),
shape = RoundedCornerShape(8.dp)
)
.background(Color.Transparent, RoundedCornerShape(8.dp))
.padding(horizontal = 12.dp),
contentAlignment = Alignment.CenterStart
) {
if (value.isEmpty()) {
Text(
text = placeholder,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF717182),
letterSpacing = (-0.312).sp
)
}
}
}
@Composable
fun MultilineTextInputField(
placeholder: String,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
.border(
width = 1.078.dp,
color = Color(0xFF000000).copy(alpha = 0.1f),
shape = RoundedCornerShape(8.dp)
)
.background(Color.Transparent, RoundedCornerShape(8.dp))
.padding(12.dp),
contentAlignment = Alignment.TopStart
) {
if (value.isEmpty()) {
Text(
text = placeholder,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF717182),
lineHeight = 24.sp,
letterSpacing = (-0.312).sp
)
}
}
}
@Composable
fun DropdownField(
placeholder: String,
value: String,
expanded: Boolean,
onExpandedChange: (Boolean) -> Unit,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
.fillMaxWidth()
.height(50.dp)
.border(
width = 1.078.dp,
color = Color(0xFF000000).copy(alpha = 0.1f),
shape = RoundedCornerShape(8.dp)
)
.background(Color.Transparent, RoundedCornerShape(8.dp))
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) { onExpandedChange(!expanded) }
.padding(horizontal = 12.dp)
) {
Text(
text = value.ifEmpty { placeholder },
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF717182),
letterSpacing = (-0.312).sp,
modifier = Modifier.align(Alignment.CenterStart)
)
Icon(
imageVector = Icons.Default.ArrowDropDown,
contentDescription = "Dropdown",
tint = Color(0xFFA5A5A5),
modifier = Modifier
.size(15.dp, 10.dp)
.align(Alignment.CenterEnd)
)
}
}
@Composable
fun DatePickerField(
placeholder: String,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
.fillMaxWidth()
.height(50.dp)
.border(
width = 1.078.dp,
color = Color(0xFF000000).copy(alpha = 0.1f),
shape = RoundedCornerShape(8.dp)
)
.background(Color.Transparent, RoundedCornerShape(8.dp))
.padding(horizontal = 12.dp)
) {
Text(
text = if (value.isEmpty()) placeholder else value,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF717182),
letterSpacing = (-0.312).sp,
modifier = Modifier.align(Alignment.CenterStart)
)
Icon(
imageVector = Icons.Default.CalendarToday,
contentDescription = "Calendar",
tint = Color(0xFF000000),
modifier = Modifier
.size(24.dp)
.align(Alignment.CenterEnd)
)
}
}
@Composable
fun AttachmentChip(
name: String,
type: SurveyAttachmentType,
modifier: Modifier = Modifier,
isAddNew: Boolean = false,
onClick: () -> Unit = {}
) {
Row(
modifier = modifier
.height(43.dp)
.border(
width = 1.078.dp,
color = Color(0xFFE5E7EB),
shape = RoundedCornerShape(24.dp)
)
.background(Color.Transparent, RoundedCornerShape(24.dp))
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) { onClick() }
.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = name,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF4A5565),
lineHeight = 24.sp,
textDecoration = TextDecoration.Underline
)
Spacer(modifier = Modifier.width(8.dp))
Icon(
imageVector = if (isAddNew) Icons.Default.Add else Icons.Default.ArrowDropDown,
contentDescription = if (isAddNew) "Add" else "Attachment",
tint = if (isAddNew) Color(0xFF777777) else Color(0xFFADADAD),
modifier = Modifier.size(24.dp)
)
}
}
@Composable
fun PrimaryButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
.width(173.dp)
.height(50.dp)
.background(Color(0xFF0A0A0A), RoundedCornerShape(50.dp))
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) { onClick() },
contentAlignment = Alignment.Center
) {
Text(
text = text,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFFFFFFFF),
lineHeight = 24.sp,
letterSpacing = (-0.312).sp,
textAlign = TextAlign.Center
)
}
}
@Composable
fun SecondaryButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
.width(173.dp)
.height(50.dp)
.border(
width = 1.078.dp,
color = Color(0xFF000000).copy(alpha = 0.1f),
shape = RoundedCornerShape(50.dp)
)
.background(Color.Transparent, RoundedCornerShape(50.dp))
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() }
) { onClick() },
contentAlignment = Alignment.Center
) {
Text(
text = text,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A),
lineHeight = 24.sp,
letterSpacing = (-0.312).sp,
textAlign = TextAlign.Center
)
}
}

View File

@ -0,0 +1,355 @@
package com.example.livingai_lg.ui.screens
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.AttachFile
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage
data class SaleArchive(
val id: String,
val name: String,
val breed: String,
val soldTo: String,
val saleDate: String,
val salePrice: String,
val location: String,
val notes: String,
val imageUrl: String,
val attachments: List<Attachment>
)
data class Attachment(
val id: String,
val name: String,
val type: AttachmentType
)
enum class AttachmentType {
SALE_PAPERS,
PHOTO
}
@Composable
fun SaleArchiveScreen(
saleId: String,
onBackClick: () -> Unit = {},
onSaleSurveyClick:(saleId: String) -> Unit = {},
) {
val sale = SaleArchive(
id = "1",
name = "Gauri",
breed = "Gir",
soldTo = "Buyer 1",
saleDate = "07.12.2025",
salePrice = "₹30,000",
location = "Ravi Nagar, Nagpur",
notes = "Buyer requested a check up after 15 days of sale. Additional support for 20 days.",
imageUrl = "https://api.builder.io/api/v1/image/assets/TEMP/9389ccf57f6d262b500be45f6d9cbfe929302413?width=784",
attachments = listOf(
Attachment("1", "Sale Papers 1", AttachmentType.SALE_PAPERS),
Attachment("2", "Photo 1", AttachmentType.PHOTO),
Attachment("3", "Sale Papers 1", AttachmentType.SALE_PAPERS),
Attachment("4", "Photo 2", AttachmentType.PHOTO)
)
)
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF7F4EE))
) {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(64.dp)
.padding(horizontal = 11.dp, vertical = 15.dp)
) {
Icon(
Icons.Default.ArrowBack,
contentDescription = "Back",
tint = Color(0xFF0A0A0A),
modifier = Modifier
.size(36.dp)
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() },
onClick = onBackClick
)
)
}
AsyncImage(
model = sale.imageUrl,
contentDescription = "${sale.name} image",
modifier = Modifier
.fillMaxWidth()
.height(273.dp),
contentScale = ContentScale.Crop
)
Column(
modifier = Modifier
.fillMaxWidth()
.shadow(
elevation = 25.dp,
shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp),
spotColor = Color.Black.copy(alpha = 0.25f)
)
.border(
width = 1.078.dp,
color = Color(0xFFE5E7EB).copy(alpha = 0.5f),
shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp)
)
.background(
color = Color(0xFFFCFBF8),
shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp)
)
.padding(horizontal = 21.dp, vertical = 17.dp)
) {
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(1.987.dp)
) {
Text(
text = "Name, Breed",
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF6A7282),
lineHeight = 16.sp
)
Text(
text = "${sale.name}, (${sale.breed})",
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF101828),
lineHeight = 22.sp
)
}
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(11.99.dp)
) {
InfoField(
label = "Sold To",
value = sale.soldTo,
modifier = Modifier.weight(1f)
)
InfoField(
label = "Sale Date",
value = sale.saleDate,
modifier = Modifier.weight(1f)
)
}
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 17.dp),
horizontalArrangement = Arrangement.spacedBy(11.99.dp)
) {
InfoField(
label = "Sale Price",
value = sale.salePrice,
modifier = Modifier.weight(1f)
)
InfoField(
label = "Location",
value = sale.location,
modifier = Modifier.weight(1f)
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(1.078.dp)
.background(Color(0xFFD1D5DC))
)
Spacer(modifier = Modifier.height(16.dp))
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(5.995.dp)
) {
Text(
text = "Notes",
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF6A7282),
lineHeight = 16.sp
)
Box(
modifier = Modifier
.fillMaxWidth()
.background(
color = Color.Transparent,
shape = RoundedCornerShape(12.dp)
)
) {
Text(
text = sale.notes,
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF364153),
lineHeight = 20.sp
)
}
}
Spacer(modifier = Modifier.height(20.dp))
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
text = "Attachments",
fontSize = 18.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF000000),
lineHeight = 24.sp
)
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(11.dp)
) {
AttachmentButton(
name = sale.attachments[0].name,
modifier = Modifier.weight(1f)
)
AttachmentButton(
name = sale.attachments[1].name,
modifier = Modifier.weight(1f)
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(11.dp)
) {
AttachmentButton(
name = sale.attachments[2].name,
modifier = Modifier.weight(1f)
)
AttachmentButton(
name = sale.attachments[3].name,
modifier = Modifier.weight(1f)
)
}
}
}
Spacer(modifier = Modifier.height(24.dp))
}
}
}
}
@Composable
fun InfoField(
label: String,
value: String,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(1.987.dp)
) {
Text(
text = label,
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF6A7282),
lineHeight = 16.sp
)
Text(
text = value,
fontSize = 15.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF101828),
lineHeight = 20.sp
)
}
}
@Composable
fun AttachmentButton(
name: String,
modifier: Modifier = Modifier,
onClick: () -> Unit = {}
) {
Row(
modifier = modifier
.height(38.dp)
.border(
width = 1.078.dp,
color = Color(0xFFE5E7EB),
shape = RoundedCornerShape(20.dp)
)
.background(
color = Color.Transparent,
shape = RoundedCornerShape(20.dp)
)
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() },
onClick = onClick,
)
.padding(horizontal = 12.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = name,
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF4A5565),
lineHeight = 20.sp,
textDecoration = TextDecoration.Underline
)
Spacer(modifier = Modifier.width(5.995.dp))
Icon(
imageVector = Icons.Default.AttachFile,
contentDescription = "Attachment",
tint = Color(0xFFADADAD),
modifier = Modifier.size(19.99.dp)
)
}
}

View File

@ -0,0 +1,560 @@
package com.example.livingai_lg.ui.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Call
import androidx.compose.material.icons.filled.LocationOn
import androidx.compose.material.icons.filled.Message
import androidx.compose.material.icons.filled.Shield
import androidx.compose.material.icons.filled.Star
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
data class SellerProfile(
val name: String,
val initials: String,
val location: String,
val rating: Double,
val reviewCount: Int,
val trustScore: Int,
val totalSales: Int,
val yearsActive: String,
val responseRate: String,
val about: String
)
data class PastSale(
val id: String,
val name: String,
val emoji: String,
val age: String,
val breed: String,
val price: String,
val rating: Double
)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SellerProfileScreen(
sellerId: String = "1",
onBackClick: () -> Unit = {},
onCallClick: () -> Unit = {},
onMessageClick: () -> Unit = {},
onViewAllSalesClick: () -> Unit = {}
) {
// Sample data - in real app, fetch based on sellerId
val seller = SellerProfile(
name = "Ramesh Singh",
initials = "RS",
location = "Nagpur, Maharashtra",
rating = 4.8,
reviewCount = 156,
trustScore = 95,
totalSales = 47,
yearsActive = "5+",
responseRate = "98%",
about = "Experienced cattle farmer and trader with over 5 years in the business. Specializing in dairy cattle breeds including Gir, Holstein, and Jersey. All animals are well-maintained with complete vaccination records and health certificates."
)
val pastSales = listOf(
PastSale("1", "Gir Cow", "🐄", "Age: 3 yrs", "Holstein", "₹45,000", 5.0),
PastSale("2", "Buffalo", "🐃", "Age: 4 yrs", "Murrah", "₹55,000", 4.9),
PastSale("3", "Goat", "🐐", "Age: 2 yrs", "Sirohi", "₹12,000", 4.7)
)
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF7F4EE))
) {
// Header
TopAppBar(
title = {
Text(
text = "Seller Profile",
fontSize = 24.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF0A0A0A)
)
},
navigationIcon = {
IconButton(onClick = onBackClick) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "Back",
tint = Color(0xFF0A0A0A)
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color(0xFFF7F4EE)
),
modifier = Modifier.border(
width = 1.dp,
color = Color(0x1A000000),
shape = RectangleShape
)
)
// Scrollable Content
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(horizontal = 16.dp)
.padding(top = 16.dp, bottom = 24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// Profile Card
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
border = BorderStroke(1.dp, Color(0x1A000000))
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// Profile Info
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
// Avatar
Box(
modifier = Modifier
.size(80.dp)
.background(Color(0xFFE5E7EB), CircleShape),
contentAlignment = Alignment.Center
) {
Text(
text = seller.initials,
fontSize = 24.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A)
)
}
// Info
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = seller.name,
fontSize = 18.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A)
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Icon(
imageVector = Icons.Default.LocationOn,
contentDescription = "Location",
tint = Color(0xFF717182),
modifier = Modifier.size(12.dp)
)
Text(
text = seller.location,
fontSize = 14.sp,
color = Color(0xFF717182)
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Icon(
imageVector = Icons.Default.Star,
contentDescription = "Rating",
tint = Color(0xFFFDC700),
modifier = Modifier.size(16.dp)
)
Text(
text = seller.rating.toString(),
fontSize = 14.sp,
color = Color(0xFF0A0A0A)
)
}
Text(
text = "",
fontSize = 14.sp,
color = Color(0xFF717182)
)
Text(
text = "${seller.reviewCount} reviews",
fontSize = 14.sp,
color = Color(0xFF717182)
)
}
}
}
// AI Trust Score
Box(
modifier = Modifier
.fillMaxWidth()
.background(
Color(0x4D60A5FA),
RoundedCornerShape(8.dp)
)
.border(
1.dp,
Color(0x4D60A5FA),
RoundedCornerShape(8.dp)
)
.padding(12.dp)
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Icon(
imageVector = Icons.Default.Shield,
contentDescription = "Trust",
tint = Color(0xFF155DFC),
modifier = Modifier.size(20.dp)
)
Text(
text = "AI Trust Score",
fontSize = 14.sp,
color = Color(0xFF0A0A0A)
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Icon(
imageVector = Icons.Default.Star,
contentDescription = "Score",
tint = Color(0xFF155DFC),
modifier = Modifier.size(16.dp)
)
Text(
text = "${seller.trustScore}/100",
fontSize = 16.sp,
color = Color(0xFF155DFC)
)
}
}
Text(
text = "Verified seller with excellent transaction history",
fontSize = 12.sp,
color = Color(0xFF717182)
)
}
}
// Action Buttons
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Button(
onClick = onCallClick,
modifier = Modifier
.weight(1f)
.height(46.dp),
shape = RoundedCornerShape(23.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color.Black,
contentColor = Color.White
)
) {
Icon(
imageVector = Icons.Default.Call,
contentDescription = "Call",
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "Call",
fontSize = 14.sp,
fontWeight = FontWeight.Normal
)
}
OutlinedButton(
onClick = onMessageClick,
modifier = Modifier
.weight(1f)
.height(46.dp),
shape = RoundedCornerShape(23.dp),
border = BorderStroke(
1.dp,
Color(0x1A000000)
),
colors = ButtonDefaults.outlinedButtonColors(
containerColor = Color.Transparent,
contentColor = Color(0xFF0A0A0A)
)
) {
Icon(
imageVector = Icons.Default.Message,
contentDescription = "Message",
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "Message",
fontSize = 14.sp,
fontWeight = FontWeight.Normal
)
}
}
}
}
// Stats Row
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
StatCard(
value = seller.totalSales.toString(),
label = "Total Sales",
modifier = Modifier.weight(1f)
)
StatCard(
value = seller.yearsActive,
label = "Years Active",
modifier = Modifier.weight(1f)
)
StatCard(
value = seller.responseRate,
label = "Response\nRate",
modifier = Modifier.weight(1f)
)
}
// Past Sales Section
Column(
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Past Sales",
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A)
)
TextButton(onClick = onViewAllSalesClick) {
Text(
text = "View All",
fontSize = 14.sp,
color = Color(0xFF717182),
textDecoration = TextDecoration.Underline
)
}
}
pastSales.forEach { sale ->
PastSaleCard(sale)
}
}
// About Seller Section
Column(
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
text = "About Seller",
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A)
)
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
border = BorderStroke(1.dp, Color(0x1A000000))
) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(
text = seller.about,
fontSize = 14.sp,
lineHeight = 22.75.sp,
color = Color(0xFF717182)
)
}
}
}
}
}
}
@Composable
fun StatCard(
value: String,
label: String,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier.height(94.dp),
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
border = BorderStroke(1.dp, Color(0x1A000000))
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = value,
fontSize = 24.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A)
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = label,
fontSize = 12.sp,
color = Color(0xFF717182),
textAlign = TextAlign.Center
)
}
}
}
@Composable
fun PastSaleCard(sale: PastSale) {
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
border = BorderStroke(1.dp, Color(0x1A000000))
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Animal Icon
Box(
modifier = Modifier
.size(64.dp)
.background(Color(0xFFF7F4EE), RoundedCornerShape(8.dp)),
contentAlignment = Alignment.Center
) {
Text(
text = sale.emoji,
fontSize = 24.sp
)
}
// Details
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = sale.name,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A)
)
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
text = sale.age,
fontSize = 12.sp,
color = Color(0xFF717182)
)
Text(
text = sale.breed,
fontSize = 12.sp,
color = Color(0xFF717182)
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = sale.price,
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF0A0A0A)
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Icon(
imageVector = Icons.Default.Star,
contentDescription = "Rating",
tint = Color(0xFFFDC700),
modifier = Modifier.size(12.dp)
)
Text(
text = sale.rating.toString(),
fontSize = 12.sp,
color = Color(0xFF717182)
)
}
}
}
}
}
}

View File

@ -0,0 +1,168 @@
package com.example.livingai_lg.ui.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.example.livingai_lg.ui.models.SortDirection
import com.example.livingai_lg.ui.models.SortField
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.ui.components.SortItem
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SortScreen(
onBackClick: () -> Unit = {},
onSkipClick: () -> Unit = {},
onApplyClick: (List<SortField>) -> Unit = {},
onCancelClick: () -> Unit = {}
) {
var fields by remember {
mutableStateOf(
listOf(
SortField("distance", "Distance"),
SortField("price", "Price"),
SortField("milk", "Milk Capacity"),
SortField("ai", "AI Score"),
SortField("age", "Age")
)
)
}
fun toggleField(index: Int) {
val current = fields[index]
val nextDirection = when (current.direction) {
SortDirection.NONE -> SortDirection.ASC
SortDirection.ASC -> SortDirection.DESC
SortDirection.DESC -> SortDirection.NONE
}
val updated = fields.toMutableList()
updated[index] = current.copy(
direction = nextDirection,
order = if (nextDirection == SortDirection.NONE) null else current.order
)
// Recalculate sort order
val active = updated
.filter { it.direction != SortDirection.NONE }
.sortedBy { it.order ?: Int.MAX_VALUE }
.mapIndexed { i, item -> item.copy(order = i + 1) }
fields = updated.map { field ->
active.find { it.key == field.key } ?: field.copy(order = null)
}
}
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF7F4EE))
) {
// Header
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = onBackClick) {
Icon(Icons.Default.ArrowBack, null)
}
Text("Sort By", fontSize = 32.sp)
}
TextButton(onClick = onSkipClick) {
Text(
"Skip",
textDecoration = TextDecoration.Underline,
fontSize = 20.sp
)
}
}
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
fields.forEachIndexed { index, field ->
SortItem(
field = field,
onToggle = { toggleField(index) }
)
}
Spacer(modifier = Modifier.height(32.dp))
Button(
onClick = { onApplyClick(fields.filter { it.direction != SortDirection.NONE }) },
modifier = Modifier
.align(Alignment.CenterHorizontally)
.width(173.dp)
.height(50.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color.Black,
contentColor = Color.White
),
shape = RoundedCornerShape(25.dp)
) {
Text("Apply")
}
OutlinedButton(
onClick = onCancelClick,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.width(173.dp)
.height(50.dp),
colors = ButtonDefaults.outlinedButtonColors(
containerColor = Color.Transparent,
contentColor = Color(0xFF0A0A0A)
),
shape = RoundedCornerShape(25.dp)
) {
Text("Cancel")
}
Spacer(modifier = Modifier.height(24.dp))
}
}
}

View File

@ -0,0 +1,203 @@
package com.example.livingai_lg.ui.screens.auth
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.ui.components.backgrounds.DecorativeBackground
import com.example.livingai_lg.ui.components.IconCircle
import com.example.livingai_lg.ui.theme.FarmButtonPrimary
import com.example.livingai_lg.ui.theme.FarmButtonSecondary
import com.example.livingai_lg.ui.theme.FarmButtonText
import com.example.livingai_lg.ui.theme.FarmSproutBg
import com.example.livingai_lg.ui.theme.FarmSproutIcon
import com.example.livingai_lg.ui.theme.FarmSunBg
import com.example.livingai_lg.ui.theme.FarmSunIcon
import com.example.livingai_lg.ui.theme.FarmTextDark
import com.example.livingai_lg.ui.theme.FarmTextLight
import com.example.livingai_lg.ui.theme.FarmTextNormal
import com.example.livingai_lg.ui.theme.FarmWheatBg
import com.example.livingai_lg.ui.theme.FarmWheatIcon
import com.example.livingai_lg.R
@Composable
fun LandingScreen(
onSignUpClick: () -> Unit = {},
onSignInClick: () -> Unit = {},
onGuestClick: () -> Unit = {}
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(FarmTextLight)
) {
// Decorative background elements
DecorativeBackground()
// Main content
BoxWithConstraints(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 32.dp)
) {
val horizontalPadding = maxWidth * 0.06f // proportional padding
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(horizontal = horizontalPadding)
.padding( top = 56.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
//Spacer(modifier = Modifier.height(24.dp))
// Icon row
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.Bottom
) {
IconCircle(
backgroundColor = FarmWheatBg,
size = 56.dp
) {
Icon(
painter = painterResource(id = R.drawable.ic_wheat),
contentDescription = "Wheat Icon",
tint = FarmWheatIcon // keeps original vector colors
)
}
Spacer(modifier = Modifier.width(12.dp))
IconCircle(
backgroundColor = FarmSproutBg,
size = 64.dp
) {
Icon(
painter = painterResource(id = R.drawable.ic_sprout),
contentDescription = "Wheat Icon",
tint = FarmSproutIcon // keeps original vector colors
)
}
Spacer(modifier = Modifier.width(12.dp))
IconCircle(
backgroundColor = FarmSunBg,
size = 56.dp
) {
Icon(
painter = painterResource(id = R.drawable.ic_sun),
contentDescription = "Wheat Icon",
tint = FarmSunIcon // keeps original vector colors
)
}
}
Spacer(modifier = Modifier.height(48.dp))
// Welcome text
Text(
text = "Welcome!",
fontSize = 32.sp,
fontWeight = FontWeight.Medium,
color = FarmTextDark
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Join the farm marketplace community",
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = FarmTextNormal
)
Spacer(modifier = Modifier.height(48.dp))
// Sign up button
Button(
onClick = onSignUpClick,
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
shape = RoundedCornerShape(16.dp),
colors = ButtonDefaults.buttonColors(
containerColor = FarmButtonPrimary,
contentColor = FarmButtonText
),
elevation = ButtonDefaults.buttonElevation(
defaultElevation = 8.dp
)
) {
Text(
text = "New user? Sign up",
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
}
Spacer(modifier = Modifier.height(16.dp))
// Sign in button
Button(
onClick = onSignInClick,
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
shape = RoundedCornerShape(16.dp),
colors = ButtonDefaults.buttonColors(
containerColor = FarmButtonSecondary,
contentColor = FarmButtonText
),
elevation = ButtonDefaults.buttonElevation(
defaultElevation = 8.dp
)
) {
Text(
text = "Already a user? Sign in",
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
}
Spacer(modifier = Modifier.height(32.dp))
// Continue as guest
TextButton(
onClick = onGuestClick
) {
Text(
text = "Continue as Guest",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = FarmTextNormal,
textDecoration = TextDecoration.Underline
)
}
Spacer(modifier = Modifier.height(48.dp))
}
}
}
}

View File

@ -0,0 +1,287 @@
package com.example.livingai_lg.ui.screens.auth
import android.widget.Toast
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.api.AuthApiClient
import com.example.livingai_lg.api.AuthManager
import com.example.livingai_lg.api.TokenManager
import com.example.livingai_lg.ui.components.backgrounds.DecorativeBackground
import kotlin.math.min
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.platform.LocalFocusManager
import kotlinx.coroutines.launch
@Composable
fun OtpScreen(
phoneNumber: String,
name: String,
onSuccess: () -> Unit = {},
onCreateProfile: (name: String) -> Unit = {},
) {
val otp = remember { mutableStateOf("") }
val context = LocalContext.current.applicationContext
val scope = rememberCoroutineScope()
val authManager = remember { AuthManager(context, AuthApiClient(context), TokenManager(context)) }
// Flag to determine if this is a sign-in flow for an existing user.
val isSignInFlow = name == "existing_user"
BoxWithConstraints(
modifier = Modifier
.fillMaxSize()
.background(
Brush.linearGradient(
listOf(
Color(0xFFFFFBEA),
Color(0xFFFDFBE8),
Color(0xFFF7FEE7)
)
)
)
) {
DecorativeBackground()
val screenW = maxWidth.value
val screenH = maxHeight.value
// Figma design reference size from Flutter widget
val designW = 393.39f
val designH = 852.53f
val scale = min(screenW / designW, screenH / designH)
fun s(v: Float) = (v * scale).dp // dp scaling
fun fs(v: Float) = (v * scale).sp // font scaling
// ---------------------------
// "Enter OTP" Title
// ---------------------------
Text(
text = "Enter OTP",
color = Color(0xFF927B5E),
fontSize = fs(20f),
fontWeight = FontWeight.Medium,
modifier = Modifier.offset(x = s(139f), y = s(279f)),
style = LocalTextStyle.current.copy(
shadow = Shadow(
color = Color.Black.copy(alpha = 0.25f),
offset = Offset(0f, s(4f).value),
blurRadius = s(4f).value
)
)
)
// ---------------------------
// OTP 4-Box Input Row
// ---------------------------
// Row(
// Modifier.offset(x = s(38f), y = s(319f)),
// horizontalArrangement = Arrangement.spacedBy(s(17f))
// ) {
// repeat(4) { index ->
// OtpBox(
// index = index,
// otp = otp.value,
// onChange = { if (it.length <= 6) otp.value = it },
// scale = scale
// )
// }
// }
OtpInputRow(
otpLength = 6,
scale = scale,
otp = otp.value,
onOtpChange = { if (it.length <= 6) otp.value = it }
)
// ---------------------------
// Continue Button
// ---------------------------
Box(
modifier = Modifier
.offset(x = s(57f), y = s(411f))
.size(s(279.25f), s(55.99f))
.shadow(
elevation = s(6f),
ambientColor = Color.Black.copy(alpha = 0.10f),
shape = RoundedCornerShape(s(16f)),
)
.shadow(
elevation = s(15f),
ambientColor = Color.Black.copy(alpha = 0.10f),
shape = RoundedCornerShape(s(16f)),
)
.background(
Brush.horizontalGradient(
listOf(Color(0xFFFD9900), Color(0xFFE17100))
),
shape = RoundedCornerShape(s(16f))
)
.clickable(
indication = LocalIndication.current,
interactionSource = remember { MutableInteractionSource() },
onClick = {
scope.launch {
authManager.login(phoneNumber, otp.value)
.onSuccess { response ->
if (isSignInFlow) {
// For existing users, always go to the success screen.
onSuccess()
} else {
// For new users, check if a profile needs to be created.
if (response.needsProfile) {
onCreateProfile(name)
} else {
onSuccess()
}
}
}
.onFailure {
Toast.makeText(context, "Invalid or expired OTP", Toast.LENGTH_SHORT).show()
}
}
}
),
contentAlignment = Alignment.Center
) {
Text(
"Continue",
color = Color.White,
fontSize = fs(16f),
fontWeight = FontWeight.Medium
)
}
}
}
@Composable
fun OtpInputRow(
otpLength: Int = 4,
scale: Float,
otp: String,
onOtpChange: (String) -> Unit
) {
val focusRequesters = remember {
List(otpLength) { FocusRequester() }
}
Row(horizontalArrangement = Arrangement.spacedBy((12 * scale).dp)) {
repeat(otpLength) { index ->
OtpBox(
index = index,
otp = otp,
scale = scale,
focusRequester = focusRequesters[index],
onRequestFocus = {
val firstEmpty = otp.length.coerceAtMost(otpLength - 1)
focusRequesters[firstEmpty].requestFocus()
},
onNextFocus = {
if (index + 1 < otpLength) {
focusRequesters[index + 1].requestFocus()
}
},
onPrevFocus = {
if (index - 1 >= 0) {
focusRequesters[index - 1].requestFocus()
}
},
onChange = onOtpChange
)
}
}
}
@Composable
private fun OtpBox(
index: Int,
otp: String,
scale: Float,
focusRequester: FocusRequester,
onRequestFocus: () -> Unit,
onNextFocus: () -> Unit,
onPrevFocus: () -> Unit,
onChange: (String) -> Unit
) {
val boxW = 66f * scale
val boxH = 52f * scale
val radius = 16f * scale
val char = otp.getOrNull(index)?.toString() ?: ""
Box(
modifier = Modifier
.size(boxW.dp, boxH.dp)
.shadow((4f * scale).dp, RoundedCornerShape(radius.dp))
.background(Color.White, RoundedCornerShape(radius.dp))
.clickable { onRequestFocus() },
contentAlignment = Alignment.Center
) {
BasicTextField(
value = char,
onValueChange = { new ->
when {
// DIGIT ENTERED
new.matches(Regex("\\d")) -> {
val updated = otp.padEnd(index, ' ').toMutableList()
if (updated.size > index) updated[index] = new.first()
else updated.add(new.first())
onChange(updated.joinToString("").trim())
onNextFocus()
}
// BACKSPACE
new.isEmpty() -> {
if (char.isNotEmpty()) {
val updated = otp.toMutableList()
updated.removeAt(index)
onChange(updated.joinToString(""))
} else {
onPrevFocus()
}
}
}
},
textStyle = LocalTextStyle.current.copy(
fontSize = (24f * scale).sp,
fontWeight = FontWeight.Medium,
textAlign = TextAlign.Center
),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.NumberPassword
),
singleLine = true,
modifier = Modifier
.focusRequester(focusRequester)
.align(Alignment.Center)
)
}
}

View File

@ -0,0 +1,136 @@
package com.example.livingai_lg.ui.screens.auth
import android.widget.Toast
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.api.AuthApiClient
import com.example.livingai_lg.api.AuthManager
import com.example.livingai_lg.api.TokenManager
import com.example.livingai_lg.ui.components.backgrounds.DecorativeBackground
import com.example.livingai_lg.ui.components.PhoneNumberInput
import com.example.livingai_lg.ui.theme.FarmTextLight
import com.example.livingai_lg.ui.theme.FarmTextLink
import com.example.livingai_lg.ui.theme.FarmTextNormal
import com.example.livingai_lg.ui.components.FarmHeader
import com.example.livingai_lg.ui.utils.isKeyboardOpen
import kotlinx.coroutines.launch
@Composable
fun SignInScreen(
onSignUpClick: () -> Unit = {},
onSignInClick: (phone: String, name: String) -> Unit = {},
) {
val phoneNumber = remember { mutableStateOf("") }
val isValid = phoneNumber.value.length == 10
val context = LocalContext.current.applicationContext
val scope = rememberCoroutineScope()
val authManager = remember { AuthManager(context, AuthApiClient(context), TokenManager(context)) }
Box(
modifier = Modifier
.fillMaxSize()
.background(FarmTextLight)
) {
DecorativeBackground()
BoxWithConstraints(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 32.dp)
) {
val horizontalPadding = maxWidth * 0.06f // proportional padding
val keyboardOpen = isKeyboardOpen()
val topPadding by animateDpAsState(
targetValue = if (keyboardOpen) 40.dp else 140.dp,
animationSpec = tween(280, easing = FastOutSlowInEasing)
)
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(horizontal = horizontalPadding)
.padding( top = 40.dp),//topPadding),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = if (keyboardOpen) Arrangement.Top else Arrangement.Center
) {
//Spacer(Modifier.height(if (keyboardOpen) 32.dp else 56.dp))
// Header
Column(horizontalAlignment = Alignment.CenterHorizontally) {
FarmHeader()
}
Spacer(modifier = Modifier.height(48.dp))
PhoneNumberInput(
phone = phoneNumber.value,
onChange = { phoneNumber.value = it }
)
Spacer(modifier = Modifier.height(48.dp))
Button(
onClick = {
val fullPhoneNumber = "+91${phoneNumber.value}"
scope.launch {
authManager.requestOtp(fullPhoneNumber)
.onSuccess {
onSignInClick(fullPhoneNumber,"existing_user")
}
.onFailure {
Toast.makeText(context, "Failed to send OTP: ${it.message}", Toast.LENGTH_LONG).show()
}
}
},
enabled = isValid,
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
shape = RoundedCornerShape(16.dp),
colors = ButtonDefaults.buttonColors(
containerColor = if (isValid) Color(0xFFE17100) else Color(0xFFE17100).copy(
alpha = 0.4f
),
contentColor = Color.White
)
) {
Text("Sign In", fontSize = 16.sp, fontWeight = FontWeight.Medium)
}
Spacer(modifier = Modifier.height(32.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Don't have an account? ", fontSize = 16.sp, color = FarmTextNormal)
TextButton(onClick = onSignUpClick) {
Text("Sign up", fontSize = 16.sp, color = FarmTextLink)
}
}
Spacer(modifier = Modifier.height(48.dp))
}
}
}
}

View File

@ -0,0 +1,203 @@
package com.example.livingai_lg.ui.screens.auth
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.livingai_lg.api.AuthApiClient
import com.example.livingai_lg.api.AuthManager
import com.example.livingai_lg.api.TokenManager
import com.example.livingai_lg.ui.components.backgrounds.DecorativeBackground
import com.example.livingai_lg.ui.components.DropdownInput
import com.example.livingai_lg.ui.components.InputField
import com.example.livingai_lg.ui.components.PhoneNumberInput
import com.example.livingai_lg.ui.theme.FarmTextLight
import com.example.livingai_lg.ui.theme.FarmTextNormal
import com.example.livingai_lg.ui.components.FarmHeader
import com.example.livingai_lg.ui.utils.isKeyboardOpen
import kotlinx.coroutines.launch
data class SignUpFormData(
val name: String = "",
val state: String = "",
val district: String = "",
val village: String = "",
val phoneNumber: String = ""
){
val isValid: Boolean
get() =
name.isNotBlank() &&
state.isNotBlank() &&
district.isNotBlank() &&
village.isNotBlank() &&
phoneNumber.isValidPhoneNumber()
}
private fun String.isValidPhoneNumber(): Boolean {
return length == 10 && all { it.isDigit() }
}
@Composable
fun SignUpScreen(
onSignInClick: () -> Unit = {},
onSignUpClick: (phone: String, name: String) -> Unit = {}
) {
var formData by remember { mutableStateOf(SignUpFormData()) }
var showStateDropdown by remember { mutableStateOf(false) }
var showDistrictDropdown by remember { mutableStateOf(false) }
var showVillageDropdown by remember { mutableStateOf(false) }
val states = listOf("Maharashtra", "Gujarat", "Tamil Nadu", "Karnataka", "Rajasthan")
val districts = listOf("Mumbai", "Pune", "Nagpur", "Aurangabad", "Nashik")
val villages = listOf("Village 1", "Village 2", "Village 3", "Village 4", "Village 5")
val context = LocalContext.current.applicationContext
val scope = rememberCoroutineScope()
// Use 10.0.2.2 to connect to host machine's localhost from emulator
val authManager = remember { AuthManager(context, AuthApiClient(context), TokenManager(context)) }
Box(
modifier = Modifier
.fillMaxSize()
.background(FarmTextLight)
) {
DecorativeBackground()
BoxWithConstraints(
modifier = Modifier
.fillMaxSize()
.padding(vertical = 32.dp)
) {
val horizontalPadding = maxWidth * 0.06f // proportional padding
val keyboardOpen = isKeyboardOpen()
// val topPadding by animateDpAsState(
// targetValue = if (keyboardOpen) 40.dp else 140.dp,
// animationSpec = tween(280, easing = FastOutSlowInEasing)
// )
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(horizontal = horizontalPadding)
.padding( top = 40.dp),//topPadding),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = if (keyboardOpen) Arrangement.Top else Arrangement.Center
) {
// Spacer(Modifier.height(if (keyboardOpen) 32.dp else 56.dp))
// Title
FarmHeader()
Spacer(modifier = Modifier.height(32.dp))
// Name Input
InputField(
label = "Enter Name*",
value = formData.name,
placeholder = "Enter your name",
onChange = { formData = formData.copy(name = it) }
)
Spacer(modifier = Modifier.height(24.dp))
// State + District Row
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
DropdownInput(
label = "Enter Location",
selected = formData.state,
options = states,
expanded = showStateDropdown,
onExpandedChange = { showStateDropdown = it },
onSelect = { formData = formData.copy(state = it) },
placeholder = "Select State",
modifier = Modifier.weight(0.4f) // <--- half width
)
DropdownInput(
selected = formData.district,
options = districts,
expanded = showDistrictDropdown,
onExpandedChange = { showDistrictDropdown = it },
onSelect = { formData = formData.copy(district = it) },
placeholder = "Select District",
modifier = Modifier.weight(0.6f) // <--- half width
)
}
Spacer(modifier = Modifier.height(24.dp))
// Village Dropdown
DropdownInput(
label = "Enter Village/Place",
selected = formData.village,
options = villages,
expanded = showVillageDropdown,
onExpandedChange = { showVillageDropdown = it },
onSelect = {
formData = formData.copy(village = it); showVillageDropdown = false
}
)
Spacer(modifier = Modifier.height(24.dp))
// Phone Number
PhoneNumberInput(
phone = formData.phoneNumber,
onChange = { formData = formData.copy(phoneNumber = it) }
)
Spacer(modifier = Modifier.height(40.dp))
// Sign Up button
Button(
onClick = {
val fullPhoneNumber = "+91${formData.phoneNumber}"
scope.launch {
authManager.requestOtp(fullPhoneNumber)
.onSuccess {
onSignUpClick(fullPhoneNumber,formData.name)
}
.onFailure {
Toast.makeText(context, "Failed to send OTP: ${it.message}", Toast.LENGTH_LONG).show()
}
}
},
modifier = Modifier.fillMaxWidth().height(56.dp),
enabled = formData.isValid,
shape = RoundedCornerShape(16.dp),
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFE17100))
) {
Text("Sign Up", fontSize = 16.sp)
}
Spacer(modifier = Modifier.height(24.dp))
// Already have account?
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Already have an account? ", color = FarmTextNormal)
TextButton(onClick = onSignInClick) {
Text("Sign In", color = Color(0xFFE17100))
}
}
Spacer(modifier = Modifier.height(40.dp))
}
}
}
}

View File

@ -2,25 +2,57 @@ package com.example.livingai_lg.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
// Farm Marketplace Color Palette
val FarmTextLight = Color(0xFFF5F1E8)
val FarmTextDark = Color(0xFF5D4E37) //Title
val FarmTextNormal = Color(0xFF8B7355)
val FarmTextLink = Color(0xFFE17100)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)
val FarmSproutBg = Color(0xFFFFCB79)
val FarmSproutIcon = Color(0xFF8B6F47)
// Custom Colors from Figma
val LightCream = Color(0xFFFFFBEB)
val LighterCream = Color(0xFFFEFCE8)
val LightestGreen = Color(0xFFF7FEE7)
val Maize = Color(0xFFF7C35F)
val TerraCotta = Color(0xFFD97D5D)
val DarkBrown = Color(0xFF5D4E37)
val MidBrown = Color(0xFF8B7355)
val DarkerBrown = Color(0xFF322410)
val OrangeBrown = Color(0xFFD4A574)
val DarkOrange = Color(0xFFE07A5F)
val Gold = Color(0xFFFFDD88)
val LightOrange = Color(0xFFFFB84D)
val DarkerOrange = Color(0xFFD96A4F)
val FarmWheatBg = Color(0xFFFFDD88)
val FarmWheatIcon = Color(0xFFD4A574)
val FarmSunBg = Color(0xFFFFB179)
val FarmSunIcon = Color(0xFFE07A5F)
val FarmGold = Color(0xFFFFB84D)
val FarmPink = Color(0xFFEDD5C8)
val FarmFence = Color(0xFFD4B5A0)
val FarmBox = Color(0xFFE8D9CC)
val FarmButtonPrimary = Color(0xFFFFB84D)
val FarmButtonSecondary = Color(0xFFE07A5F)
val FarmButtonText = Color(0xFF322410)
// Material Design 3 Color Scheme
val md_theme_light_primary = Color(0xFF5D4E37)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFF5F1E8)
val md_theme_light_onPrimaryContainer = Color(0xFF3E3220)
val md_theme_light_secondary = Color(0xFF8B7355)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFFFDDBD)
val md_theme_light_onSecondaryContainer = Color(0xFF2B2415)
val md_theme_light_tertiary = Color(0xFFE07A5F)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFFFDDBD)
val md_theme_light_onTertiaryContainer = Color(0xFF3E2415)
val md_theme_light_error = Color(0xFFB3261E)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_errorContainer = Color(0xFFF9DEDC)
val md_theme_light_onErrorContainer = Color(0xFF410E0B)
val md_theme_light_background = Color(0xFFFBF8F3)
val md_theme_light_onBackground = Color(0xFF1C1B1F)
val md_theme_light_surface = Color(0xFFFBF8F3)
val md_theme_light_onSurface = Color(0xFF1C1B1F)
val md_theme_light_surfaceVariant = Color(0xFFEFE0D6)
val md_theme_light_onSurfaceVariant = Color(0xFF49454E)
val md_theme_light_outline = Color(0xFF79747E)
val md_theme_light_inverseOnSurface = Color(0xFFF4EFF4)
val md_theme_light_inverseSurface = Color(0xFF313033)
val md_theme_light_inversePrimary = Color(0xFFFFDDBD)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF5D4E37)
val md_theme_light_outlineVariant = Color(0xFFD3C4BC)
val md_theme_light_scrim = Color(0xFF000000)

View File

@ -3,56 +3,111 @@ package com.example.livingai_lg.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
private val lightScheme = lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
onError = md_theme_light_onError,
errorContainer = md_theme_light_errorContainer,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
private val darkScheme = darkColorScheme(
primary = Color(0xFFFFDDBD),
onPrimary = Color(0xFF3E3220),
primaryContainer = Color(0xFF57472D),
onPrimaryContainer = Color(0xFFFFDDBD),
secondary = Color(0xFFFFDDBD),
onSecondary = Color(0xFF2B2415),
secondaryContainer = Color(0xFF6B5344),
onSecondaryContainer = Color(0xFFFFDDBD),
tertiary = Color(0xFFFFB893),
onTertiary = Color(0xFF3E2415),
tertiaryContainer = Color(0xFF8B5A3C),
onTertiaryContainer = Color(0xFFFFDDBD),
error = Color(0xFFFFB4AB),
onError = Color(0xFF690005),
errorContainer = Color(0xFF93000A),
onErrorContainer = Color(0xFFFFDAD6),
background = Color(0xFF1C1B1F),
onBackground = Color(0xFFE7E0E8),
surface = Color(0xFF1C1B1F),
onSurface = Color(0xFFE7E0E8),
surfaceVariant = Color(0xFF49454E),
onSurfaceVariant = Color(0xFFCAC7D0),
outline = Color(0xFF94919A),
inverseOnSurface = Color(0xFF1C1B1F),
inverseSurface = Color(0xFFE7E0E8),
inversePrimary = Color(0xFF5D4E37),
surfaceTint = Color(0xFFFFDDBD),
outlineVariant = Color(0xFF49454E),
scrim = Color(0xFF000000),
)
@Composable
fun LivingAi_LgTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
fun FarmMarketplaceTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
if (useDarkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
useDarkTheme -> darkScheme
else -> lightScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view)?.isAppearanceLightStatusBars =
!useDarkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
typography = typography,
content = content
)
}
}

View File

@ -6,29 +6,110 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
val typography = Typography(
displayLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 57.sp,
lineHeight = 64.sp,
letterSpacing = (-0.25).sp,
),
displayMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 45.sp,
lineHeight = 52.sp,
letterSpacing = 0.sp,
),
displaySmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 36.sp,
lineHeight = 44.sp,
letterSpacing = 0.sp,
),
headlineLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 32.sp,
lineHeight = 40.sp,
letterSpacing = 0.sp,
),
headlineMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 28.sp,
lineHeight = 36.sp,
letterSpacing = 0.sp,
),
headlineSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 24.sp,
lineHeight = 32.sp,
letterSpacing = 0.sp,
),
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp,
),
titleMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.15.sp,
),
titleSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.1.sp,
),
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
letterSpacing = 0.15.sp,
),
bodyMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.25.sp,
),
bodySmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.4.sp,
),
labelLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.1.sp,
),
labelMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp,
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)
letterSpacing = 0.5.sp,
),
)

View File

@ -0,0 +1,61 @@
package com.example.livingai_lg.ui.utils
import java.text.DecimalFormat
import kotlin.math.roundToInt
import java.text.NumberFormat
import java.util.Locale
class FormatUtils {
}
fun formatter(): NumberFormat{
return NumberFormat.getInstance(Locale.forLanguageTag("en-IN"))
}
fun formatViews(views: Long?): String {
val count = views ?: 0
val formattedNumber = formatter().format(count)
return when (count) {
1L -> "$formattedNumber View"
else -> "$formattedNumber Views"
}
}
fun formatPrice(price: Long?): String {
val value = price ?: 0
val formattedNumber = formatter().format(value)
return "$formattedNumber"
}
fun formatDistance(distanceMeters: Long?): String {
val meters = distanceMeters ?: 0L
return if (meters < 1_000) {
val unit = if (meters == 1L) "mt" else "mts"
"$meters $unit away"
} else {
val km = meters / 1_000.0
val formatter = DecimalFormat("#.#") // 1 decimal if needed
val formattedKm = formatter.format(km)
val unit = if (formattedKm == "1") "km" else "kms"
"$formattedKm $unit away"
}
}
fun formatAge(months: Int?): String {
val value = months ?: 0
val years = value / 12f
val roundedYears = (years * 10).roundToInt() / 10f
val unit = if (roundedYears == 1f) "Year" else "Years"
return "$roundedYears $unit"
}

View File

@ -0,0 +1,12 @@
package com.example.livingai_lg.ui.utils
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.ime
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalDensity
@Composable
fun isKeyboardOpen(): Boolean {
val density = LocalDensity.current
return WindowInsets.ime.getBottom(density) > 0
}

View File

@ -0,0 +1,51 @@
package com.example.livingai_lg.ui.utils
import android.content.Context
import android.net.Uri
import androidx.core.content.FileProvider
import java.io.File
import java.util.UUID
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever
import com.example.livingai_lg.ui.models.MediaType
fun createMediaUri(
context: Context,
type: MediaType
): Uri {
val directory = when (type) {
MediaType.PHOTO -> File(context.cacheDir, "images")
MediaType.VIDEO -> File(context.cacheDir, "videos")
}
if (!directory.exists()) {
directory.mkdirs()
}
val fileName = when (type) {
MediaType.PHOTO -> "${UUID.randomUUID()}.jpg"
MediaType.VIDEO -> "${UUID.randomUUID()}.mp4"
}
val file = File(directory, fileName)
return FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
file
)
}
fun getVideoThumbnail(
context: Context,
uri: Uri
): Bitmap? {
return try {
val retriever = MediaMetadataRetriever()
retriever.setDataSource(context, uri)
retriever.getFrameAtTime(0)
} catch (e: Exception) {
null
}
}

View File

@ -0,0 +1,180 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="393dp"
android:height="852dp"
android:viewportWidth="393"
android:viewportHeight="852">
<group>
<clip-path
android:pathData="M0,0h393v852h-393z"/>
<path
android:pathData="M0,0h393v852h-393z"
android:fillColor="#ffffff"/>
<path
android:pathData="M0,0h393v852h-393z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="0"
android:startY="0"
android:endX="648.1"
android:endY="298.95"
android:type="linear">
<item android:offset="0" android:color="#FFFFFBEB"/>
<item android:offset="1" android:color="#FFEFF6FF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M0,0h393.39v852.53h-393.39z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="0"
android:startY="0"
android:endX="648.67"
android:endY="299.32"
android:type="linear">
<item android:offset="0" android:color="#FFFFFBEB"/>
<item android:offset="0.5" android:color="#FFFEFCE8"/>
<item android:offset="1" android:color="#FFF7FEE7"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M32.11,128.78H111.87V230.77C111.87,236.29 107.39,240.77 101.87,240.77H42.11C36.59,240.77 32.11,236.29 32.11,230.77V128.78Z"
android:strokeAlpha="0.15"
android:fillColor="#C10007"
android:fillAlpha="0.09"/>
<path
android:pathData="M56.11,138.78C56.11,133.26 60.59,128.78 66.11,128.78H77.87C83.4,128.78 87.87,133.26 87.87,138.78V176.77H56.11V138.78Z"
android:strokeAlpha="0.15"
android:fillColor="#7B3306"
android:fillAlpha="0.09"/>
<path
android:pathData="M16,690.55C16,688.34 17.79,686.55 20,686.55C22.21,686.55 24,688.34 24,690.55V730.55C24,732.76 22.21,734.55 20,734.55C17.79,734.55 16,732.76 16,730.55V690.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M29.99,690.55C29.99,688.34 31.78,686.55 33.99,686.55C36.2,686.55 37.99,688.34 37.99,690.55V730.55C37.99,732.76 36.2,734.55 33.99,734.55C31.78,734.55 29.99,732.76 29.99,730.55V690.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M61.99,690.55C61.99,688.34 63.78,686.55 65.99,686.55C68.2,686.55 69.99,688.34 69.99,690.55V730.55C69.99,732.76 68.2,734.55 65.99,734.55C63.78,734.55 61.99,732.76 61.99,730.55V690.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M75.98,690.55C75.98,688.34 77.77,686.55 79.98,686.55C82.19,686.55 83.98,688.34 83.98,690.55V730.55C83.98,732.76 82.19,734.55 79.98,734.55C77.77,734.55 75.98,732.76 75.98,730.55V690.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M107.98,690.55C107.98,688.34 109.77,686.55 111.98,686.55C114.19,686.55 115.98,688.34 115.98,690.55V730.55C115.98,732.76 114.19,734.55 111.98,734.55C109.77,734.55 107.98,732.76 107.98,730.55V690.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M121.98,690.55C121.98,688.34 123.77,686.55 125.97,686.55C128.18,686.55 129.98,688.34 129.98,690.55V730.55C129.98,732.76 128.18,734.55 125.97,734.55C123.77,734.55 121.98,732.76 121.98,730.55V690.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M153.97,690.55C153.97,688.34 155.76,686.55 157.97,686.55C160.18,686.55 161.97,688.34 161.97,690.55V730.55C161.97,732.76 160.18,734.55 157.97,734.55C155.76,734.55 153.97,732.76 153.97,730.55V690.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M167.97,690.55C167.97,688.34 169.76,686.55 171.97,686.55C174.18,686.55 175.97,688.34 175.97,690.55V730.55C175.97,732.76 174.18,734.55 171.97,734.55C169.76,734.55 167.97,732.76 167.97,730.55V690.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M199.96,690.55C199.96,688.34 201.76,686.55 203.96,686.55C206.17,686.55 207.96,688.34 207.96,690.55V730.55C207.96,732.76 206.17,734.55 203.96,734.55C201.76,734.55 199.96,732.76 199.96,730.55V690.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M213.96,690.55C213.96,688.34 215.75,686.55 217.96,686.55C220.17,686.55 221.96,688.34 221.96,690.55V730.55C221.96,732.76 220.17,734.55 217.96,734.55C215.75,734.55 213.96,732.76 213.96,730.55V690.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M245.96,690.55C245.96,688.34 247.75,686.55 249.96,686.55C252.16,686.55 253.95,688.34 253.95,690.55V730.55C253.95,732.76 252.16,734.55 249.96,734.55C247.75,734.55 245.96,732.76 245.96,730.55V690.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M259.95,690.55C259.95,688.34 261.74,686.55 263.95,686.55C266.16,686.55 267.95,688.34 267.95,690.55V730.55C267.95,732.76 266.16,734.55 263.95,734.55C261.74,734.55 259.95,732.76 259.95,730.55V690.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M16,707.55C16,705.9 17.34,704.55 19,704.55H374.4C376.05,704.55 377.39,705.9 377.39,707.55C377.39,709.21 376.05,710.55 374.4,710.55H19C17.34,710.55 16,709.21 16,707.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M16,721.55C16,719.89 17.34,718.55 19,718.55H374.4C376.05,718.55 377.39,719.89 377.39,721.55C377.39,723.2 376.05,724.54 374.4,724.54H19C17.34,724.54 16,723.2 16,721.55Z"
android:strokeAlpha="0.2"
android:fillColor="#973C00"
android:fillAlpha="0.12"/>
<path
android:pathData="M241.41,618.56C241.41,613.04 245.88,608.56 251.41,608.56H279.4C284.93,608.56 289.4,613.04 289.4,618.56V638.56C289.4,644.08 284.93,648.56 279.4,648.56H251.41C245.88,648.56 241.41,644.08 241.41,638.56V618.56Z"
android:strokeAlpha="0.15"
android:fillColor="#D08700"
android:fillAlpha="0.105"/>
<path
android:pathData="M297.4,618.56C297.4,613.04 301.88,608.56 307.4,608.56H335.4C340.92,608.56 345.4,613.04 345.4,618.56V638.56C345.4,644.08 340.92,648.56 335.4,648.56H307.4C301.88,648.56 297.4,644.08 297.4,638.56V618.56Z"
android:strokeAlpha="0.15"
android:fillColor="#A65F00"
android:fillAlpha="0.105"/>
<path
android:pathData="M241.41,662.55C241.41,657.03 245.88,652.55 251.41,652.55H279.4C284.93,652.55 289.4,657.03 289.4,662.55V682.55C289.4,688.07 284.93,692.55 279.4,692.55H251.41C245.88,692.55 241.41,688.07 241.41,682.55V662.55Z"
android:strokeAlpha="0.15"
android:fillColor="#D08700"
android:fillAlpha="0.105"/>
<path
android:pathData="M63.99,622.07H123.99V562.07H63.99V622.07Z"
android:strokeAlpha="0.12"
android:fillColor="#33FFFFFF"
android:fillAlpha="0.12"/>
<path
android:pathData="M264.9,203.67H312.9V155.67H264.9V203.67Z"
android:strokeAlpha="0.1"
android:fillColor="#33FFFFFF"
android:fillAlpha="0.1"/>
<path
android:pathData="M79.99,770.99H115.99V734.99H79.99V770.99Z"
android:strokeAlpha="0.15"
android:fillColor="#33FFFFFF"
android:fillAlpha="0.15"/>
<path
android:pathData="M123.56,762.54H153.56V732.54H123.56V762.54Z"
android:strokeAlpha="0.15"
android:fillColor="#33FFFFFF"
android:fillAlpha="0.15"/>
<path
android:pathData="M161.74,770.99H197.74V734.99H161.74V770.99Z"
android:strokeAlpha="0.15"
android:fillColor="#33FFFFFF"
android:fillAlpha="0.15"/>
<path
android:pathData="M281.4,79.99C281.4,62.32 295.73,48 313.4,48C331.07,48 345.4,62.32 345.4,79.99C345.4,97.66 331.07,111.99 313.4,111.99C295.73,111.99 281.4,97.66 281.4,79.99Z"
android:strokeAlpha="0.2"
android:fillColor="#FDC700"
android:fillAlpha="0.12"/>
<path
android:pathData="M115.94,127.99C115.94,119.15 123.1,111.99 131.94,111.99H147.94C156.77,111.99 163.93,119.15 163.93,127.99C163.93,136.82 156.77,143.99 147.94,143.99H131.94C123.1,143.99 115.94,136.82 115.94,127.99Z"
android:fillColor="#ffffff"
android:fillAlpha="0.4"/>
<path
android:pathData="M167.93,128C167.93,116.95 176.88,108 187.92,108H203.92C214.97,108 223.92,116.95 223.92,128C223.92,139.04 214.97,147.99 203.92,147.99H187.92C176.88,147.99 167.93,139.04 167.93,128Z"
android:fillColor="#ffffff"
android:fillAlpha="0.4"/>
<path
android:pathData="M227.91,127.99C227.91,119.15 235.07,111.99 243.91,111.99H251.91C260.74,111.99 267.91,119.15 267.91,127.99C267.91,136.82 260.74,143.99 251.91,143.99H243.91C235.07,143.99 227.91,136.82 227.91,127.99Z"
android:fillColor="#ffffff"
android:fillAlpha="0.4"/>
</group>
</vector>

View File

@ -0,0 +1,149 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="393dp"
android:height="852dp"
android:viewportWidth="393"
android:viewportHeight="852">
<path
android:pathData="M341.53,596.63V574.64"
android:strokeWidth="1.2"
android:fillColor="#00000000"
android:strokeColor="#B5A89F"
android:strokeLineCap="round"/>
<path
android:pathData="M339.69,581.64C340.96,581.64 341.99,579.85 341.99,577.64C341.99,575.43 340.96,573.64 339.69,573.64C338.42,573.64 337.39,575.43 337.39,577.64C337.39,579.85 338.42,581.64 339.69,581.64Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M343.37,581.64C344.64,581.64 345.68,579.85 345.68,577.64C345.68,575.43 344.64,573.64 343.37,573.64C342.1,573.64 341.07,575.43 341.07,577.64C341.07,579.85 342.1,581.64 343.37,581.64Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M339.23,586.63C340.5,586.63 341.53,584.84 341.53,582.64C341.53,580.43 340.5,578.64 339.23,578.64C337.96,578.64 336.93,580.43 336.93,582.64C336.93,584.84 337.96,586.63 339.23,586.63Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M343.83,586.63C345.11,586.63 346.14,584.84 346.14,582.64C346.14,580.43 345.11,578.64 343.83,578.64C342.56,578.64 341.53,580.43 341.53,582.64C341.53,584.84 342.56,586.63 343.83,586.63Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M339.69,592.63C340.96,592.63 341.99,590.84 341.99,588.63C341.99,586.42 340.96,584.64 339.69,584.64C338.42,584.64 337.39,586.42 337.39,588.63C337.39,590.84 338.42,592.63 339.69,592.63Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M343.37,592.63C344.64,592.63 345.68,590.84 345.68,588.63C345.68,586.42 344.64,584.64 343.37,584.64C342.1,584.64 341.07,586.42 341.07,588.63C341.07,590.84 342.1,592.63 343.37,592.63Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M297.79,56.96H274.77C274.01,56.96 273.39,57.64 273.39,58.46V77.45C273.39,78.28 274.01,78.95 274.77,78.95H297.79C298.56,78.95 299.17,78.28 299.17,77.45V58.46C299.17,57.64 298.56,56.96 297.79,56.96Z"
android:strokeWidth="1.3"
android:fillColor="#00000000"
android:strokeColor="#B5A89F"/>
<path
android:pathData="M270.63,56.96L286.28,42.97L301.94,56.96"
android:strokeWidth="1.3"
android:fillColor="#00000000"
android:strokeColor="#B5A89F"/>
<path
android:strokeWidth="1"
android:pathData="M289.23,64.96H281.5C281.09,64.96 280.76,65.32 280.76,65.76V78.15C280.76,78.59 281.09,78.95 281.5,78.95H289.23C289.64,78.95 289.97,78.59 289.97,78.15V65.76C289.97,65.32 289.64,64.96 289.23,64.96Z"
android:fillColor="#00000000"
android:strokeColor="#B5A89F"/>
<path
android:pathData="M41.35,543.66C51.52,543.66 59.77,537.4 59.77,529.67C59.77,521.94 51.52,515.68 41.35,515.68C31.18,515.68 22.94,521.94 22.94,529.67C22.94,537.4 31.18,543.66 41.35,543.66Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M32.14,535.67C37.74,535.67 42.27,530.3 42.27,523.67C42.27,517.05 37.74,511.68 32.14,511.68C26.55,511.68 22.02,517.05 22.02,523.67C22.02,530.3 26.55,535.67 32.14,535.67Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M33.53,542.41C33.53,541.45 32.8,540.66 31.91,540.66C31.02,540.66 30.3,541.45 30.3,542.41V550.91C30.3,551.87 31.02,552.66 31.91,552.66C32.8,552.66 33.53,551.87 33.53,550.91V542.41Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M39.97,542.41C39.97,541.45 39.25,540.66 38.36,540.66C37.47,540.66 36.75,541.45 36.75,542.41V550.91C36.75,551.87 37.47,552.66 38.36,552.66C39.25,552.66 39.97,551.87 39.97,550.91V542.41Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M46.42,542.41C46.42,541.45 45.7,540.66 44.81,540.66C43.92,540.66 43.19,541.45 43.19,542.41V550.91C43.19,551.87 43.92,552.66 44.81,552.66C45.7,552.66 46.42,551.87 46.42,550.91V542.41Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M52.86,542.41C52.86,541.45 52.14,540.66 51.25,540.66C50.36,540.66 49.64,541.45 49.64,542.41V550.91C49.64,551.87 50.36,552.66 51.25,552.66C52.14,552.66 52.86,551.87 52.86,550.91V542.41Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M13.73,156.4V133.42"
android:strokeWidth="1.3"
android:fillColor="#00000000"
android:strokeColor="#A8AFA0"
android:strokeLineCap="round"/>
<path
android:pathData="M11.89,140.91C13.16,140.91 14.19,138.9 14.19,136.41C14.19,133.93 13.16,131.92 11.89,131.92C10.62,131.92 9.59,133.93 9.59,136.41C9.59,138.9 10.62,140.91 11.89,140.91Z"
android:fillColor="#A8AFA0"/>
<path
android:pathData="M15.57,140.91C16.84,140.91 17.87,138.9 17.87,136.41C17.87,133.93 16.84,131.92 15.57,131.92C14.3,131.92 13.27,133.93 13.27,136.41C13.27,138.9 14.3,140.91 15.57,140.91Z"
android:fillColor="#A8AFA0"/>
<path
android:pathData="M11.43,146.91C12.7,146.91 13.73,144.89 13.73,142.41C13.73,139.93 12.7,137.91 11.43,137.91C10.16,137.91 9.13,139.93 9.13,142.41C9.13,144.89 10.16,146.91 11.43,146.91Z"
android:fillColor="#A8AFA0"/>
<path
android:pathData="M16.03,146.91C17.3,146.91 18.33,144.89 18.33,142.41C18.33,139.93 17.3,137.91 16.03,137.91C14.76,137.91 13.73,139.93 13.73,142.41C13.73,144.89 14.76,146.91 16.03,146.91Z"
android:fillColor="#A8AFA0"/>
<path
android:pathData="M11.89,153.9C13.16,153.9 14.19,151.89 14.19,149.41C14.19,146.92 13.16,144.91 11.89,144.91C10.62,144.91 9.59,146.92 9.59,149.41C9.59,151.89 10.62,153.9 11.89,153.9Z"
android:fillColor="#A8AFA0"/>
<path
android:pathData="M15.57,153.9C16.84,153.9 17.87,151.89 17.87,149.41C17.87,146.92 16.84,144.91 15.57,144.91C14.3,144.91 13.27,146.92 13.27,149.41C13.27,151.89 14.3,153.9 15.57,153.9Z"
android:fillColor="#A8AFA0"/>
<path
android:pathData="M273.85,741.54H248.99C248.23,741.54 247.61,742.21 247.61,743.04V764.02C247.61,764.85 248.23,765.52 248.99,765.52H273.85C274.62,765.52 275.23,764.85 275.23,764.02V743.04C275.23,742.21 274.62,741.54 273.85,741.54Z"
android:strokeWidth="1.4"
android:fillColor="#00000000"
android:strokeColor="#A8AFA0"/>
<path
android:pathData="M244.85,741.54L261.42,726.55L278,741.54"
android:strokeWidth="1.4"
android:fillColor="#00000000"
android:strokeColor="#A8AFA0"/>
<path
android:pathData="M265.29,751.53H256.64C256.23,751.53 255.9,751.89 255.9,752.33V764.72C255.9,765.16 256.23,765.52 256.64,765.52H265.29C265.7,765.52 266.03,765.16 266.03,764.72V752.33C266.03,751.89 265.7,751.53 265.29,751.53Z"
android:strokeWidth="1.1"
android:fillColor="#00000000"
android:strokeColor="#A8AFA0"/>
<path
android:strokeWidth="1"
android:pathData="M270.63,738.54C272.16,738.54 273.39,737.2 273.39,735.54C273.39,733.89 272.16,732.54 270.63,732.54C269.11,732.54 267.87,733.89 267.87,735.54C267.87,737.2 269.11,738.54 270.63,738.54Z"
android:fillColor="#00000000"
android:strokeColor="#A8AFA0"/>
<path
android:pathData="M351.66,245.85C359.8,245.85 366.39,240.92 366.39,234.85C366.39,228.78 359.8,223.86 351.66,223.86C343.52,223.86 336.93,228.78 336.93,234.85C336.93,240.92 343.52,245.85 351.66,245.85Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M344.29,239.85C348.87,239.85 352.58,235.82 352.58,230.86C352.58,225.89 348.87,221.86 344.29,221.86C339.72,221.86 336.01,225.89 336.01,230.86C336.01,235.82 339.72,239.85 344.29,239.85Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M345.21,245.35C345.21,244.52 344.6,243.85 343.83,243.85C343.07,243.85 342.45,244.52 342.45,245.35V252.34C342.45,253.17 343.07,253.84 343.83,253.84C344.6,253.84 345.21,253.17 345.21,252.34V245.35Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M350.74,245.35C350.74,244.52 350.12,243.85 349.36,243.85C348.6,243.85 347.98,244.52 347.98,245.35V252.34C347.98,253.17 348.6,253.84 349.36,253.84C350.12,253.84 350.74,253.17 350.74,252.34V245.35Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M356.27,245.35C356.27,244.52 355.65,243.85 354.88,243.85C354.12,243.85 353.5,244.52 353.5,245.35V252.34C353.5,253.17 354.12,253.84 354.88,253.84C355.65,253.84 356.27,253.17 356.27,252.34V245.35Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M361.79,245.35C361.79,244.52 361.17,243.85 360.41,243.85C359.65,243.85 359.03,244.52 359.03,245.35V252.34C359.03,253.17 359.65,253.84 360.41,253.84C361.17,253.84 361.79,253.17 361.79,252.34V245.35Z"
android:fillColor="#B5A89F"/>
<path
android:pathData="M32.14,799V780.01"
android:strokeWidth="1.1"
android:fillColor="#00000000"
android:strokeColor="#A8AFA0"
android:strokeLineCap="round"/>
<path
android:pathData="M30.76,786.51C31.78,786.51 32.61,784.94 32.61,783.01C32.61,781.08 31.78,779.51 30.76,779.51C29.75,779.51 28.92,781.08 28.92,783.01C28.92,784.94 29.75,786.51 30.76,786.51Z"
android:fillColor="#A8AFA0"/>
<path
android:pathData="M33.53,786.51C34.54,786.51 35.37,784.94 35.37,783.01C35.37,781.08 34.54,779.51 33.53,779.51C32.51,779.51 31.68,781.08 31.68,783.01C31.68,784.94 32.51,786.51 33.53,786.51Z"
android:fillColor="#A8AFA0"/>
<path
android:pathData="M30.3,791.51C31.32,791.51 32.14,789.94 32.14,788.01C32.14,786.08 31.32,784.51 30.3,784.51C29.29,784.51 28.46,786.08 28.46,788.01C28.46,789.94 29.29,791.51 30.3,791.51Z"
android:fillColor="#A8AFA0"/>
<path
android:pathData="M33.99,791.51C35,791.51 35.83,789.94 35.83,788.01C35.83,786.08 35,784.51 33.99,784.51C32.97,784.51 32.14,786.08 32.14,788.01C32.14,789.94 32.97,791.51 33.99,791.51Z"
android:fillColor="#A8AFA0"/>
<path
android:pathData="M30.76,796.5C31.78,796.5 32.61,794.94 32.61,793.01C32.61,791.07 31.78,789.51 30.76,789.51C29.75,789.51 28.92,791.07 28.92,793.01C28.92,794.94 29.75,796.5 30.76,796.5Z"
android:fillColor="#A8AFA0"/>
<path
android:pathData="M33.53,796.5C34.54,796.5 35.37,794.94 35.37,793.01C35.37,791.07 34.54,789.51 33.53,789.51C32.51,789.51 31.68,791.07 31.68,793.01C31.68,794.94 32.51,796.5 33.53,796.5Z"
android:fillColor="#A8AFA0"/>
</vector>

View File

@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M18.667,11.667C18.667,12.904 18.175,14.091 17.3,14.967C16.425,15.842 15.238,16.333 14,16.333C12.762,16.333 11.575,15.842 10.7,14.967C9.825,14.091 9.333,12.904 9.333,11.667"
android:strokeLineJoin="round"
android:strokeWidth="2.33333"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M3.62,7.04H24.38"
android:strokeLineJoin="round"
android:strokeWidth="2.33333"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M3.967,6.378C3.664,6.782 3.5,7.273 3.5,7.778V23.333C3.5,23.952 3.746,24.546 4.183,24.983C4.621,25.421 5.214,25.667 5.833,25.667H22.167C22.785,25.667 23.379,25.421 23.817,24.983C24.254,24.546 24.5,23.952 24.5,23.333V7.778C24.5,7.273 24.336,6.782 24.033,6.378L21.7,3.267C21.483,2.977 21.201,2.742 20.877,2.58C20.553,2.418 20.196,2.333 19.833,2.333H8.167C7.804,2.333 7.447,2.418 7.123,2.58C6.799,2.742 6.517,2.977 6.3,3.267L3.967,6.378Z"
android:strokeLineJoin="round"
android:strokeWidth="2.33333"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M22.158,24.49L13.995,19.825L5.831,24.49V5.831C5.831,5.212 6.077,4.619 6.514,4.182C6.952,3.744 7.545,3.499 8.163,3.499H19.826C20.444,3.499 21.037,3.744 21.475,4.182C21.912,4.619 22.158,5.212 22.158,5.831V24.49Z"
android:strokeLineJoin="round"
android:strokeWidth="2.33241"
android:fillColor="#ffffff"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M5,21V5C5,4.45 5.196,3.979 5.588,3.588C5.979,3.196 6.45,3 7,3H13V5H7V17.95L12,15.8L17,17.95V11H19V21L12,18L5,21ZM17,9V7H15V5H17V3H19V5H21V7H19V9H17Z"
android:fillColor="#ffffffff"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M4,12H12V10H4V12ZM4,9H16V7H4V9ZM4,6H16V4H4V6ZM0,20V2C0,1.45 0.196,0.979 0.587,0.587C0.979,0.196 1.45,0 2,0H18C18.55,0 19.021,0.196 19.413,0.587C19.804,0.979 20,1.45 20,2V14C20,14.55 19.804,15.021 19.413,15.413C19.021,15.804 18.55,16 18,16H4L0,20ZM3.15,14H18V2H2V15.125L3.15,14Z"
android:fillColor="#ffffffff"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="21dp"
android:viewportWidth="20"
android:viewportHeight="21">
<path
android:pathData="M16.85,20.975C16.717,20.975 16.592,20.954 16.475,20.913C16.358,20.871 16.25,20.8 16.15,20.7L11.05,15.6C10.95,15.5 10.879,15.392 10.837,15.275C10.796,15.158 10.775,15.033 10.775,14.9C10.775,14.767 10.796,14.642 10.837,14.525C10.879,14.408 10.95,14.3 11.05,14.2L13.175,12.075C13.275,11.975 13.383,11.904 13.5,11.863C13.617,11.821 13.742,11.8 13.875,11.8C14.008,11.8 14.133,11.821 14.25,11.863C14.367,11.904 14.475,11.975 14.575,12.075L19.675,17.175C19.775,17.275 19.846,17.383 19.888,17.5C19.929,17.617 19.95,17.742 19.95,17.875C19.95,18.008 19.929,18.133 19.888,18.25C19.846,18.367 19.775,18.475 19.675,18.575L17.55,20.7C17.45,20.8 17.342,20.871 17.225,20.913C17.108,20.954 16.983,20.975 16.85,20.975ZM16.85,18.6L17.575,17.875L13.9,14.2L13.175,14.925L16.85,18.6ZM3.125,21C2.992,21 2.862,20.975 2.737,20.925C2.612,20.875 2.5,20.8 2.4,20.7L0.3,18.6C0.2,18.5 0.125,18.388 0.075,18.263C0.025,18.138 0,18.008 0,17.875C0,17.742 0.025,17.617 0.075,17.5C0.125,17.383 0.2,17.275 0.3,17.175L5.6,11.875H7.725L8.575,11.025L4.45,6.9H3.025L0,3.875L2.825,1.05L5.85,4.075V5.5L9.975,9.625L12.875,6.725L11.8,5.65L13.2,4.25H10.375L9.675,3.55L13.225,0L13.925,0.7V3.525L15.325,2.125L18.875,5.675C19.158,5.958 19.375,6.279 19.525,6.637C19.675,6.996 19.75,7.375 19.75,7.775C19.75,8.175 19.675,8.558 19.525,8.925C19.375,9.292 19.158,9.617 18.875,9.9L16.75,7.775L15.35,9.175L14.3,8.125L9.125,13.3V15.4L3.825,20.7C3.725,20.8 3.617,20.875 3.5,20.925C3.383,20.975 3.258,21 3.125,21ZM3.125,18.6L7.375,14.35V13.625H6.65L2.4,17.875L3.125,18.6ZM3.125,18.6L2.4,17.875L2.775,18.225L3.125,18.6Z"
android:fillColor="#ffffffff"/>
</vector>

View File

@ -0,0 +1,17 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<group>
<clip-path
android:pathData="M0,0h15.998v15.998h-15.998z"/>
<path
android:pathData="M6.666,13.332C6.666,13.456 6.7,13.577 6.766,13.683C6.831,13.788 6.924,13.873 7.035,13.929L8.368,14.595C8.47,14.646 8.583,14.67 8.696,14.665C8.81,14.66 8.92,14.626 9.017,14.566C9.113,14.506 9.193,14.423 9.248,14.323C9.304,14.224 9.333,14.112 9.332,13.999V9.332C9.333,9.002 9.455,8.683 9.677,8.438L14.492,3.113C14.578,3.017 14.635,2.899 14.655,2.772C14.676,2.644 14.659,2.514 14.607,2.396C14.554,2.279 14.469,2.178 14.361,2.108C14.253,2.038 14.127,2 13.999,2H2C1.871,2 1.745,2.037 1.637,2.108C1.529,2.178 1.443,2.278 1.391,2.396C1.339,2.513 1.322,2.644 1.342,2.771C1.362,2.899 1.419,3.017 1.505,3.113L6.321,8.438C6.543,8.683 6.666,9.002 6.666,9.332V13.332Z"
android:strokeLineJoin="round"
android:strokeWidth="1.3332"
android:fillColor="#00000000"
android:strokeColor="#ffffffff"
android:strokeLineCap="round"/>
</group>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M6,19H9V13H15V19H18V10L12,5.5L6,10V19ZM4,21V9L12,3L20,9V21H13V15H11V21H4Z"
android:fillColor="#ffffffff"/>
</vector>

View File

@ -0,0 +1,24 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="12dp"
android:viewportWidth="12"
android:viewportHeight="12">
<group>
<clip-path
android:pathData="M0,0h11.99v11.99h-11.99z"/>
<path
android:pathData="M9.992,4.996C9.992,7.491 7.225,10.089 6.296,10.891C6.209,10.956 6.104,10.991 5.995,10.991C5.887,10.991 5.782,10.956 5.695,10.891C4.766,10.089 1.999,7.491 1.999,4.996C1.999,3.936 2.42,2.919 3.169,2.17C3.919,1.42 4.935,0.999 5.995,0.999C7.055,0.999 8.072,1.42 8.822,2.17C9.571,2.919 9.992,3.936 9.992,4.996Z"
android:strokeLineJoin="round"
android:strokeWidth="0.999201"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M5.995,6.495C6.823,6.495 7.494,5.824 7.494,4.996C7.494,4.168 6.823,3.497 5.995,3.497C5.168,3.497 4.497,4.168 4.497,4.996C4.497,5.824 5.168,6.495 5.995,6.495Z"
android:strokeLineJoin="round"
android:strokeWidth="0.999201"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</group>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="16dp"
android:viewportWidth="18"
android:viewportHeight="16">
<path
android:pathData="M1,2V0H17V2H1ZM1,16V10H0V8L1,3H17L18,8V10H17V16H15V10H11V16H1ZM3,14H9V10H3V14ZM2.05,8H15.95L15.35,5H2.65L2.05,8Z"
android:fillColor="#ffffffff"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="27dp"
android:height="28dp"
android:viewportWidth="27"
android:viewportHeight="28">
<path
android:pathData="M13.5,25.667C12.881,25.667 12.352,25.438 11.911,24.981C11.47,24.524 11.25,23.975 11.25,23.333H15.75C15.75,23.975 15.53,24.524 15.089,24.981C14.648,25.438 14.119,25.667 13.5,25.667ZM4.5,22.167V19.833H6.75V11.667C6.75,10.053 7.219,8.619 8.156,7.365C9.094,6.11 10.313,5.289 11.813,4.9V4.083C11.813,3.597 11.977,3.184 12.305,2.844C12.633,2.503 13.031,2.333 13.5,2.333C13.969,2.333 14.367,2.503 14.695,2.844C15.023,3.184 15.188,3.597 15.188,4.083V4.462C14.981,4.89 14.831,5.328 14.738,5.775C14.644,6.222 14.606,6.679 14.625,7.146C14.438,7.107 14.255,7.073 14.077,7.044C13.898,7.015 13.706,7 13.5,7C12.262,7 11.203,7.457 10.322,8.371C9.441,9.285 9,10.383 9,11.667V19.833H18V12.337C18.337,12.493 18.698,12.615 19.083,12.702C19.467,12.79 19.856,12.833 20.25,12.833V19.833H22.5V22.167H4.5ZM20.25,10.5C19.313,10.5 18.516,10.16 17.859,9.479C17.203,8.799 16.875,7.972 16.875,7C16.875,6.028 17.203,5.201 17.859,4.521C18.516,3.84 19.313,3.5 20.25,3.5C21.188,3.5 21.984,3.84 22.641,4.521C23.297,5.201 23.625,6.028 23.625,7C23.625,7.972 23.297,8.799 22.641,9.479C21.984,10.16 21.188,10.5 20.25,10.5Z"
android:fillColor="#ffffffff"/>
</vector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group>
<clip-path
android:pathData="M0,0h24v24h-24z"/>
<path
android:pathData="M1.876,14.562V4.253C1.876,3.54 2.104,2.93 2.558,2.422C3.013,1.914 3.56,1.66 4.199,1.66H20.461C21.1,1.66 21.647,1.914 22.101,2.422C22.556,2.93 22.784,3.54 22.784,4.253V14.562C23.229,15.016 23.573,15.599 23.815,16.312C24.057,17.025 24.062,17.803 23.829,18.646L22.61,23.055C22.455,23.617 22.179,24.081 21.782,24.449C21.385,24.816 20.926,25 20.403,25H4.257C3.735,25 3.28,24.822 2.892,24.465C2.505,24.108 2.225,23.639 2.05,23.055L0.831,18.646C0.598,17.803 0.603,17.036 0.845,16.345C1.087,15.653 1.431,15.059 1.876,14.562ZM9.775,15.923C9.252,15.923 8.855,16.123 8.584,16.523C8.313,16.923 8.207,17.371 8.265,17.868L8.903,22.407H11.168V17.609C11.168,17.155 11.033,16.761 10.762,16.426C10.491,16.091 10.162,15.923 9.775,15.923ZM15.002,15.923C14.556,15.923 14.193,16.091 13.913,16.426C13.632,16.761 13.491,17.155 13.491,17.609V22.407H15.757L16.395,17.868C16.473,17.35 16.371,16.896 16.09,16.507C15.81,16.118 15.447,15.923 15.002,15.923ZM20.17,15.923C19.822,15.923 19.517,16.064 19.256,16.345C18.994,16.626 18.835,16.982 18.777,17.414L18.138,22.407H20.403L21.564,18.063C21.681,17.631 21.618,17.166 21.376,16.669C21.134,16.172 20.732,15.923 20.17,15.923ZM4.49,15.923C3.928,15.923 3.522,16.172 3.27,16.669C3.018,17.166 2.96,17.631 3.096,18.063L4.315,22.407H6.522L5.883,17.414C5.825,16.982 5.666,16.626 5.404,16.345C5.143,16.064 4.838,15.923 4.49,15.923ZM20.461,4.253H4.199V13.395C4.296,13.352 4.359,13.33 4.388,13.33H4.49C5.012,13.33 5.472,13.427 5.869,13.622C6.266,13.816 6.658,14.13 7.045,14.562C7.393,14.173 7.79,13.87 8.236,13.654C8.681,13.438 9.155,13.33 9.658,13.33C10.181,13.33 10.67,13.438 11.125,13.654C11.58,13.87 11.981,14.173 12.33,14.562C12.659,14.173 13.041,13.87 13.477,13.654C13.913,13.438 14.382,13.33 14.885,13.33C15.447,13.33 15.955,13.438 16.41,13.654C16.865,13.87 17.267,14.173 17.615,14.562C18.022,14.108 18.423,13.789 18.82,13.606C19.217,13.422 19.667,13.33 20.17,13.33H20.301C20.35,13.33 20.403,13.352 20.461,13.395V4.253Z"
android:fillColor="#ffffff"/>
</group>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="18"
android:viewportHeight="18">
<path
android:pathData="M16.95,18C14.867,18 12.808,17.546 10.775,16.638C8.742,15.729 6.892,14.442 5.225,12.775C3.558,11.108 2.271,9.258 1.362,7.225C0.454,5.192 0,3.133 0,1.05C0,0.75 0.1,0.5 0.3,0.3C0.5,0.1 0.75,0 1.05,0H5.1C5.333,0 5.542,0.079 5.725,0.237C5.908,0.396 6.017,0.583 6.05,0.8L6.7,4.3C6.733,4.567 6.725,4.792 6.675,4.975C6.625,5.158 6.533,5.317 6.4,5.45L3.975,7.9C4.308,8.517 4.704,9.113 5.162,9.688C5.621,10.262 6.125,10.817 6.675,11.35C7.192,11.867 7.733,12.346 8.3,12.788C8.867,13.229 9.467,13.633 10.1,14L12.45,11.65C12.6,11.5 12.796,11.387 13.038,11.313C13.279,11.238 13.517,11.217 13.75,11.25L17.2,11.95C17.433,12.017 17.625,12.137 17.775,12.313C17.925,12.488 18,12.683 18,12.9V16.95C18,17.25 17.9,17.5 17.7,17.7C17.5,17.9 17.25,18 16.95,18ZM3.025,6L4.675,4.35L4.25,2H2.025C2.108,2.683 2.225,3.358 2.375,4.025C2.525,4.692 2.742,5.35 3.025,6ZM11.975,14.95C12.625,15.233 13.288,15.458 13.962,15.625C14.637,15.792 15.317,15.9 16,15.95V13.75L13.65,13.275L11.975,14.95Z"
android:fillColor="#ffffffff"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="73dp"
android:height="71dp"
android:viewportWidth="73"
android:viewportHeight="71">
<path
android:pathData="M14.052,53.605C17.155,51.298 20.622,49.478 24.455,48.147C28.288,46.816 32.303,46.15 36.5,46.15C40.697,46.15 44.713,46.816 48.545,48.147C52.377,49.478 55.845,51.298 58.947,53.605C61.077,51.179 62.734,48.428 63.921,45.351C65.107,42.275 65.7,38.991 65.7,35.5C65.7,27.631 62.856,20.93 57.168,15.398C51.48,9.866 44.591,7.1 36.5,7.1C28.409,7.1 21.52,9.866 15.832,15.398C10.144,20.93 7.3,27.631 7.3,35.5C7.3,38.991 7.893,42.275 9.079,45.351C10.266,48.428 11.923,51.179 14.052,53.605ZM36.5,39.05C32.911,39.05 29.884,37.852 27.421,35.456C24.957,33.059 23.725,30.116 23.725,26.625C23.725,23.134 24.957,20.191 27.421,17.794C29.884,15.398 32.911,14.2 36.5,14.2C40.089,14.2 43.116,15.398 45.579,17.794C48.043,20.191 49.275,23.134 49.275,26.625C49.275,30.116 48.043,33.059 45.579,35.456C43.116,37.852 40.089,39.05 36.5,39.05ZM36.5,71C31.451,71 26.706,70.068 22.265,68.204C17.824,66.341 13.961,63.811 10.676,60.616C7.391,57.421 4.791,53.664 2.874,49.345C0.958,45.026 0,40.411 0,35.5C0,30.589 0.958,25.974 2.874,21.655C4.791,17.336 7.391,13.579 10.676,10.384C13.961,7.189 17.824,4.659 22.265,2.796C26.706,0.932 31.451,0 36.5,0C41.549,0 46.294,0.932 50.735,2.796C55.176,4.659 59.039,7.189 62.324,10.384C65.609,13.579 68.209,17.336 70.126,21.655C72.042,25.974 73,30.589 73,35.5C73,40.411 72.042,45.026 70.126,49.345C68.209,53.664 65.609,57.421 62.324,60.616C59.039,63.811 55.176,66.341 50.735,68.204C46.294,70.068 41.549,71 36.5,71ZM36.5,63.9C39.724,63.9 42.766,63.442 45.625,62.524C48.484,61.607 51.1,60.291 53.472,58.575C51.1,56.859 48.484,55.543 45.625,54.626C42.766,53.708 39.724,53.25 36.5,53.25C33.276,53.25 30.234,53.708 27.375,54.626C24.516,55.543 21.9,56.859 19.528,58.575C21.9,60.291 24.516,61.607 27.375,62.524C30.234,63.442 33.276,63.9 36.5,63.9ZM36.5,31.95C38.082,31.95 39.39,31.447 40.424,30.441C41.458,29.435 41.975,28.163 41.975,26.625C41.975,25.087 41.458,23.815 40.424,22.809C39.39,21.803 38.082,21.3 36.5,21.3C34.918,21.3 33.61,21.803 32.576,22.809C31.542,23.815 31.025,25.087 31.025,26.625C31.025,28.163 31.542,29.435 32.576,30.441C33.61,31.447 34.918,31.95 36.5,31.95Z"
android:fillColor="#ffffff"/>
</vector>

View File

@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M17.5,24.5V18.667C17.5,18.357 17.377,18.06 17.158,17.842C16.94,17.623 16.643,17.5 16.333,17.5H11.667C11.357,17.5 11.061,17.623 10.842,17.842C10.623,18.06 10.5,18.357 10.5,18.667V24.5"
android:strokeLineJoin="round"
android:strokeWidth="2.33333"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M20.736,12.028C20.493,11.795 20.169,11.665 19.833,11.665C19.496,11.665 19.172,11.795 18.929,12.028C18.386,12.546 17.666,12.834 16.916,12.834C16.166,12.834 15.445,12.546 14.903,12.028C14.66,11.796 14.336,11.666 14,11.666C13.663,11.666 13.34,11.796 13.097,12.028C12.554,12.546 11.833,12.835 11.083,12.835C10.333,12.835 9.612,12.546 9.069,12.028C8.826,11.795 8.503,11.665 8.166,11.665C7.829,11.665 7.505,11.795 7.262,12.028C6.738,12.528 6.047,12.816 5.323,12.834C4.599,12.852 3.894,12.601 3.345,12.128C2.796,11.655 2.443,10.995 2.354,10.276C2.265,9.557 2.447,8.831 2.864,8.239L6.234,3.358C6.448,3.042 6.736,2.784 7.073,2.605C7.41,2.427 7.785,2.333 8.166,2.333H19.833C20.213,2.333 20.588,2.426 20.924,2.603C21.26,2.781 21.547,3.038 21.762,3.352L25.139,8.242C25.556,8.835 25.738,9.562 25.648,10.281C25.559,11 25.205,11.66 24.655,12.133C24.105,12.605 23.4,12.856 22.675,12.837C21.951,12.817 21.259,12.528 20.736,12.027"
android:strokeLineJoin="round"
android:strokeWidth="2.33333"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M4.667,12.775V22.167C4.667,22.785 4.912,23.379 5.35,23.816C5.788,24.254 6.381,24.5 7,24.5H21C21.619,24.5 22.212,24.254 22.65,23.816C23.087,23.379 23.333,22.785 23.333,22.167V12.775"
android:strokeLineJoin="round"
android:strokeWidth="2.33333"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group>
<clip-path
android:pathData="M0,0h24v24h-24z"/>
<path
android:pathData="M22.124,11.438V21.747C22.124,22.46 21.896,23.07 21.441,23.578C20.987,24.086 20.44,24.34 19.801,24.34H3.539C2.9,24.34 2.353,24.086 1.898,23.578C1.444,23.07 1.216,22.46 1.216,21.747V11.438C0.771,10.984 0.427,10.401 0.185,9.688C-0.057,8.974 -0.062,8.196 0.171,7.354L1.39,2.945C1.545,2.383 1.821,1.918 2.218,1.551C2.615,1.184 3.075,1 3.597,1H19.743C20.265,1 20.72,1.178 21.108,1.535C21.495,1.891 21.775,2.362 21.95,2.945L23.169,7.354C23.402,8.196 23.397,8.964 23.155,9.655C22.913,10.347 22.569,10.941 22.124,11.438ZM14.225,10.077C14.748,10.077 15.145,9.877 15.416,9.477C15.687,9.077 15.793,8.629 15.735,8.132L15.097,3.593H12.832V8.391C12.832,8.845 12.967,9.239 13.238,9.574C13.509,9.909 13.838,10.077 14.225,10.077ZM8.998,10.077C9.444,10.077 9.807,9.909 10.087,9.574C10.368,9.239 10.509,8.845 10.509,8.391V3.593H8.243L7.605,8.132C7.527,8.65 7.629,9.104 7.909,9.493C8.19,9.882 8.553,10.077 8.998,10.077ZM3.83,10.077C4.178,10.077 4.483,9.936 4.744,9.655C5.006,9.374 5.165,9.018 5.223,8.585L5.862,3.593H3.597L2.436,7.937C2.32,8.369 2.382,8.834 2.624,9.331C2.866,9.828 3.268,10.077 3.83,10.077ZM19.51,10.077C20.072,10.077 20.478,9.828 20.73,9.331C20.982,8.834 21.04,8.369 20.904,7.937L19.685,3.593H17.478L18.117,8.585C18.175,9.018 18.334,9.374 18.596,9.655C18.857,9.936 19.162,10.077 19.51,10.077ZM3.539,21.747H19.801V12.605C19.704,12.648 19.641,12.67 19.612,12.67H19.51C18.988,12.67 18.528,12.573 18.131,12.378C17.734,12.184 17.342,11.87 16.955,11.438C16.607,11.827 16.21,12.13 15.764,12.346C15.319,12.562 14.845,12.67 14.342,12.67C13.819,12.67 13.33,12.562 12.875,12.346C12.42,12.13 12.019,11.827 11.67,11.438C11.341,11.827 10.959,12.13 10.523,12.346C10.087,12.562 9.618,12.67 9.115,12.67C8.553,12.67 8.045,12.562 7.59,12.346C7.135,12.13 6.733,11.827 6.385,11.438C5.978,11.892 5.577,12.211 5.18,12.394C4.783,12.578 4.333,12.67 3.83,12.67H3.699C3.65,12.67 3.597,12.648 3.539,12.605V21.747Z"
android:fillColor="#ffffff"/>
</group>
</vector>

View File

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M13.999,10.665L11.332,13.332L8.666,10.665"
android:strokeLineJoin="round"
android:strokeWidth="1.3332"
android:fillColor="#00000000"
android:strokeColor="#ffffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M11.332,13.332V2.667"
android:strokeLineJoin="round"
android:strokeWidth="1.3332"
android:fillColor="#00000000"
android:strokeColor="#ffffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M2,5.333L4.666,2.667L7.333,5.333"
android:strokeLineJoin="round"
android:strokeWidth="1.3332"
android:fillColor="#00000000"
android:strokeColor="#ffffffff"
android:strokeLineCap="round"/>
<path
android:pathData="M4.666,2.667V13.332"
android:strokeLineJoin="round"
android:strokeWidth="1.3332"
android:fillColor="#00000000"
android:strokeColor="#ffffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M17.15,7.35C16.937,7.568 16.817,7.861 16.817,8.167C16.817,8.472 16.937,8.765 17.15,8.983L19.017,10.85C19.235,11.064 19.528,11.184 19.834,11.184C20.139,11.184 20.432,11.064 20.65,10.85L24.274,7.228C24.647,6.852 25.281,6.971 25.421,7.482C25.773,8.764 25.753,10.12 25.363,11.391C24.973,12.662 24.229,13.796 23.219,14.66C22.208,15.523 20.972,16.081 19.655,16.268C18.339,16.455 16.997,16.263 15.785,15.715L6.557,24.943C6.093,25.407 5.463,25.668 4.807,25.668C4.151,25.668 3.521,25.407 3.057,24.943C2.593,24.479 2.333,23.849 2.333,23.193C2.333,22.537 2.594,21.907 3.058,21.443L12.286,12.215C11.738,11.004 11.546,9.661 11.733,8.345C11.92,7.029 12.478,5.793 13.342,4.782C14.206,3.771 15.339,3.027 16.611,2.637C17.882,2.247 19.237,2.227 20.52,2.58C21.031,2.72 21.15,3.352 20.775,3.728L17.15,7.35Z"
android:strokeLineJoin="round"
android:strokeWidth="2.33333"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="40dp"
android:viewportWidth="40"
android:viewportHeight="40">
<path
android:pathData="M23.331,15.892V11.665C23.331,9.898 24.033,8.202 25.284,6.952C26.534,5.702 28.229,5 29.997,5H32.497C32.718,5 32.93,5.087 33.086,5.244C33.242,5.4 33.33,5.612 33.33,5.833V8.333C33.33,10.101 32.628,11.796 31.378,13.046C30.128,14.296 28.432,14.998 26.664,14.998C24.896,14.998 23.201,15.701 21.951,16.951C20.7,18.201 19.998,19.897 19.998,21.665C19.998,24.998 21.665,26.664 21.665,29.997C21.665,31.8 21.08,33.554 19.998,34.997"
android:strokeLineJoin="round"
android:strokeWidth="3.33301"
android:fillColor="#00000000"
android:strokeColor="#8B6F47"
android:strokeLineCap="round"/>
<path
android:pathData="M6.666,14.998C7.904,14.07 9.376,13.505 10.917,13.366C12.458,13.227 14.008,13.52 15.392,14.212C16.776,14.904 17.94,15.968 18.754,17.284C19.567,18.6 19.998,20.117 19.998,21.665C18.76,22.593 17.288,23.158 15.747,23.297C14.206,23.436 12.656,23.143 11.272,22.451C9.888,21.759 8.724,20.695 7.91,19.379C7.097,18.063 6.666,16.546 6.666,14.998Z"
android:strokeLineJoin="round"
android:strokeWidth="3.33301"
android:fillColor="#00000000"
android:strokeColor="#8B6F47"
android:strokeLineCap="round"/>
<path
android:pathData="M8.333,34.997H31.664"
android:strokeLineJoin="round"
android:strokeWidth="3.33301"
android:fillColor="#00000000"
android:strokeColor="#8B6F47"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="8dp"
android:height="8dp"
android:viewportWidth="8"
android:viewportHeight="8">
<path
android:pathData="M3.7143,4.8571L4,5.12L4.96,5.86L2.5,4H5.5H4.38H4H3.62H2.5L3.4,4.64L3.7143,4.8571ZM1.53,8L2.46,4.96L0,3.2H3.04L4,0L4.96,3.2H8L5.54,4.96L6.47,8L4,6.12L1.53,8Z"
android:fillColor="#DE9A07"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="10dp"
android:height="10dp"
android:viewportWidth="10"
android:viewportHeight="10">
<path
android:pathData="M5,3.45V6.4L6.2,7.325L5.75,5.8L6.875,5H5.475L5,3.45ZM1.913,10L3.075,6.2L0,4H3.8L5,0L6.2,4H10L6.925,6.2L8.087,10L5,7.65L1.913,10Z"
android:fillColor="#DE9A07"/>
</vector>

View File

@ -0,0 +1,69 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="33dp"
android:height="32dp"
android:viewportWidth="33"
android:viewportHeight="32">
<path
android:pathData="M16.01,21.331C18.957,21.331 21.346,18.944 21.346,15.998C21.346,13.053 18.957,10.665 16.01,10.665C13.062,10.665 10.673,13.053 10.673,15.998C10.673,18.944 13.062,21.331 16.01,21.331Z"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#E07A5F"
android:strokeLineCap="round"/>
<path
android:pathData="M16.01,2.667V5.333"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#E07A5F"
android:strokeLineCap="round"/>
<path
android:pathData="M16.01,26.664V29.33"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#E07A5F"
android:strokeLineCap="round"/>
<path
android:pathData="M6.577,6.573L8.458,8.453"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#E07A5F"
android:strokeLineCap="round"/>
<path
android:pathData="M23.561,23.544L25.442,25.424"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#E07A5F"
android:strokeLineCap="round"/>
<path
android:pathData="M2.668,15.998H5.336"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#E07A5F"
android:strokeLineCap="round"/>
<path
android:pathData="M26.683,15.998H29.351"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#E07A5F"
android:strokeLineCap="round"/>
<path
android:pathData="M8.458,23.544L6.577,25.424"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#E07A5F"
android:strokeLineCap="round"/>
<path
android:pathData="M25.442,6.573L23.561,8.453"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#E07A5F"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M17.15,7.35C16.937,7.568 16.817,7.861 16.817,8.167C16.817,8.472 16.937,8.765 17.15,8.983L19.017,10.85C19.235,11.064 19.528,11.184 19.834,11.184C20.139,11.184 20.432,11.064 20.65,10.85L24.274,7.228C24.647,6.852 25.281,6.971 25.421,7.482C25.773,8.764 25.753,10.12 25.363,11.391C24.973,12.662 24.229,13.796 23.219,14.66C22.208,15.523 20.972,16.081 19.655,16.268C18.339,16.455 16.997,16.263 15.785,15.715L6.557,24.943C6.093,25.407 5.463,25.668 4.807,25.668C4.151,25.668 3.521,25.407 3.057,24.943C2.593,24.479 2.333,23.849 2.333,23.193C2.333,22.537 2.594,21.907 3.058,21.443L12.286,12.215C11.738,11.004 11.546,9.661 11.733,8.345C11.92,7.029 12.478,5.793 13.342,4.782C14.206,3.771 15.339,3.027 16.611,2.637C17.882,2.247 19.237,2.227 20.52,2.58C21.031,2.72 21.15,3.352 20.775,3.728L17.15,7.35Z"
android:strokeLineJoin="round"
android:strokeWidth="2.33333"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M19.4,12.25L12.25,19.4C12.05,19.6 11.825,19.75 11.575,19.85C11.325,19.95 11.075,20 10.825,20C10.575,20 10.325,19.95 10.075,19.85C9.825,19.75 9.6,19.6 9.4,19.4L0.575,10.575C0.392,10.392 0.25,10.179 0.15,9.938C0.05,9.696 0,9.442 0,9.175V2C0,1.45 0.196,0.979 0.587,0.587C0.979,0.196 1.45,0 2,0H9.175C9.442,0 9.7,0.054 9.95,0.162C10.2,0.271 10.417,0.417 10.6,0.6L19.4,9.425C19.6,9.625 19.746,9.85 19.837,10.1C19.929,10.35 19.975,10.6 19.975,10.85C19.975,11.1 19.929,11.346 19.837,11.587C19.746,11.829 19.6,12.05 19.4,12.25ZM10.825,18L17.975,10.85L9.15,2H2V9.15L10.825,18ZM4.5,6C4.917,6 5.271,5.854 5.563,5.563C5.854,5.271 6,4.917 6,4.5C6,4.083 5.854,3.729 5.563,3.438C5.271,3.146 4.917,3 4.5,3C4.083,3 3.729,3.146 3.438,3.438C3.146,3.729 3,4.083 3,4.5C3,4.917 3.146,5.271 3.438,5.563C3.729,5.854 4.083,6 4.5,6Z"
android:fillColor="#ffffffff"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="15dp"
android:height="15dp"
android:viewportWidth="15"
android:viewportHeight="15">
<path
android:pathData="M5.25,12H9.375C9.587,12 9.784,11.947 9.966,11.841C10.147,11.734 10.275,11.587 10.35,11.4L11.925,7.725C11.95,7.662 11.969,7.6 11.981,7.537C11.994,7.475 12,7.412 12,7.35V6.75C12,6.537 11.928,6.359 11.784,6.216C11.641,6.072 11.462,6 11.25,6H7.8L8.25,3.45C8.275,3.325 8.269,3.206 8.231,3.094C8.194,2.981 8.131,2.881 8.044,2.794L7.5,2.25L4.05,6C3.95,6.1 3.875,6.213 3.825,6.338C3.775,6.463 3.75,6.6 3.75,6.75V10.5C3.75,10.913 3.897,11.266 4.191,11.559C4.484,11.853 4.838,12 5.25,12ZM7.5,15C6.463,15 5.488,14.803 4.575,14.409C3.662,14.016 2.869,13.481 2.194,12.806C1.519,12.131 0.984,11.337 0.591,10.425C0.197,9.512 0,8.538 0,7.5C0,6.463 0.197,5.488 0.591,4.575C0.984,3.662 1.519,2.869 2.194,2.194C2.869,1.519 3.662,0.984 4.575,0.591C5.488,0.197 6.463,0 7.5,0C8.538,0 9.512,0.197 10.425,0.591C11.337,0.984 12.131,1.519 12.806,2.194C13.481,2.869 14.016,3.662 14.409,4.575C14.803,5.488 15,6.463 15,7.5C15,8.538 14.803,9.512 14.409,10.425C14.016,11.337 13.481,12.131 12.806,12.806C12.131,13.481 11.337,14.016 10.425,14.409C9.512,14.803 8.538,15 7.5,15ZM7.5,13.5C9.175,13.5 10.594,12.919 11.756,11.756C12.919,10.594 13.5,9.175 13.5,7.5C13.5,5.825 12.919,4.406 11.756,3.244C10.594,2.081 9.175,1.5 7.5,1.5C5.825,1.5 4.406,2.081 3.244,3.244C2.081,4.406 1.5,5.825 1.5,7.5C1.5,9.175 2.081,10.594 3.244,11.756C4.406,12.919 5.825,13.5 7.5,13.5Z"
android:fillColor="#0A0A0A"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="22dp"
android:height="15dp"
android:viewportWidth="22"
android:viewportHeight="15">
<path
android:pathData="M11,12C12.25,12 13.313,11.563 14.188,10.688C15.063,9.813 15.5,8.75 15.5,7.5C15.5,6.25 15.063,5.188 14.188,4.313C13.313,3.438 12.25,3 11,3C9.75,3 8.688,3.438 7.813,4.313C6.938,5.188 6.5,6.25 6.5,7.5C6.5,8.75 6.938,9.813 7.813,10.688C8.688,11.563 9.75,12 11,12ZM11,10.2C10.25,10.2 9.613,9.938 9.087,9.413C8.563,8.887 8.3,8.25 8.3,7.5C8.3,6.75 8.563,6.113 9.087,5.588C9.613,5.063 10.25,4.8 11,4.8C11.75,4.8 12.387,5.063 12.913,5.588C13.438,6.113 13.7,6.75 13.7,7.5C13.7,8.25 13.438,8.887 12.913,9.413C12.387,9.938 11.75,10.2 11,10.2ZM11,15C8.567,15 6.35,14.321 4.35,12.962C2.35,11.604 0.9,9.783 0,7.5C0.9,5.217 2.35,3.396 4.35,2.037C6.35,0.679 8.567,0 11,0C13.433,0 15.65,0.679 17.65,2.037C19.65,3.396 21.1,5.217 22,7.5C21.1,9.783 19.65,11.604 17.65,12.962C15.65,14.321 13.433,15 11,15ZM11,13C12.883,13 14.613,12.504 16.188,11.512C17.763,10.521 18.967,9.183 19.8,7.5C18.967,5.817 17.763,4.479 16.188,3.487C14.613,2.496 12.883,2 11,2C9.117,2 7.387,2.496 5.813,3.487C4.238,4.479 3.033,5.817 2.2,7.5C3.033,9.183 4.238,10.521 5.813,11.512C7.387,12.504 9.117,13 11,13Z"
android:fillColor="#E3E3E3"/>
</vector>

View File

@ -0,0 +1,62 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="33dp"
android:height="32dp"
android:viewportWidth="33"
android:viewportHeight="32">
<path
android:pathData="M2.668,29.33L21.346,10.665"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#D4A574"
android:strokeLineCap="round"/>
<path
android:pathData="M4.629,16.705L6.671,14.665L8.712,16.705C9.584,17.58 10.073,18.764 10.073,19.998C10.073,21.233 9.584,22.417 8.712,23.291L6.671,25.331L4.629,23.291C3.758,22.417 3.268,21.233 3.268,19.998C3.268,18.764 3.758,17.58 4.629,16.705Z"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#D4A574"
android:strokeLineCap="round"/>
<path
android:pathData="M9.966,11.372L12.007,9.333L14.048,11.372C14.92,12.247 15.41,13.431 15.41,14.665C15.41,15.9 14.92,17.084 14.048,17.958L12.007,19.998L9.966,17.958C9.094,17.084 8.605,15.9 8.605,14.665C8.605,13.431 9.094,12.247 9.966,11.372Z"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#D4A574"
android:strokeLineCap="round"/>
<path
android:pathData="M15.303,6.039L17.344,4L19.385,6.039C20.257,6.914 20.746,8.098 20.746,9.332C20.746,10.567 20.257,11.751 19.385,12.625L17.344,14.665L15.303,12.625C14.431,11.751 13.941,10.567 13.941,9.332C13.941,8.098 14.431,6.914 15.303,6.039Z"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#D4A574"
android:strokeLineCap="round"/>
<path
android:pathData="M26.683,2.667H29.351V5.333C29.351,6.747 28.789,8.104 27.788,9.104C26.787,10.104 25.43,10.666 24.014,10.666H21.346V7.999C21.346,6.585 21.908,5.229 22.909,4.228C23.91,3.228 25.267,2.667 26.683,2.667Z"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#D4A574"
android:strokeLineCap="round"/>
<path
android:pathData="M15.302,23.291L17.344,25.331L15.302,27.371C14.427,28.242 13.242,28.731 12.007,28.731C10.772,28.731 9.587,28.242 8.712,27.371L6.671,25.331L8.712,23.291C9.587,22.42 10.772,21.931 12.007,21.931C13.242,21.931 14.427,22.42 15.302,23.291Z"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#D4A574"
android:strokeLineCap="round"/>
<path
android:pathData="M20.639,17.958L22.68,19.998L20.639,22.038C19.764,22.909 18.579,23.398 17.344,23.398C16.108,23.398 14.924,22.909 14.048,22.038L12.007,19.998L14.048,17.958C14.924,17.087 16.108,16.598 17.344,16.598C18.579,16.598 19.764,17.087 20.639,17.958Z"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#D4A574"
android:strokeLineCap="round"/>
<path
android:pathData="M25.976,12.625L28.017,14.665L25.976,16.705C25.1,17.576 23.916,18.065 22.68,18.065C21.445,18.065 20.26,17.576 19.385,16.705L17.344,14.665L19.385,12.625C20.26,11.754 21.445,11.265 22.68,11.265C23.916,11.265 25.1,11.754 25.976,12.625Z"
android:strokeLineJoin="round"
android:strokeWidth="2.66641"
android:fillColor="#00000000"
android:strokeColor="#D4A574"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache_images" path="images/" />
<cache-path
name="cache_videos"
path="videos/" />
</paths>