moved the UI repo
integrated existing logic with new screens
This commit is contained in:
parent
1def19003b
commit
4f8ae88820
|
|
@ -0,0 +1 @@
|
||||||
|
LivingAi_Lg
|
||||||
|
|
@ -72,4 +72,19 @@ dependencies {
|
||||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
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")
|
||||||
}
|
}
|
||||||
|
|
@ -25,6 +25,15 @@
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</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>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
@ -23,30 +23,32 @@ import com.example.livingai_lg.ui.AuthState
|
||||||
import com.example.livingai_lg.ui.MainViewModel
|
import com.example.livingai_lg.ui.MainViewModel
|
||||||
import com.example.livingai_lg.ui.MainViewModelFactory
|
import com.example.livingai_lg.ui.MainViewModelFactory
|
||||||
import com.example.livingai_lg.ui.login.*
|
import com.example.livingai_lg.ui.login.*
|
||||||
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() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
LivingAi_LgTheme {
|
FarmMarketplaceTheme {
|
||||||
val mainViewModel: MainViewModel = viewModel(factory = MainViewModelFactory(LocalContext.current))
|
val mainViewModel: MainViewModel = viewModel(factory = MainViewModelFactory(LocalContext.current))
|
||||||
val authState by mainViewModel.authState.collectAsState()
|
val authState by mainViewModel.authState.collectAsState()
|
||||||
|
|
||||||
when (authState) {
|
AppNavigation(authState)
|
||||||
is AuthState.Unknown -> {
|
// when (authState) {
|
||||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
// is AuthState.Unknown -> {
|
||||||
CircularProgressIndicator()
|
// Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
}
|
// CircularProgressIndicator()
|
||||||
}
|
// }
|
||||||
is AuthState.Authenticated -> {
|
// }
|
||||||
SuccessScreen(mainViewModel)
|
// is AuthState.Authenticated -> {
|
||||||
}
|
// SuccessScreen(mainViewModel)
|
||||||
is AuthState.Unauthenticated -> {
|
// }
|
||||||
AuthNavigation()
|
// is AuthState.Unauthenticated -> {
|
||||||
}
|
// AuthNavigation()
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 doesn’t 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)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 asset’s 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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -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 asset’s 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -52,11 +52,7 @@ fun CreateProfileScreen(navController: NavController, name: String) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(
|
|
||||||
brush = Brush.linearGradient(
|
|
||||||
colors = listOf(LightCream, LighterCream, LightestGreen)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -105,7 +101,7 @@ fun ProfileTypeItem(text: String, icon: Int, onClick: () -> Unit) {
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun CreateProfileScreenPreview() {
|
fun CreateProfileScreenPreview() {
|
||||||
LivingAi_LgTheme {
|
FarmMarketplaceTheme {
|
||||||
CreateProfileScreen(rememberNavController(), "John Doe")
|
CreateProfileScreen(rememberNavController(), "John Doe")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
|
@ -29,11 +30,11 @@ fun LoginScreen(navController: NavController) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(
|
// .background(
|
||||||
brush = Brush.linearGradient(
|
// brush = Brush.linearGradient(
|
||||||
colors = listOf(LightCream, LighterCream, LightestGreen)
|
// colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||||
)
|
// )
|
||||||
)
|
// )
|
||||||
) {
|
) {
|
||||||
// Decorative elements...
|
// Decorative elements...
|
||||||
|
|
||||||
|
|
@ -55,7 +56,7 @@ fun LoginScreen(navController: NavController) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(56.dp)
|
.size(56.dp)
|
||||||
.background(Gold, RoundedCornerShape(28.dp)),
|
.background(Color.Yellow, RoundedCornerShape(28.dp)),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(text = "🌾", fontSize = 24.sp)
|
Text(text = "🌾", fontSize = 24.sp)
|
||||||
|
|
@ -64,7 +65,7 @@ fun LoginScreen(navController: NavController) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(72.dp)
|
.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
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(text = "🌱", fontSize = 32.sp)
|
Text(text = "🌱", fontSize = 32.sp)
|
||||||
|
|
@ -73,7 +74,7 @@ fun LoginScreen(navController: NavController) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(56.dp)
|
.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
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(text = "☀️", fontSize = 24.sp)
|
Text(text = "☀️", fontSize = 24.sp)
|
||||||
|
|
@ -86,13 +87,13 @@ fun LoginScreen(navController: NavController) {
|
||||||
text = "Welcome!",
|
text = "Welcome!",
|
||||||
fontSize = 24.sp,
|
fontSize = 24.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
color = DarkBrown
|
color = Color.Yellow
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
Text(
|
Text(
|
||||||
text = "Join the farm marketplace community",
|
text = "Join the farm marketplace community",
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
color = MidBrown,
|
color = Color.Yellow,
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(48.dp))
|
Spacer(modifier = Modifier.height(48.dp))
|
||||||
|
|
@ -101,12 +102,12 @@ fun LoginScreen(navController: NavController) {
|
||||||
Button(
|
Button(
|
||||||
onClick = { navController.navigate("signup") },
|
onClick = { navController.navigate("signup") },
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(16.dp),
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = LightOrange),
|
colors = ButtonDefaults.buttonColors(containerColor = Color.Yellow),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(56.dp)
|
.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))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
@ -115,12 +116,12 @@ fun LoginScreen(navController: NavController) {
|
||||||
Button(
|
Button(
|
||||||
onClick = { navController.navigate("signin") },
|
onClick = { navController.navigate("signin") },
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(16.dp),
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = TerraCotta),
|
colors = ButtonDefaults.buttonColors(containerColor = Color.Yellow),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(56.dp)
|
.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))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
@ -128,7 +129,7 @@ fun LoginScreen(navController: NavController) {
|
||||||
// Guest Button
|
// Guest Button
|
||||||
Text(
|
Text(
|
||||||
text = "Continue as Guest",
|
text = "Continue as Guest",
|
||||||
color = MidBrown,
|
color = Color.Yellow,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
modifier = Modifier.clickable { Toast.makeText(context, "Guest mode is not yet available", Toast.LENGTH_SHORT).show() }
|
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)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginScreenPreview() {
|
fun LoginScreenPreview() {
|
||||||
LivingAi_LgTheme {
|
FarmMarketplaceTheme() {
|
||||||
LoginScreen(rememberNavController())
|
LoginScreen(rememberNavController())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,11 @@ fun OtpScreen(navController: NavController, phoneNumber: String, name: String) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(
|
// .background(
|
||||||
brush = Brush.linearGradient(
|
// brush = Brush.linearGradient(
|
||||||
colors = listOf(LightCream, LighterCream, LightestGreen)
|
// colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||||
)
|
// )
|
||||||
)
|
// )
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -117,7 +117,7 @@ fun OtpScreen(navController: NavController, phoneNumber: String, name: String) {
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun OtpScreenPreview() {
|
fun OtpScreenPreview() {
|
||||||
LivingAi_LgTheme {
|
FarmMarketplaceTheme() {
|
||||||
OtpScreen(rememberNavController(), "+919876543210", "John Doe")
|
OtpScreen(rememberNavController(), "+919876543210", "John Doe")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,11 @@ fun SignInScreen(navController: NavController) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(
|
// .background(
|
||||||
brush = Brush.linearGradient(
|
// brush = Brush.linearGradient(
|
||||||
colors = listOf(LightCream, LighterCream, LightestGreen)
|
// colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||||
)
|
// )
|
||||||
)
|
// )
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -161,7 +161,7 @@ fun SignInScreen(navController: NavController) {
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun SignInScreenPreview() {
|
fun SignInScreenPreview() {
|
||||||
LivingAi_LgTheme {
|
FarmMarketplaceTheme() {
|
||||||
SignInScreen(rememberNavController())
|
SignInScreen(rememberNavController())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,11 @@ fun SignUpScreen(navController: NavController) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(
|
// .background(
|
||||||
brush = Brush.linearGradient(
|
// brush = Brush.linearGradient(
|
||||||
colors = listOf(LightCream, LighterCream, LightestGreen)
|
// colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||||
)
|
// )
|
||||||
)
|
// )
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -190,7 +190,7 @@ fun SignUpScreen(navController: NavController) {
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun SignUpScreenPreview() {
|
fun SignUpScreenPreview() {
|
||||||
LivingAi_LgTheme {
|
FarmMarketplaceTheme {
|
||||||
SignUpScreen(rememberNavController())
|
SignUpScreen(rememberNavController())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,6 @@ import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.example.livingai_lg.ui.MainViewModel
|
import com.example.livingai_lg.ui.MainViewModel
|
||||||
import com.example.livingai_lg.ui.UserState
|
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
|
import com.example.livingai_lg.ui.MainViewModelFactory // <-- This was the missing import
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -28,11 +25,12 @@ fun SuccessScreen(mainViewModel: MainViewModel = viewModel(factory = MainViewMod
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(
|
// .background(
|
||||||
brush = Brush.linearGradient(
|
// brush = Brush.linearGradient(
|
||||||
colors = listOf(LightCream, LighterCream, LightestGreen)
|
// colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||||
)
|
// )
|
||||||
),
|
// )
|
||||||
|
,
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
when (val state = userState) {
|
when (val state = userState) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
@ -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", "🦜")
|
||||||
|
)
|
||||||
|
|
@ -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")
|
||||||
|
)
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
package com.example.livingai_lg.ui.models
|
||||||
|
|
||||||
|
class Seller {
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
@ -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"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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"))}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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))
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,25 +2,57 @@ package com.example.livingai_lg.ui.theme
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
val Purple80 = Color(0xFFD0BCFF)
|
// Farm Marketplace Color Palette
|
||||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
val FarmTextLight = Color(0xFFF5F1E8)
|
||||||
val Pink80 = Color(0xFFEFB8C8)
|
val FarmTextDark = Color(0xFF5D4E37) //Title
|
||||||
|
val FarmTextNormal = Color(0xFF8B7355)
|
||||||
|
val FarmTextLink = Color(0xFFE17100)
|
||||||
|
|
||||||
val Purple40 = Color(0xFF6650a4)
|
val FarmSproutBg = Color(0xFFFFCB79)
|
||||||
val PurpleGrey40 = Color(0xFF625b71)
|
val FarmSproutIcon = Color(0xFF8B6F47)
|
||||||
val Pink40 = Color(0xFF7D5260)
|
|
||||||
|
|
||||||
// Custom Colors from Figma
|
val FarmWheatBg = Color(0xFFFFDD88)
|
||||||
val LightCream = Color(0xFFFFFBEB)
|
val FarmWheatIcon = Color(0xFFD4A574)
|
||||||
val LighterCream = Color(0xFFFEFCE8)
|
|
||||||
val LightestGreen = Color(0xFFF7FEE7)
|
val FarmSunBg = Color(0xFFFFB179)
|
||||||
val Maize = Color(0xFFF7C35F)
|
val FarmSunIcon = Color(0xFFE07A5F)
|
||||||
val TerraCotta = Color(0xFFD97D5D)
|
|
||||||
val DarkBrown = Color(0xFF5D4E37)
|
val FarmGold = Color(0xFFFFB84D)
|
||||||
val MidBrown = Color(0xFF8B7355)
|
val FarmPink = Color(0xFFEDD5C8)
|
||||||
val DarkerBrown = Color(0xFF322410)
|
val FarmFence = Color(0xFFD4B5A0)
|
||||||
val OrangeBrown = Color(0xFFD4A574)
|
val FarmBox = Color(0xFFE8D9CC)
|
||||||
val DarkOrange = Color(0xFFE07A5F)
|
val FarmButtonPrimary = Color(0xFFFFB84D)
|
||||||
val Gold = Color(0xFFFFDD88)
|
val FarmButtonSecondary = Color(0xFFE07A5F)
|
||||||
val LightOrange = Color(0xFFFFB84D)
|
val FarmButtonText = Color(0xFF322410)
|
||||||
val DarkerOrange = Color(0xFFD96A4F)
|
|
||||||
|
// 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)
|
||||||
|
|
|
||||||
|
|
@ -3,56 +3,111 @@ package com.example.livingai_lg.ui.theme
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.material3.ColorScheme
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.darkColorScheme
|
import androidx.compose.material3.darkColorScheme
|
||||||
import androidx.compose.material3.dynamicDarkColorScheme
|
import androidx.compose.material3.dynamicDarkColorScheme
|
||||||
import androidx.compose.material3.dynamicLightColorScheme
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
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.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
|
|
||||||
private val DarkColorScheme = darkColorScheme(
|
private val lightScheme = lightColorScheme(
|
||||||
primary = Purple80,
|
primary = md_theme_light_primary,
|
||||||
secondary = PurpleGrey80,
|
onPrimary = md_theme_light_onPrimary,
|
||||||
tertiary = Pink80
|
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(
|
private val darkScheme = darkColorScheme(
|
||||||
primary = Purple40,
|
primary = Color(0xFFFFDDBD),
|
||||||
secondary = PurpleGrey40,
|
onPrimary = Color(0xFF3E3220),
|
||||||
tertiary = Pink40
|
primaryContainer = Color(0xFF57472D),
|
||||||
|
onPrimaryContainer = Color(0xFFFFDDBD),
|
||||||
/* Other default colors to override
|
secondary = Color(0xFFFFDDBD),
|
||||||
background = Color(0xFFFFFBFE),
|
onSecondary = Color(0xFF2B2415),
|
||||||
surface = Color(0xFFFFFBFE),
|
secondaryContainer = Color(0xFF6B5344),
|
||||||
onPrimary = Color.White,
|
onSecondaryContainer = Color(0xFFFFDDBD),
|
||||||
onSecondary = Color.White,
|
tertiary = Color(0xFFFFB893),
|
||||||
onTertiary = Color.White,
|
onTertiary = Color(0xFF3E2415),
|
||||||
onBackground = Color(0xFF1C1B1F),
|
tertiaryContainer = Color(0xFF8B5A3C),
|
||||||
onSurface = Color(0xFF1C1B1F),
|
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
|
@Composable
|
||||||
fun LivingAi_LgTheme(
|
fun FarmMarketplaceTheme(
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
useDarkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
// Dynamic color is available on Android 12+
|
|
||||||
dynamicColor: Boolean = true,
|
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
val colorScheme = when {
|
val colorScheme = when {
|
||||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
if (useDarkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
darkTheme -> DarkColorScheme
|
useDarkTheme -> darkScheme
|
||||||
else -> LightColorScheme
|
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(
|
MaterialTheme(
|
||||||
colorScheme = colorScheme,
|
colorScheme = colorScheme,
|
||||||
typography = Typography,
|
typography = typography,
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -6,29 +6,110 @@ import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.sp
|
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(
|
bodyLarge = TextStyle(
|
||||||
fontFamily = FontFamily.Default,
|
fontFamily = FontFamily.Default,
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
lineHeight = 24.sp,
|
lineHeight = 24.sp,
|
||||||
letterSpacing = 0.5.sp
|
letterSpacing = 0.15.sp,
|
||||||
)
|
),
|
||||||
/* Other default text styles to override
|
bodyMedium = TextStyle(
|
||||||
titleLarge = TextStyle(
|
|
||||||
fontFamily = FontFamily.Default,
|
fontFamily = FontFamily.Default,
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
fontSize = 22.sp,
|
fontSize = 14.sp,
|
||||||
lineHeight = 28.sp,
|
lineHeight = 20.sp,
|
||||||
letterSpacing = 0.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(
|
labelSmall = TextStyle(
|
||||||
fontFamily = FontFamily.Default,
|
fontFamily = FontFamily.Default,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
fontSize = 11.sp,
|
fontSize = 11.sp,
|
||||||
lineHeight = 16.sp,
|
lineHeight = 16.sp,
|
||||||
letterSpacing = 0.5.sp
|
letterSpacing = 0.5.sp,
|
||||||
)
|
),
|
||||||
*/
|
|
||||||
)
|
)
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
Loading…
Reference in New Issue