From 4f8ae8882007486df2f5a2e8d05be39103e765f6 Mon Sep 17 00:00:00 2001 From: ankitsaraf Date: Tue, 16 Dec 2025 20:12:51 +0530 Subject: [PATCH] moved the UI repo integrated existing logic with new screens --- .idea/.name | 1 + app/build.gradle.kts | 15 + app/src/main/AndroidManifest.xml | 9 + .../com/example/livingai_lg/MainActivity.kt | 32 +- .../ui/components/AdSpaceBanner.kt | 42 ++ .../ui/components/AnimalTypeSelector.kt | 87 +++ .../ui/components/BottomNavigationBar.kt | 93 +++ .../ui/components/BuyAnimalCard.kt | 225 +++++++ .../ui/components/DropdownInput.kt | 113 ++++ .../livingai_lg/ui/components/FarmHeader.kt | 40 ++ .../livingai_lg/ui/components/FilterButton.kt | 58 ++ .../ui/components/FloatingActionBar.kt | 77 +++ .../ui/components/IconComponents.kt | 33 ++ .../ui/components/ImageCarousel.kt | 146 +++++ .../livingai_lg/ui/components/InputField.kt | 67 +++ .../ui/components/MediaPickerCard.kt | 280 +++++++++ .../livingai_lg/ui/components/OptionCard.kt | 81 +++ .../ui/components/PhoneNumberInput.kt | 108 ++++ .../livingai_lg/ui/components/RangeFilter.kt | 137 +++++ .../livingai_lg/ui/components/SortButton.kt | 56 ++ .../livingai_lg/ui/components/SortItem.kt | 103 ++++ .../ui/components/UserLocationHeader.kt | 153 +++++ .../ui/components/VideoPlayerDialog.kt | 60 ++ .../backgrounds/DecorativeBackground.kt | 106 ++++ .../components/backgrounds/StoreBackground.kt | 50 ++ .../ui/layout/BottomNavScaffold.kt | 38 ++ .../ui/login/CreateProfileScreen.kt | 8 +- .../livingai_lg/ui/login/LoginScreen.kt | 33 +- .../example/livingai_lg/ui/login/OtpScreen.kt | 14 +- .../livingai_lg/ui/login/SignInScreen.kt | 12 +- .../livingai_lg/ui/login/SignUpScreen.kt | 12 +- .../livingai_lg/ui/login/SuccessScreen.kt | 14 +- .../example/livingai_lg/ui/models/Animal.kt | 74 +++ .../livingai_lg/ui/models/AnimalType.kt | 18 + .../ui/models/BottomNavItemData.kt | 27 + .../livingai_lg/ui/models/NewAnimalForm.kt | 28 + .../livingai_lg/ui/models/ProfileType.kt | 38 ++ .../example/livingai_lg/ui/models/Seller.kt | 4 + .../com/example/livingai_lg/ui/models/Sort.kt | 14 + .../com/example/livingai_lg/ui/models/User.kt | 30 + .../livingai_lg/ui/models/mediaType.kt | 19 + .../ui/navigation/AppNavigation.kt | 338 +++++++++++ .../livingai_lg/ui/navigation/AuthNavGraph.kt | 70 +++ .../livingai_lg/ui/navigation/MainNavGraph.kt | 230 +++++++ .../ui/screens/AnimalProfileScreen.kt | 371 ++++++++++++ .../livingai_lg/ui/screens/BuyScreen.kt | 170 ++++++ .../ui/screens/ChooseServiceScreen.kt | 227 +++++++ .../ui/screens/CreateProfileScreen.kt | 117 ++++ .../livingai_lg/ui/screens/FilterScreen.kt | 552 +++++++++++++++++ .../ui/screens/NewListingScreen.kt | 473 +++++++++++++++ .../livingai_lg/ui/screens/PostSaleSurvey.kt | 523 ++++++++++++++++ .../ui/screens/SaleArchiveScreen.kt | 355 +++++++++++ .../ui/screens/SellerProfileScreen.kt | 560 ++++++++++++++++++ .../livingai_lg/ui/screens/SortScreen.kt | 168 ++++++ .../ui/screens/auth/LandingScreen.kt | 203 +++++++ .../livingai_lg/ui/screens/auth/OTPScreen.kt | 287 +++++++++ .../ui/screens/auth/SignInScreen.kt | 136 +++++ .../ui/screens/auth/SignUpScreen.kt | 203 +++++++ .../com/example/livingai_lg/ui/theme/Color.kt | 72 ++- .../com/example/livingai_lg/ui/theme/Theme.kt | 111 +++- .../com/example/livingai_lg/ui/theme/Type.kt | 107 +++- .../livingai_lg/ui/utils/FormatUtils.kt | 61 ++ .../livingai_lg/ui/utils/KeyboardUtils.kt | 12 + .../livingai_lg/ui/utils/MediaUtils.kt | 51 ++ app/src/main/res/drawable/bg.xml | 180 ++++++ app/src/main/res/drawable/bg_shop.xml | 149 +++++ app/src/main/res/drawable/ic_bag.xml | 27 + app/src/main/res/drawable/ic_bookmark.xml | 13 + .../main/res/drawable/ic_bookmark_plus.xml | 9 + app/src/main/res/drawable/ic_chat.xml | 9 + app/src/main/res/drawable/ic_config.xml | 9 + app/src/main/res/drawable/ic_filter.xml | 17 + app/src/main/res/drawable/ic_home.xml | 9 + app/src/main/res/drawable/ic_location.xml | 24 + app/src/main/res/drawable/ic_market.xml | 9 + .../res/drawable/ic_notification_unread.xml | 9 + app/src/main/res/drawable/ic_other.xml | 13 + app/src/main/res/drawable/ic_phone.xml | 9 + app/src/main/res/drawable/ic_profile.xml | 9 + app/src/main/res/drawable/ic_shop.xml | 27 + app/src/main/res/drawable/ic_shop2.xml | 13 + app/src/main/res/drawable/ic_sort.xml | 34 ++ app/src/main/res/drawable/ic_spanner.xml | 13 + app/src/main/res/drawable/ic_sprout.xml | 27 + app/src/main/res/drawable/ic_star.xml | 9 + app/src/main/res/drawable/ic_star_half.xml | 9 + app/src/main/res/drawable/ic_sun.xml | 69 +++ app/src/main/res/drawable/ic_supplier.xml | 13 + app/src/main/res/drawable/ic_tag.xml | 9 + app/src/main/res/drawable/ic_thumbs_up.xml | 9 + app/src/main/res/drawable/ic_view.xml | 9 + app/src/main/res/drawable/ic_wheat.xml | 62 ++ app/src/main/res/xml/file_paths.xml | 7 + 93 files changed, 8632 insertions(+), 125 deletions(-) create mode 100644 .idea/.name create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/AdSpaceBanner.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/AnimalTypeSelector.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/BottomNavigationBar.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/BuyAnimalCard.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/DropdownInput.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/FarmHeader.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/FilterButton.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/FloatingActionBar.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/IconComponents.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/ImageCarousel.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/InputField.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/MediaPickerCard.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/OptionCard.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/PhoneNumberInput.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/RangeFilter.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/SortButton.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/SortItem.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/UserLocationHeader.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/VideoPlayerDialog.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/backgrounds/DecorativeBackground.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/components/backgrounds/StoreBackground.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/layout/BottomNavScaffold.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/models/Animal.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/models/AnimalType.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/models/BottomNavItemData.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/models/NewAnimalForm.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/models/ProfileType.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/models/Seller.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/models/Sort.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/models/User.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/models/mediaType.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/navigation/AppNavigation.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/navigation/AuthNavGraph.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/navigation/MainNavGraph.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/AnimalProfileScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/BuyScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/ChooseServiceScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/CreateProfileScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/FilterScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/NewListingScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/PostSaleSurvey.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/SaleArchiveScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/SellerProfileScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/SortScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/auth/LandingScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/auth/OTPScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignInScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignUpScreen.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/utils/FormatUtils.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/utils/KeyboardUtils.kt create mode 100644 app/src/main/java/com/example/livingai_lg/ui/utils/MediaUtils.kt create mode 100644 app/src/main/res/drawable/bg.xml create mode 100644 app/src/main/res/drawable/bg_shop.xml create mode 100644 app/src/main/res/drawable/ic_bag.xml create mode 100644 app/src/main/res/drawable/ic_bookmark.xml create mode 100644 app/src/main/res/drawable/ic_bookmark_plus.xml create mode 100644 app/src/main/res/drawable/ic_chat.xml create mode 100644 app/src/main/res/drawable/ic_config.xml create mode 100644 app/src/main/res/drawable/ic_filter.xml create mode 100644 app/src/main/res/drawable/ic_home.xml create mode 100644 app/src/main/res/drawable/ic_location.xml create mode 100644 app/src/main/res/drawable/ic_market.xml create mode 100644 app/src/main/res/drawable/ic_notification_unread.xml create mode 100644 app/src/main/res/drawable/ic_other.xml create mode 100644 app/src/main/res/drawable/ic_phone.xml create mode 100644 app/src/main/res/drawable/ic_profile.xml create mode 100644 app/src/main/res/drawable/ic_shop.xml create mode 100644 app/src/main/res/drawable/ic_shop2.xml create mode 100644 app/src/main/res/drawable/ic_sort.xml create mode 100644 app/src/main/res/drawable/ic_spanner.xml create mode 100644 app/src/main/res/drawable/ic_sprout.xml create mode 100644 app/src/main/res/drawable/ic_star.xml create mode 100644 app/src/main/res/drawable/ic_star_half.xml create mode 100644 app/src/main/res/drawable/ic_sun.xml create mode 100644 app/src/main/res/drawable/ic_supplier.xml create mode 100644 app/src/main/res/drawable/ic_tag.xml create mode 100644 app/src/main/res/drawable/ic_thumbs_up.xml create mode 100644 app/src/main/res/drawable/ic_view.xml create mode 100644 app/src/main/res/drawable/ic_wheat.xml create mode 100644 app/src/main/res/xml/file_paths.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..2e03d9b --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +LivingAi_Lg \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ba7d270..e190b63 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3a291db..8456dac 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,6 +25,15 @@ + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/livingai_lg/MainActivity.kt b/app/src/main/java/com/example/livingai_lg/MainActivity.kt index 70e935c..6281511 100644 --- a/app/src/main/java/com/example/livingai_lg/MainActivity.kt +++ b/app/src/main/java/com/example/livingai_lg/MainActivity.kt @@ -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() +// } +// } } } } diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/AdSpaceBanner.kt b/app/src/main/java/com/example/livingai_lg/ui/components/AdSpaceBanner.kt new file mode 100644 index 0000000..98a9527 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/AdSpaceBanner.kt @@ -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) + ) + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/AnimalTypeSelector.kt b/app/src/main/java/com/example/livingai_lg/ui/components/AnimalTypeSelector.kt new file mode 100644 index 0000000..ad930c7 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/AnimalTypeSelector.kt @@ -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, + selectedAnimalType: MutableState, + 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 + ) + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/BottomNavigationBar.kt b/app/src/main/java/com/example/livingai_lg/ui/components/BottomNavigationBar.kt new file mode 100644 index 0000000..fbb47bf --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/BottomNavigationBar.kt @@ -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, + 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 + ) + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/BuyAnimalCard.kt b/app/src/main/java/com/example/livingai_lg/ui/components/BuyAnimalCard.kt new file mode 100644 index 0000000..6d70097 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/BuyAnimalCard.kt @@ -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) + ) + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/DropdownInput.kt b/app/src/main/java/com/example/livingai_lg/ui/components/DropdownInput.kt new file mode 100644 index 0000000..3620a62 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/DropdownInput.kt @@ -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, + 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) + } + ) + } + } + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/FarmHeader.kt b/app/src/main/java/com/example/livingai_lg/ui/components/FarmHeader.kt new file mode 100644 index 0000000..1e51194 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/FarmHeader.kt @@ -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 + ) +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/FilterButton.kt b/app/src/main/java/com/example/livingai_lg/ui/components/FilterButton.kt new file mode 100644 index 0000000..5e07ebe --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/FilterButton.kt @@ -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) + ) + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/FloatingActionBar.kt b/app/src/main/java/com/example/livingai_lg/ui/components/FloatingActionBar.kt new file mode 100644 index 0000000..0f094cd --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/FloatingActionBar.kt @@ -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) + ) + } +} + diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/IconComponents.kt b/app/src/main/java/com/example/livingai_lg/ui/components/IconComponents.kt new file mode 100644 index 0000000..80e5cb4 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/IconComponents.kt @@ -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() + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/ImageCarousel.kt b/app/src/main/java/com/example/livingai_lg/ui/components/ImageCarousel.kt new file mode 100644 index 0000000..f0b27d3 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/ImageCarousel.kt @@ -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, + 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 + ) + } +} + diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/InputField.kt b/app/src/main/java/com/example/livingai_lg/ui/components/InputField.kt new file mode 100644 index 0000000..02e3738 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/InputField.kt @@ -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() + } + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/MediaPickerCard.kt b/app/src/main/java/com/example/livingai_lg/ui/components/MediaPickerCard.kt new file mode 100644 index 0000000..b875b8c --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/MediaPickerCard.kt @@ -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(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 } + ) + } + } +} + diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/OptionCard.kt b/app/src/main/java/com/example/livingai_lg/ui/components/OptionCard.kt new file mode 100644 index 0000000..2e5d4d5 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/OptionCard.kt @@ -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) + ) + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/PhoneNumberInput.kt b/app/src/main/java/com/example/livingai_lg/ui/components/PhoneNumberInput.kt new file mode 100644 index 0000000..b67ee9b --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/PhoneNumberInput.kt @@ -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() + } + ) + } + } + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/RangeFilter.kt b/app/src/main/java/com/example/livingai_lg/ui/components/RangeFilter.kt new file mode 100644 index 0000000..a38cb52 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/RangeFilter.kt @@ -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) + ) + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/SortButton.kt b/app/src/main/java/com/example/livingai_lg/ui/components/SortButton.kt new file mode 100644 index 0000000..02a68e9 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/SortButton.kt @@ -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) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/SortItem.kt b/app/src/main/java/com/example/livingai_lg/ui/components/SortItem.kt new file mode 100644 index 0000000..27a7391 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/SortItem.kt @@ -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 + ) + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/UserLocationHeader.kt b/app/src/main/java/com/example/livingai_lg/ui/components/UserLocationHeader.kt new file mode 100644 index 0000000..d19b3f5 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/UserLocationHeader.kt @@ -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() + } + ) + } + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/VideoPlayerDialog.kt b/app/src/main/java/com/example/livingai_lg/ui/components/VideoPlayerDialog.kt new file mode 100644 index 0000000..5cc2261 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/VideoPlayerDialog.kt @@ -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() + ) + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/backgrounds/DecorativeBackground.kt b/app/src/main/java/com/example/livingai_lg/ui/components/backgrounds/DecorativeBackground.kt new file mode 100644 index 0000000..7b86ce8 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/backgrounds/DecorativeBackground.kt @@ -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) + ) +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/components/backgrounds/StoreBackground.kt b/app/src/main/java/com/example/livingai_lg/ui/components/backgrounds/StoreBackground.kt new file mode 100644 index 0000000..78146c7 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/components/backgrounds/StoreBackground.kt @@ -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 + ) + } +} + diff --git a/app/src/main/java/com/example/livingai_lg/ui/layout/BottomNavScaffold.kt b/app/src/main/java/com/example/livingai_lg/ui/layout/BottomNavScaffold.kt new file mode 100644 index 0000000..1416918 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/layout/BottomNavScaffold.kt @@ -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, + 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) + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/login/CreateProfileScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/login/CreateProfileScreen.kt index a33abfe..53e2a83 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/login/CreateProfileScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/login/CreateProfileScreen.kt @@ -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") } } diff --git a/app/src/main/java/com/example/livingai_lg/ui/login/LoginScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/login/LoginScreen.kt index 99c7fb0..c04ba19 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/login/LoginScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/login/LoginScreen.kt @@ -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()) } } diff --git a/app/src/main/java/com/example/livingai_lg/ui/login/OtpScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/login/OtpScreen.kt index 0a4f3e6..16e3154 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/login/OtpScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/login/OtpScreen.kt @@ -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") } } diff --git a/app/src/main/java/com/example/livingai_lg/ui/login/SignInScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/login/SignInScreen.kt index 1e468df..32d0cbb 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/login/SignInScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/login/SignInScreen.kt @@ -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()) } } diff --git a/app/src/main/java/com/example/livingai_lg/ui/login/SignUpScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/login/SignUpScreen.kt index 9e8672c..da8f0e6 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/login/SignUpScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/login/SignUpScreen.kt @@ -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()) } } diff --git a/app/src/main/java/com/example/livingai_lg/ui/login/SuccessScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/login/SuccessScreen.kt index be96d44..a5e23b2 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/login/SuccessScreen.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/login/SuccessScreen.kt @@ -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) { diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/Animal.kt b/app/src/main/java/com/example/livingai_lg/ui/models/Animal.kt new file mode 100644 index 0000000..1861b93 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/models/Animal.kt @@ -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? = 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 + ) +) diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/AnimalType.kt b/app/src/main/java/com/example/livingai_lg/ui/models/AnimalType.kt new file mode 100644 index 0000000..79209c2 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/models/AnimalType.kt @@ -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", "🦜") +) \ No newline at end of file diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/BottomNavItemData.kt b/app/src/main/java/com/example/livingai_lg/ui/models/BottomNavItemData.kt new file mode 100644 index 0000000..d6501d0 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/models/BottomNavItemData.kt @@ -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") +) diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/NewAnimalForm.kt b/app/src/main/java/com/example/livingai_lg/ui/models/NewAnimalForm.kt new file mode 100644 index 0000000..07b09cd --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/models/NewAnimalForm.kt @@ -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) + ) +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/ProfileType.kt b/app/src/main/java/com/example/livingai_lg/ui/models/ProfileType.kt new file mode 100644 index 0000000..309245f --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/models/ProfileType.kt @@ -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) + ) +) diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/Seller.kt b/app/src/main/java/com/example/livingai_lg/ui/models/Seller.kt new file mode 100644 index 0000000..cdb0495 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/models/Seller.kt @@ -0,0 +1,4 @@ +package com.example.livingai_lg.ui.models + +class Seller { +} \ No newline at end of file diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/Sort.kt b/app/src/main/java/com/example/livingai_lg/ui/models/Sort.kt new file mode 100644 index 0000000..825aac1 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/models/Sort.kt @@ -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 +) diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/User.kt b/app/src/main/java/com/example/livingai_lg/ui/models/User.kt new file mode 100644 index 0000000..4af6432 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/models/User.kt @@ -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 +) + +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" + ) + ) + ) diff --git a/app/src/main/java/com/example/livingai_lg/ui/models/mediaType.kt b/app/src/main/java/com/example/livingai_lg/ui/models/mediaType.kt new file mode 100644 index 0000000..7719bd8 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/models/mediaType.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/com/example/livingai_lg/ui/navigation/AppNavigation.kt b/app/src/main/java/com/example/livingai_lg/ui/navigation/AppNavigation.kt new file mode 100644 index 0000000..140f78d --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/navigation/AppNavigation.kt @@ -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() +// } +// ) +// } + + +// } +} + + diff --git a/app/src/main/java/com/example/livingai_lg/ui/navigation/AuthNavGraph.kt b/app/src/main/java/com/example/livingai_lg/ui/navigation/AuthNavGraph.kt new file mode 100644 index 0000000..092b125 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/navigation/AuthNavGraph.kt @@ -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"))} + ) + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/navigation/MainNavGraph.kt b/app/src/main/java/com/example/livingai_lg/ui/navigation/MainNavGraph.kt new file mode 100644 index 0000000..d5dcfb6 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/navigation/MainNavGraph.kt @@ -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() + } + ) + }} +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/AnimalProfileScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/AnimalProfileScreen.kt new file mode 100644 index 0000000..13b84a7 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/AnimalProfileScreen.kt @@ -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) + ) + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/BuyScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/BuyScreen.kt new file mode 100644 index 0000000..d66fff0 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/BuyScreen.kt @@ -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(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)) + + + } + } + } + + + } +} + + + + + diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/ChooseServiceScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/ChooseServiceScreen.kt new file mode 100644 index 0000000..62cb862 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/ChooseServiceScreen.kt @@ -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(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) + ) + ) + } + } +} + diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/CreateProfileScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/CreateProfileScreen.kt new file mode 100644 index 0000000..25511bc --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/CreateProfileScreen.kt @@ -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(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)) + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/FilterScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/FilterScreen.kt new file mode 100644 index 0000000..5f4c9d6 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/FilterScreen.kt @@ -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()) } + + 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) + ) + } + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/NewListingScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/NewListingScreen.kt new file mode 100644 index 0000000..4792fd5 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/NewListingScreen.kt @@ -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)) + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/PostSaleSurvey.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/PostSaleSurvey.kt new file mode 100644 index 0000000..ee28d9e --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/PostSaleSurvey.kt @@ -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 = 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 + ) + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/SaleArchiveScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/SaleArchiveScreen.kt new file mode 100644 index 0000000..4cd3517 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/SaleArchiveScreen.kt @@ -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 +) + +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) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/SellerProfileScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/SellerProfileScreen.kt new file mode 100644 index 0000000..d7d0484 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/SellerProfileScreen.kt @@ -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) + ) + } + } + } + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/SortScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/SortScreen.kt new file mode 100644 index 0000000..57043e2 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/SortScreen.kt @@ -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) -> 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)) + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/auth/LandingScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/auth/LandingScreen.kt new file mode 100644 index 0000000..37a8103 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/auth/LandingScreen.kt @@ -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)) + } + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/auth/OTPScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/auth/OTPScreen.kt new file mode 100644 index 0000000..436024e --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/auth/OTPScreen.kt @@ -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) + ) + } +} + diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignInScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignInScreen.kt new file mode 100644 index 0000000..bfa0e4d --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignInScreen.kt @@ -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)) + } + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignUpScreen.kt b/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignUpScreen.kt new file mode 100644 index 0000000..e45d56a --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/screens/auth/SignUpScreen.kt @@ -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)) + } + } + } +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/theme/Color.kt b/app/src/main/java/com/example/livingai_lg/ui/theme/Color.kt index 33f0480..a7ec9c6 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/theme/Color.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/theme/Color.kt @@ -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) diff --git a/app/src/main/java/com/example/livingai_lg/ui/theme/Theme.kt b/app/src/main/java/com/example/livingai_lg/ui/theme/Theme.kt index 9a570d3..3863d35 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/theme/Theme.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/theme/Theme.kt @@ -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 ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/theme/Type.kt b/app/src/main/java/com/example/livingai_lg/ui/theme/Type.kt index 85ff3ea..1711f3f 100644 --- a/app/src/main/java/com/example/livingai_lg/ui/theme/Type.kt +++ b/app/src/main/java/com/example/livingai_lg/ui/theme/Type.kt @@ -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 - ) - */ -) \ No newline at end of file + letterSpacing = 0.5.sp, + ), +) diff --git a/app/src/main/java/com/example/livingai_lg/ui/utils/FormatUtils.kt b/app/src/main/java/com/example/livingai_lg/ui/utils/FormatUtils.kt new file mode 100644 index 0000000..dabec56 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/utils/FormatUtils.kt @@ -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" +} + diff --git a/app/src/main/java/com/example/livingai_lg/ui/utils/KeyboardUtils.kt b/app/src/main/java/com/example/livingai_lg/ui/utils/KeyboardUtils.kt new file mode 100644 index 0000000..d100f78 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/utils/KeyboardUtils.kt @@ -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 +} diff --git a/app/src/main/java/com/example/livingai_lg/ui/utils/MediaUtils.kt b/app/src/main/java/com/example/livingai_lg/ui/utils/MediaUtils.kt new file mode 100644 index 0000000..9db4857 --- /dev/null +++ b/app/src/main/java/com/example/livingai_lg/ui/utils/MediaUtils.kt @@ -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 + } +} diff --git a/app/src/main/res/drawable/bg.xml b/app/src/main/res/drawable/bg.xml new file mode 100644 index 0000000..70fa575 --- /dev/null +++ b/app/src/main/res/drawable/bg.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/bg_shop.xml b/app/src/main/res/drawable/bg_shop.xml new file mode 100644 index 0000000..14a9666 --- /dev/null +++ b/app/src/main/res/drawable/bg_shop.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_bag.xml b/app/src/main/res/drawable/ic_bag.xml new file mode 100644 index 0000000..8af69b2 --- /dev/null +++ b/app/src/main/res/drawable/ic_bag.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_bookmark.xml b/app/src/main/res/drawable/ic_bookmark.xml new file mode 100644 index 0000000..92f4ab2 --- /dev/null +++ b/app/src/main/res/drawable/ic_bookmark.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_bookmark_plus.xml b/app/src/main/res/drawable/ic_bookmark_plus.xml new file mode 100644 index 0000000..b2bd7b6 --- /dev/null +++ b/app/src/main/res/drawable/ic_bookmark_plus.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_chat.xml b/app/src/main/res/drawable/ic_chat.xml new file mode 100644 index 0000000..945046d --- /dev/null +++ b/app/src/main/res/drawable/ic_chat.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_config.xml b/app/src/main/res/drawable/ic_config.xml new file mode 100644 index 0000000..b940b1a --- /dev/null +++ b/app/src/main/res/drawable/ic_config.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_filter.xml b/app/src/main/res/drawable/ic_filter.xml new file mode 100644 index 0000000..bef4199 --- /dev/null +++ b/app/src/main/res/drawable/ic_filter.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_home.xml b/app/src/main/res/drawable/ic_home.xml new file mode 100644 index 0000000..3dd01b4 --- /dev/null +++ b/app/src/main/res/drawable/ic_home.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_location.xml b/app/src/main/res/drawable/ic_location.xml new file mode 100644 index 0000000..22b1b35 --- /dev/null +++ b/app/src/main/res/drawable/ic_location.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_market.xml b/app/src/main/res/drawable/ic_market.xml new file mode 100644 index 0000000..be0835f --- /dev/null +++ b/app/src/main/res/drawable/ic_market.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_notification_unread.xml b/app/src/main/res/drawable/ic_notification_unread.xml new file mode 100644 index 0000000..a88e046 --- /dev/null +++ b/app/src/main/res/drawable/ic_notification_unread.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_other.xml b/app/src/main/res/drawable/ic_other.xml new file mode 100644 index 0000000..fceb745 --- /dev/null +++ b/app/src/main/res/drawable/ic_other.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_phone.xml b/app/src/main/res/drawable/ic_phone.xml new file mode 100644 index 0000000..aeaa31c --- /dev/null +++ b/app/src/main/res/drawable/ic_phone.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_profile.xml b/app/src/main/res/drawable/ic_profile.xml new file mode 100644 index 0000000..01b28e8 --- /dev/null +++ b/app/src/main/res/drawable/ic_profile.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_shop.xml b/app/src/main/res/drawable/ic_shop.xml new file mode 100644 index 0000000..0ebe824 --- /dev/null +++ b/app/src/main/res/drawable/ic_shop.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_shop2.xml b/app/src/main/res/drawable/ic_shop2.xml new file mode 100644 index 0000000..fc2a5e8 --- /dev/null +++ b/app/src/main/res/drawable/ic_shop2.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_sort.xml b/app/src/main/res/drawable/ic_sort.xml new file mode 100644 index 0000000..6d38123 --- /dev/null +++ b/app/src/main/res/drawable/ic_sort.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_spanner.xml b/app/src/main/res/drawable/ic_spanner.xml new file mode 100644 index 0000000..0cef492 --- /dev/null +++ b/app/src/main/res/drawable/ic_spanner.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_sprout.xml b/app/src/main/res/drawable/ic_sprout.xml new file mode 100644 index 0000000..896841a --- /dev/null +++ b/app/src/main/res/drawable/ic_sprout.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_star.xml b/app/src/main/res/drawable/ic_star.xml new file mode 100644 index 0000000..9c66783 --- /dev/null +++ b/app/src/main/res/drawable/ic_star.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_star_half.xml b/app/src/main/res/drawable/ic_star_half.xml new file mode 100644 index 0000000..db0b78c --- /dev/null +++ b/app/src/main/res/drawable/ic_star_half.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_sun.xml b/app/src/main/res/drawable/ic_sun.xml new file mode 100644 index 0000000..31f3ba1 --- /dev/null +++ b/app/src/main/res/drawable/ic_sun.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_supplier.xml b/app/src/main/res/drawable/ic_supplier.xml new file mode 100644 index 0000000..0cef492 --- /dev/null +++ b/app/src/main/res/drawable/ic_supplier.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_tag.xml b/app/src/main/res/drawable/ic_tag.xml new file mode 100644 index 0000000..e74d670 --- /dev/null +++ b/app/src/main/res/drawable/ic_tag.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_thumbs_up.xml b/app/src/main/res/drawable/ic_thumbs_up.xml new file mode 100644 index 0000000..98238b3 --- /dev/null +++ b/app/src/main/res/drawable/ic_thumbs_up.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_view.xml b/app/src/main/res/drawable/ic_view.xml new file mode 100644 index 0000000..1192780 --- /dev/null +++ b/app/src/main/res/drawable/ic_view.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_wheat.xml b/app/src/main/res/drawable/ic_wheat.xml new file mode 100644 index 0000000..9a04a62 --- /dev/null +++ b/app/src/main/res/drawable/ic_wheat.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..0e295bb --- /dev/null +++ b/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,7 @@ + + + + +