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)
|
||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
|
||||
|
||||
//UI
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
androidTestImplementation(platform("androidx.compose:compose-bom:2023.09.00"))
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
implementation("io.coil-kt:coil-compose:2.6.0")
|
||||
implementation("androidx.compose.foundation:foundation")
|
||||
implementation("androidx.compose.material:material-icons-extended")
|
||||
implementation("androidx.media3:media3-exoplayer:1.3.1")
|
||||
implementation("androidx.media3:media3-ui:1.3.1")
|
||||
}
|
||||
|
|
@ -25,6 +25,15 @@
|
|||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
@ -23,30 +23,32 @@ import com.example.livingai_lg.ui.AuthState
|
|||
import com.example.livingai_lg.ui.MainViewModel
|
||||
import com.example.livingai_lg.ui.MainViewModelFactory
|
||||
import com.example.livingai_lg.ui.login.*
|
||||
import com.example.livingai_lg.ui.theme.LivingAi_LgTheme
|
||||
import com.example.livingai_lg.ui.navigation.AppNavigation
|
||||
import com.example.livingai_lg.ui.theme.FarmMarketplaceTheme
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
LivingAi_LgTheme {
|
||||
FarmMarketplaceTheme {
|
||||
val mainViewModel: MainViewModel = viewModel(factory = MainViewModelFactory(LocalContext.current))
|
||||
val authState by mainViewModel.authState.collectAsState()
|
||||
|
||||
when (authState) {
|
||||
is AuthState.Unknown -> {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
is AuthState.Authenticated -> {
|
||||
SuccessScreen(mainViewModel)
|
||||
}
|
||||
is AuthState.Unauthenticated -> {
|
||||
AuthNavigation()
|
||||
}
|
||||
}
|
||||
AppNavigation(authState)
|
||||
// when (authState) {
|
||||
// is AuthState.Unknown -> {
|
||||
// Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
// CircularProgressIndicator()
|
||||
// }
|
||||
// }
|
||||
// is AuthState.Authenticated -> {
|
||||
// SuccessScreen(mainViewModel)
|
||||
// }
|
||||
// is AuthState.Unauthenticated -> {
|
||||
// AuthNavigation()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||
)
|
||||
)
|
||||
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -105,7 +101,7 @@ fun ProfileTypeItem(text: String, icon: Int, onClick: () -> Unit) {
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun CreateProfileScreenPreview() {
|
||||
LivingAi_LgTheme {
|
||||
FarmMarketplaceTheme {
|
||||
CreateProfileScreen(rememberNavController(), "John Doe")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -29,11 +30,11 @@ fun LoginScreen(navController: NavController) {
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||
)
|
||||
)
|
||||
// .background(
|
||||
// brush = Brush.linearGradient(
|
||||
// colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||
// )
|
||||
// )
|
||||
) {
|
||||
// Decorative elements...
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ fun LoginScreen(navController: NavController) {
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.size(56.dp)
|
||||
.background(Gold, RoundedCornerShape(28.dp)),
|
||||
.background(Color.Yellow, RoundedCornerShape(28.dp)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(text = "🌾", fontSize = 24.sp)
|
||||
|
|
@ -64,7 +65,7 @@ fun LoginScreen(navController: NavController) {
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.size(72.dp)
|
||||
.background(LightOrange.copy(alpha = 0.72f), RoundedCornerShape(36.dp)),
|
||||
.background(Color.Yellow.copy(alpha = 0.72f), RoundedCornerShape(36.dp)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(text = "🌱", fontSize = 32.sp)
|
||||
|
|
@ -73,7 +74,7 @@ fun LoginScreen(navController: NavController) {
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.size(56.dp)
|
||||
.background(DarkOrange.copy(alpha = 0.67f), RoundedCornerShape(28.dp)),
|
||||
.background(Color.Yellow.copy(alpha = 0.67f), RoundedCornerShape(28.dp)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(text = "☀️", fontSize = 24.sp)
|
||||
|
|
@ -86,13 +87,13 @@ fun LoginScreen(navController: NavController) {
|
|||
text = "Welcome!",
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = DarkBrown
|
||||
color = Color.Yellow
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = "Join the farm marketplace community",
|
||||
fontSize = 16.sp,
|
||||
color = MidBrown,
|
||||
color = Color.Yellow,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.height(48.dp))
|
||||
|
|
@ -101,12 +102,12 @@ fun LoginScreen(navController: NavController) {
|
|||
Button(
|
||||
onClick = { navController.navigate("signup") },
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
colors = ButtonDefaults.buttonColors(containerColor = LightOrange),
|
||||
colors = ButtonDefaults.buttonColors(containerColor = Color.Yellow),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
) {
|
||||
Text(text = "New user? Sign up", color = DarkerBrown, fontSize = 16.sp, fontWeight = FontWeight.Medium)
|
||||
Text(text = "New user? Sign up", color = Color.Yellow, fontSize = 16.sp, fontWeight = FontWeight.Medium)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
|
@ -115,12 +116,12 @@ fun LoginScreen(navController: NavController) {
|
|||
Button(
|
||||
onClick = { navController.navigate("signin") },
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
colors = ButtonDefaults.buttonColors(containerColor = TerraCotta),
|
||||
colors = ButtonDefaults.buttonColors(containerColor = Color.Yellow),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
) {
|
||||
Text(text = "Already a user? Sign in", color = DarkerBrown, fontSize = 16.sp, fontWeight = FontWeight.Medium)
|
||||
Text(text = "Already a user? Sign in", color = Color.Yellow, fontSize = 16.sp, fontWeight = FontWeight.Medium)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
|
@ -128,7 +129,7 @@ fun LoginScreen(navController: NavController) {
|
|||
// Guest Button
|
||||
Text(
|
||||
text = "Continue as Guest",
|
||||
color = MidBrown,
|
||||
color = Color.Yellow,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.clickable { Toast.makeText(context, "Guest mode is not yet available", Toast.LENGTH_SHORT).show() }
|
||||
|
|
@ -142,7 +143,7 @@ fun LoginScreen(navController: NavController) {
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun LoginScreenPreview() {
|
||||
LivingAi_LgTheme {
|
||||
FarmMarketplaceTheme() {
|
||||
LoginScreen(rememberNavController())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,11 +40,11 @@ fun OtpScreen(navController: NavController, phoneNumber: String, name: String) {
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||
)
|
||||
)
|
||||
// .background(
|
||||
// brush = Brush.linearGradient(
|
||||
// colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||
// )
|
||||
// )
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -60,7 +60,7 @@ fun OtpScreen(navController: NavController, phoneNumber: String, name: String) {
|
|||
|
||||
TextField(
|
||||
value = otp.value,
|
||||
onValueChange = { if (it.length <= 6) otp.value = it },
|
||||
onValueChange = { if (it.length <= 6) otp.value = it },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(60.dp)
|
||||
|
|
@ -117,7 +117,7 @@ fun OtpScreen(navController: NavController, phoneNumber: String, name: String) {
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun OtpScreenPreview() {
|
||||
LivingAi_LgTheme {
|
||||
FarmMarketplaceTheme() {
|
||||
OtpScreen(rememberNavController(), "+919876543210", "John Doe")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,11 +42,11 @@ fun SignInScreen(navController: NavController) {
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||
)
|
||||
)
|
||||
// .background(
|
||||
// brush = Brush.linearGradient(
|
||||
// colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||
// )
|
||||
// )
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -161,7 +161,7 @@ fun SignInScreen(navController: NavController) {
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun SignInScreenPreview() {
|
||||
LivingAi_LgTheme {
|
||||
FarmMarketplaceTheme() {
|
||||
SignInScreen(rememberNavController())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,11 +45,11 @@ fun SignUpScreen(navController: NavController) {
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||
)
|
||||
)
|
||||
// .background(
|
||||
// brush = Brush.linearGradient(
|
||||
// colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||
// )
|
||||
// )
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -190,7 +190,7 @@ fun SignUpScreen(navController: NavController) {
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun SignUpScreenPreview() {
|
||||
LivingAi_LgTheme {
|
||||
FarmMarketplaceTheme {
|
||||
SignUpScreen(rememberNavController())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,6 @@ import androidx.compose.ui.unit.sp
|
|||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.example.livingai_lg.ui.MainViewModel
|
||||
import com.example.livingai_lg.ui.UserState
|
||||
import com.example.livingai_lg.ui.theme.LightCream
|
||||
import com.example.livingai_lg.ui.theme.LighterCream
|
||||
import com.example.livingai_lg.ui.theme.LightestGreen
|
||||
import com.example.livingai_lg.ui.MainViewModelFactory // <-- This was the missing import
|
||||
|
||||
@Composable
|
||||
|
|
@ -28,11 +25,12 @@ fun SuccessScreen(mainViewModel: MainViewModel = viewModel(factory = MainViewMod
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||
)
|
||||
),
|
||||
// .background(
|
||||
// brush = Brush.linearGradient(
|
||||
// colors = listOf(LightCream, LighterCream, LightestGreen)
|
||||
// )
|
||||
// )
|
||||
,
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
when (val state = userState) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
// Farm Marketplace Color Palette
|
||||
val FarmTextLight = Color(0xFFF5F1E8)
|
||||
val FarmTextDark = Color(0xFF5D4E37) //Title
|
||||
val FarmTextNormal = Color(0xFF8B7355)
|
||||
val FarmTextLink = Color(0xFFE17100)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
val FarmSproutBg = Color(0xFFFFCB79)
|
||||
val FarmSproutIcon = Color(0xFF8B6F47)
|
||||
|
||||
// Custom Colors from Figma
|
||||
val LightCream = Color(0xFFFFFBEB)
|
||||
val LighterCream = Color(0xFFFEFCE8)
|
||||
val LightestGreen = Color(0xFFF7FEE7)
|
||||
val Maize = Color(0xFFF7C35F)
|
||||
val TerraCotta = Color(0xFFD97D5D)
|
||||
val DarkBrown = Color(0xFF5D4E37)
|
||||
val MidBrown = Color(0xFF8B7355)
|
||||
val DarkerBrown = Color(0xFF322410)
|
||||
val OrangeBrown = Color(0xFFD4A574)
|
||||
val DarkOrange = Color(0xFFE07A5F)
|
||||
val Gold = Color(0xFFFFDD88)
|
||||
val LightOrange = Color(0xFFFFB84D)
|
||||
val DarkerOrange = Color(0xFFD96A4F)
|
||||
val FarmWheatBg = Color(0xFFFFDD88)
|
||||
val FarmWheatIcon = Color(0xFFD4A574)
|
||||
|
||||
val FarmSunBg = Color(0xFFFFB179)
|
||||
val FarmSunIcon = Color(0xFFE07A5F)
|
||||
|
||||
val FarmGold = Color(0xFFFFB84D)
|
||||
val FarmPink = Color(0xFFEDD5C8)
|
||||
val FarmFence = Color(0xFFD4B5A0)
|
||||
val FarmBox = Color(0xFFE8D9CC)
|
||||
val FarmButtonPrimary = Color(0xFFFFB84D)
|
||||
val FarmButtonSecondary = Color(0xFFE07A5F)
|
||||
val FarmButtonText = Color(0xFF322410)
|
||||
|
||||
// Material Design 3 Color Scheme
|
||||
val md_theme_light_primary = Color(0xFF5D4E37)
|
||||
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
|
||||
val md_theme_light_primaryContainer = Color(0xFFF5F1E8)
|
||||
val md_theme_light_onPrimaryContainer = Color(0xFF3E3220)
|
||||
val md_theme_light_secondary = Color(0xFF8B7355)
|
||||
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
|
||||
val md_theme_light_secondaryContainer = Color(0xFFFFDDBD)
|
||||
val md_theme_light_onSecondaryContainer = Color(0xFF2B2415)
|
||||
val md_theme_light_tertiary = Color(0xFFE07A5F)
|
||||
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
|
||||
val md_theme_light_tertiaryContainer = Color(0xFFFFDDBD)
|
||||
val md_theme_light_onTertiaryContainer = Color(0xFF3E2415)
|
||||
val md_theme_light_error = Color(0xFFB3261E)
|
||||
val md_theme_light_onError = Color(0xFFFFFFFF)
|
||||
val md_theme_light_errorContainer = Color(0xFFF9DEDC)
|
||||
val md_theme_light_onErrorContainer = Color(0xFF410E0B)
|
||||
val md_theme_light_background = Color(0xFFFBF8F3)
|
||||
val md_theme_light_onBackground = Color(0xFF1C1B1F)
|
||||
val md_theme_light_surface = Color(0xFFFBF8F3)
|
||||
val md_theme_light_onSurface = Color(0xFF1C1B1F)
|
||||
val md_theme_light_surfaceVariant = Color(0xFFEFE0D6)
|
||||
val md_theme_light_onSurfaceVariant = Color(0xFF49454E)
|
||||
val md_theme_light_outline = Color(0xFF79747E)
|
||||
val md_theme_light_inverseOnSurface = Color(0xFFF4EFF4)
|
||||
val md_theme_light_inverseSurface = Color(0xFF313033)
|
||||
val md_theme_light_inversePrimary = Color(0xFFFFDDBD)
|
||||
val md_theme_light_shadow = Color(0xFF000000)
|
||||
val md_theme_light_surfaceTint = Color(0xFF5D4E37)
|
||||
val md_theme_light_outlineVariant = Color(0xFFD3C4BC)
|
||||
val md_theme_light_scrim = Color(0xFF000000)
|
||||
|
|
|
|||
|
|
@ -3,56 +3,111 @@ package com.example.livingai_lg.ui.theme
|
|||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
private val lightScheme = lightColorScheme(
|
||||
primary = md_theme_light_primary,
|
||||
onPrimary = md_theme_light_onPrimary,
|
||||
primaryContainer = md_theme_light_primaryContainer,
|
||||
onPrimaryContainer = md_theme_light_onPrimaryContainer,
|
||||
secondary = md_theme_light_secondary,
|
||||
onSecondary = md_theme_light_onSecondary,
|
||||
secondaryContainer = md_theme_light_secondaryContainer,
|
||||
onSecondaryContainer = md_theme_light_onSecondaryContainer,
|
||||
tertiary = md_theme_light_tertiary,
|
||||
onTertiary = md_theme_light_onTertiary,
|
||||
tertiaryContainer = md_theme_light_tertiaryContainer,
|
||||
onTertiaryContainer = md_theme_light_onTertiaryContainer,
|
||||
error = md_theme_light_error,
|
||||
onError = md_theme_light_onError,
|
||||
errorContainer = md_theme_light_errorContainer,
|
||||
onErrorContainer = md_theme_light_onErrorContainer,
|
||||
background = md_theme_light_background,
|
||||
onBackground = md_theme_light_onBackground,
|
||||
surface = md_theme_light_surface,
|
||||
onSurface = md_theme_light_onSurface,
|
||||
surfaceVariant = md_theme_light_surfaceVariant,
|
||||
onSurfaceVariant = md_theme_light_onSurfaceVariant,
|
||||
outline = md_theme_light_outline,
|
||||
inverseOnSurface = md_theme_light_inverseOnSurface,
|
||||
inverseSurface = md_theme_light_inverseSurface,
|
||||
inversePrimary = md_theme_light_inversePrimary,
|
||||
surfaceTint = md_theme_light_surfaceTint,
|
||||
outlineVariant = md_theme_light_outlineVariant,
|
||||
scrim = md_theme_light_scrim,
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
private val darkScheme = darkColorScheme(
|
||||
primary = Color(0xFFFFDDBD),
|
||||
onPrimary = Color(0xFF3E3220),
|
||||
primaryContainer = Color(0xFF57472D),
|
||||
onPrimaryContainer = Color(0xFFFFDDBD),
|
||||
secondary = Color(0xFFFFDDBD),
|
||||
onSecondary = Color(0xFF2B2415),
|
||||
secondaryContainer = Color(0xFF6B5344),
|
||||
onSecondaryContainer = Color(0xFFFFDDBD),
|
||||
tertiary = Color(0xFFFFB893),
|
||||
onTertiary = Color(0xFF3E2415),
|
||||
tertiaryContainer = Color(0xFF8B5A3C),
|
||||
onTertiaryContainer = Color(0xFFFFDDBD),
|
||||
error = Color(0xFFFFB4AB),
|
||||
onError = Color(0xFF690005),
|
||||
errorContainer = Color(0xFF93000A),
|
||||
onErrorContainer = Color(0xFFFFDAD6),
|
||||
background = Color(0xFF1C1B1F),
|
||||
onBackground = Color(0xFFE7E0E8),
|
||||
surface = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFFE7E0E8),
|
||||
surfaceVariant = Color(0xFF49454E),
|
||||
onSurfaceVariant = Color(0xFFCAC7D0),
|
||||
outline = Color(0xFF94919A),
|
||||
inverseOnSurface = Color(0xFF1C1B1F),
|
||||
inverseSurface = Color(0xFFE7E0E8),
|
||||
inversePrimary = Color(0xFF5D4E37),
|
||||
surfaceTint = Color(0xFFFFDDBD),
|
||||
outlineVariant = Color(0xFF49454E),
|
||||
scrim = Color(0xFF000000),
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun LivingAi_LgTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
fun FarmMarketplaceTheme(
|
||||
useDarkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
if (useDarkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
useDarkTheme -> darkScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
window.statusBarColor = colorScheme.primary.toArgb()
|
||||
WindowCompat.getInsetsController(window, view)?.isAppearanceLightStatusBars =
|
||||
!useDarkTheme
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
typography = typography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,29 +6,110 @@ import androidx.compose.ui.text.font.FontFamily
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
val typography = Typography(
|
||||
displayLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 57.sp,
|
||||
lineHeight = 64.sp,
|
||||
letterSpacing = (-0.25).sp,
|
||||
),
|
||||
displayMedium = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 45.sp,
|
||||
lineHeight = 52.sp,
|
||||
letterSpacing = 0.sp,
|
||||
),
|
||||
displaySmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 36.sp,
|
||||
lineHeight = 44.sp,
|
||||
letterSpacing = 0.sp,
|
||||
),
|
||||
headlineLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 32.sp,
|
||||
lineHeight = 40.sp,
|
||||
letterSpacing = 0.sp,
|
||||
),
|
||||
headlineMedium = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 28.sp,
|
||||
lineHeight = 36.sp,
|
||||
letterSpacing = 0.sp,
|
||||
),
|
||||
headlineSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 24.sp,
|
||||
lineHeight = 32.sp,
|
||||
letterSpacing = 0.sp,
|
||||
),
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp,
|
||||
),
|
||||
titleMedium = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.15.sp,
|
||||
),
|
||||
titleSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 14.sp,
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = 0.1.sp,
|
||||
),
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
letterSpacing = 0.15.sp,
|
||||
),
|
||||
bodyMedium = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
fontSize = 14.sp,
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = 0.25.sp,
|
||||
),
|
||||
bodySmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 12.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.4.sp,
|
||||
),
|
||||
labelLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 14.sp,
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = 0.1.sp,
|
||||
),
|
||||
labelMedium = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 12.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp,
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
||||
letterSpacing = 0.5.sp,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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