From 75431fc8e046a1d9dc4b87bfdb260ce7005cc6e9 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 23 Feb 2025 03:27:09 +0600 Subject: [PATCH 1/7] feat: initial structure --- .run/spotlessApply.run.xml | 1 + app/build.gradle.kts | 1 + .../whynotcompose/ui/screens/NavGraphMain.kt | 11 + .../ui/screens/tutorial/index/TutorialList.kt | 6 + app/src/main/res/drawable/store.jpg | Bin 0 -> 15303 bytes .../src/main/res/drawable/store.jpg | Bin 0 -> 15303 bytes gradle/libs.versions.toml | 5 + settings.gradle.kts | 1 + store/.gitignore | 1 + store/build.gradle.kts | 135 +++++++ store/src/main/AndroidManifest.xml | 4 + .../kotlin/com/example/store/theme/Color.kt | 11 + .../kotlin/com/example/store/theme/Theme.kt | 29 ++ .../kotlin/com/example/store/theme/Type.kt | 34 ++ .../example/store/ui/compositions/CartCard.kt | 181 ++++++++++ .../store/ui/compositions/ProductItem.kt | 198 +++++++++++ .../store/ui/compositions/StoreAppBar.kt | 75 ++++ .../store/ui/compositions/TabScreen.kt | 141 ++++++++ .../store/ui/screen/StoreMainScreen.kt | 68 ++++ .../example/store/ui/screen/StoreNavGraph.kt | 211 +++++++++++ .../store/ui/screen/cart/CartScreen.kt | 188 ++++++++++ .../ui/screen/categories/CategoriesScreen.kt | 171 +++++++++ .../CategoriesWiseProductScreen.kt | 69 ++++ .../store/ui/screen/home/StoreHomeScreen.kt | 248 +++++++++++++ .../store/ui/screen/login/LogInScreen.kt | 229 ++++++++++++ .../productdetails/ProductDetailsScreen.kt | 336 ++++++++++++++++++ .../store/ui/screen/profile/ProfileScreen.kt | 187 ++++++++++ .../store/ui/screen/splash/SplashScreen.kt | 54 +++ .../res/drawable/baseline_visibility_24.xml | 6 + store/src/main/res/values/colors.xml | 10 + store/src/main/res/values/strings.xml | 3 + store/src/main/res/values/themes.xml | 5 + 32 files changed, 2619 insertions(+) create mode 100644 app/src/main/res/drawable/store.jpg create mode 100644 common-ui-compose/src/main/res/drawable/store.jpg create mode 100644 store/.gitignore create mode 100644 store/build.gradle.kts create mode 100644 store/src/main/AndroidManifest.xml create mode 100644 store/src/main/kotlin/com/example/store/theme/Color.kt create mode 100644 store/src/main/kotlin/com/example/store/theme/Theme.kt create mode 100644 store/src/main/kotlin/com/example/store/theme/Type.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/compositions/CartCard.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/compositions/ProductItem.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/compositions/StoreAppBar.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/compositions/TabScreen.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/screen/StoreMainScreen.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/screen/StoreNavGraph.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/screen/cart/CartScreen.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/screen/categories/CategoriesScreen.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/screen/categorieswiseproduct/CategoriesWiseProductScreen.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/screen/home/StoreHomeScreen.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/screen/login/LogInScreen.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/screen/productdetails/ProductDetailsScreen.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/screen/profile/ProfileScreen.kt create mode 100644 store/src/main/kotlin/com/example/store/ui/screen/splash/SplashScreen.kt create mode 100644 store/src/main/res/drawable/baseline_visibility_24.xml create mode 100644 store/src/main/res/values/colors.xml create mode 100644 store/src/main/res/values/strings.xml create mode 100644 store/src/main/res/values/themes.xml diff --git a/.run/spotlessApply.run.xml b/.run/spotlessApply.run.xml index 5c4dac83..253893bb 100644 --- a/.run/spotlessApply.run.xml +++ b/.run/spotlessApply.run.xml @@ -18,6 +18,7 @@ true true false + false \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9ed5d5ce..ef1ec016 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -118,6 +118,7 @@ dependencies { implementation(project(":exoplayer")) implementation(project(":cms")) implementation(project(":popbackstack")) + implementation(project(":store")) "baselineProfile"(project(":benchmarks")) implementation(libs.kotlin.stdlib) diff --git a/app/src/main/kotlin/org/imaginativeworld/whynotcompose/ui/screens/NavGraphMain.kt b/app/src/main/kotlin/org/imaginativeworld/whynotcompose/ui/screens/NavGraphMain.kt index 6c5aae43..940b4aeb 100644 --- a/app/src/main/kotlin/org/imaginativeworld/whynotcompose/ui/screens/NavGraphMain.kt +++ b/app/src/main/kotlin/org/imaginativeworld/whynotcompose/ui/screens/NavGraphMain.kt @@ -50,6 +50,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.navArgument import androidx.navigation.navigation +import com.example.store.ui.screen.StoreMainScreen import org.imaginativeworld.whynotcompose.base.extensions.getJsonFromObj import org.imaginativeworld.whynotcompose.base.extensions.getObjFromJson import org.imaginativeworld.whynotcompose.base.extensions.navArg @@ -239,6 +240,7 @@ sealed class TutorialsScreen(val route: String) { data object TutorialTicTacToe : TutorialsScreen("tutorial/tic-tac-toe") data object TutorialExoPlayer : TutorialsScreen("tutorial/exoplayer") data object TutorialCMS : TutorialsScreen("tutorial/cms") + data object TutorialStore : TutorialsScreen("tutorial/store") data object TutorialDeepLink : TutorialsScreen("tutorial/deep-link") // ================================================================ @@ -982,6 +984,15 @@ private fun NavGraphBuilder.addTutorialIndexScreen( ) } + composable(TutorialsScreen.TutorialStore.route) { + StoreMainScreen( + updateUiThemeMode = updateUiThemeMode, + goBack = { + navController.popBackStackOrIgnore() + } + ) + } + composable(TutorialsScreen.TutorialDeepLink.route) { DeepLinksScreen( goBack = { diff --git a/app/src/main/kotlin/org/imaginativeworld/whynotcompose/ui/screens/tutorial/index/TutorialList.kt b/app/src/main/kotlin/org/imaginativeworld/whynotcompose/ui/screens/tutorial/index/TutorialList.kt index 0d969788..8ee29648 100644 --- a/app/src/main/kotlin/org/imaginativeworld/whynotcompose/ui/screens/tutorial/index/TutorialList.kt +++ b/app/src/main/kotlin/org/imaginativeworld/whynotcompose/ui/screens/tutorial/index/TutorialList.kt @@ -129,6 +129,12 @@ data class Tutorial( route = TutorialsScreen.TutorialCMS, level = TutorialLevel.Advanced ), + Tutorial( + name = "Store", + description = "Example of a Content Management System.", + route = TutorialsScreen.TutorialStore, + level = TutorialLevel.Advanced + ), Tutorial( name = "Deep Link", description = "Example of Deep Link.", diff --git a/app/src/main/res/drawable/store.jpg b/app/src/main/res/drawable/store.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2ada3e6bedd2170e26c546f7b712e33f8c2aa632 GIT binary patch literal 15303 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 TPE4I#Gyip=2|mm9y!`(FiS?QP literal 0 HcmV?d00001 diff --git a/common-ui-compose/src/main/res/drawable/store.jpg b/common-ui-compose/src/main/res/drawable/store.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2ada3e6bedd2170e26c546f7b712e33f8c2aa632 GIT binary patch literal 15303 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 TPE4I#Gyip=2|mm9y!`(FiS?QP literal 0 HcmV?d00001 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 15f329a0..4047bae0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -104,6 +104,7 @@ swiperefreshlayout = "1.1.0" mlkitBarcodeScanning = "17.3.0" cameraX = "1.4.1" haze = "1.2.2" +material = "1.8.0" [libraries] @@ -236,6 +237,10 @@ mlkit-barcode-scanning = { group = "com.google.mlkit", name = "barcode-scanning" haze = { group = "dev.chrisbanes.haze", name = "haze", version.ref = "haze" } haze-materials = { group = "dev.chrisbanes.haze", name = "haze-materials", version.ref = "haze" } +androidx-material3 = { group = "androidx.compose.material3", name = "material3" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } + + # The following line is optional, as the core library is included indirectly by camera-camera2 camerax-core = { group = "androidx.camera", name = "camera-core", version.ref = "cameraX" } camerax-camera2 = { group = "androidx.camera", name = "camera-camera2", version.ref = "cameraX" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 57fdea9b..6e877b70 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -56,3 +56,4 @@ include(":exoplayer") include(":cms") include(":popbackstack") include(":benchmarks") +include(":store") diff --git a/store/.gitignore b/store/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/store/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/store/build.gradle.kts b/store/build.gradle.kts new file mode 100644 index 00000000..da306d91 --- /dev/null +++ b/store/build.gradle.kts @@ -0,0 +1,135 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.compose) + alias(libs.plugins.ksp) +} + +android { + namespace = "org.imaginativeworld.whynotcompose.store" + compileSdk = BuildConfigConst.compileSdk + + defaultConfig { + minSdk = BuildConfigConst.minSdk + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = "17" + + freeCompilerArgs = freeCompilerArgs + "-opt-in=kotlin.RequiresOptIn" + + // Enable experimental compose APIs + freeCompilerArgs = + freeCompilerArgs + "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api" + freeCompilerArgs = + freeCompilerArgs + "-opt-in=androidx.compose.animation.ExperimentalAnimationApi" + freeCompilerArgs = + freeCompilerArgs + "-opt-in=androidx.compose.ui.ExperimentalComposeUiApi" + freeCompilerArgs = + freeCompilerArgs + "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi" + } + + ksp { + arg("room.schemaLocation", "$projectDir/schemas") + } + + buildFeatures { + buildConfig = true + compose = true + } +} + +dependencies { + implementation(project(":base")) + implementation(project(":common-ui-compose")) + + implementation(libs.kotlin.stdlib) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.swiperefreshlayout) + + // ---------------------------------------------------------------- + // Compose + // ---------------------------------------------------------------- + implementation(platform(libs.androidx.compose.bom)) + + implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.ui.util) + // Tooling support (Previews, etc.) + debugImplementation(libs.androidx.compose.ui.tooling) + implementation(libs.androidx.compose.ui.tooling.preview) + // Animation + implementation(libs.androidx.compose.animation) + // Foundation (Border, Background, Box, Image, Scroll, shapes, animations, etc.) + implementation(libs.androidx.compose.foundation) + implementation(libs.androidx.compose.foundation.layout) + // Material Design + implementation(libs.androidx.compose.material3) + implementation(libs.androidx.compose.material3.windowSizeClass) + // Material design icons + implementation(libs.androidx.compose.material.iconsCore) + implementation(libs.androidx.compose.material.iconsExtended) + // Integration with observables + implementation(libs.androidx.compose.runtime) + implementation(libs.androidx.compose.runtime.livedata) + implementation(libs.androidx.compose.runtime.tracing) + // compose Navigation Component + implementation(libs.androidx.navigation.compose) + // Constraint Layout + implementation(libs.androidx.constraintlayout.compose) + // Integration with activities + implementation(libs.androidx.activity.compose) + + // Jetpack compose Integration for ViewModel + implementation(libs.androidx.lifecycle.viewmodel.compose) + + // Paging + implementation(libs.androidx.paging.compose) + + // ---------------------------------------------------------------- + + // Timber + implementation(libs.timber) + + // Hilt + implementation(libs.hilt.android) + ksp(libs.hilt.compiler) + implementation(libs.androidx.hilt.navigation.compose) + + // Retrofit + implementation(libs.retrofit.core) + implementation(libs.okhttp.logging) + + // Moshi + implementation(libs.retrofit.moshi) + implementation(libs.moshi.kotlin) + ksp(libs.moshi.kotlin.codegen) + + // Room Persistence Library + implementation(libs.room.runtime) + ksp(libs.room.compiler) + + // Room: Kotlin Extensions and Coroutines support for Room + implementation(libs.room.ktx) + + // Coil + implementation(libs.coil.compose) + implementation(libs.coil.svg) + implementation(libs.coil.network) + + // Serialization + implementation(libs.kotlinx.serialization.json) + + // Haze + implementation(libs.haze) + implementation(libs.haze.materials) + + implementation(libs.androidx.material3) + implementation(libs.material) +} diff --git a/store/src/main/AndroidManifest.xml b/store/src/main/AndroidManifest.xml new file mode 100644 index 00000000..44008a43 --- /dev/null +++ b/store/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/store/src/main/kotlin/com/example/store/theme/Color.kt b/store/src/main/kotlin/com/example/store/theme/Color.kt new file mode 100644 index 00000000..b46315b1 --- /dev/null +++ b/store/src/main/kotlin/com/example/store/theme/Color.kt @@ -0,0 +1,11 @@ +package com.example.store.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) diff --git a/store/src/main/kotlin/com/example/store/theme/Theme.kt b/store/src/main/kotlin/com/example/store/theme/Theme.kt new file mode 100644 index 00000000..6fcb4c75 --- /dev/null +++ b/store/src/main/kotlin/com/example/store/theme/Theme.kt @@ -0,0 +1,29 @@ +package com.example.store.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable + +private val DarkColorPalette = darkColorScheme() + +private val LightColorPalette = lightColorScheme() + +@Composable +fun StoreAppTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit +) { + val colors = if (darkTheme) { + DarkColorPalette + } else { + LightColorPalette + } + + MaterialTheme( + colorScheme = colors, + typography = Typography, + content = content + ) +} diff --git a/store/src/main/kotlin/com/example/store/theme/Type.kt b/store/src/main/kotlin/com/example/store/theme/Type.kt new file mode 100644 index 00000000..50801834 --- /dev/null +++ b/store/src/main/kotlin/com/example/store/theme/Type.kt @@ -0,0 +1,34 @@ +package com.example.store.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +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( + 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( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) diff --git a/store/src/main/kotlin/com/example/store/ui/compositions/CartCard.kt b/store/src/main/kotlin/com/example/store/ui/compositions/CartCard.kt new file mode 100644 index 00000000..9fd4804d --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/compositions/CartCard.kt @@ -0,0 +1,181 @@ +package com.example.store.ui.compositions + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +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.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Remove +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +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.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil3.compose.AsyncImage +import com.example.store.theme.StoreAppTheme +import com.example.store.ui.screen.productdetails.Product +import com.example.store.ui.screen.productdetails.dummyProducts + +@Composable +fun CartItemCard( + product: Product, + onQuantityChange: (Int) -> Unit, + modifier: Modifier = Modifier +) { + var quantity by remember { mutableIntStateOf(2) } + val totalPrice = product.price * quantity + + Card( + modifier = modifier + .fillMaxWidth(), + elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + AsyncImage( + model = product.imageUrl, + contentDescription = null, + modifier = Modifier + .width(80.dp) + .height(80.dp) + .background(Color.Gray) + ) + + Spacer(modifier = Modifier.width(16.dp)) + + Column( + modifier = Modifier + .weight(1f) + ) { + Text( + product.title, + fontSize = 16.sp, + maxLines = 1, + fontWeight = FontWeight.Bold + ) + Text( + text = "$${product.price}", + fontSize = 14.sp, + fontWeight = FontWeight.Bold + ) + } + } + + HorizontalDivider() + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.weight(.4f) + ) { + IconButton( + onClick = { + if (quantity > 1) { + quantity-- + onQuantityChange(quantity) + } + } + ) { + Box( + modifier = Modifier + .clip(RoundedCornerShape(8.dp)) + .background(Color.Blue.copy(alpha = .7f)) + .padding(4.dp), +// .border(1.dp, MaterialTheme.colorScheme.surface), + contentAlignment = Alignment.Center + ) { + Icon( + Icons.Default.Remove, + contentDescription = "Decrease", + tint = MaterialTheme.colorScheme.surface + ) + } + } + + Text( + text = "$quantity", + style = MaterialTheme.typography.bodySmall, + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(horizontal = 8.dp) + ) + + IconButton( + onClick = { + quantity++ + onQuantityChange(quantity) + } + ) { + Box( + modifier = Modifier + .clip(RoundedCornerShape(8.dp)) + .background(Color.Blue.copy(alpha = .7f)) + .padding(4.dp) + ) { + Icon( + Icons.Default.Add, + contentDescription = "Increase", + tint = MaterialTheme.colorScheme.surface + ) + } + } + } + + Text( + text = "$$totalPrice", + fontSize = 14.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.End, + color = MaterialTheme.colorScheme.error, + modifier = Modifier + .padding(horizontal = 8.dp) + .weight(.5f) + ) + } + } +} + +@PreviewLightDark +@Composable +private fun CartItemCardPreview() { + StoreAppTheme { + CartItemCard( + product = dummyProducts[0], + onQuantityChange = {}, + modifier = Modifier + ) + } +} diff --git a/store/src/main/kotlin/com/example/store/ui/compositions/ProductItem.kt b/store/src/main/kotlin/com/example/store/ui/compositions/ProductItem.kt new file mode 100644 index 00000000..c3864638 --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/compositions/ProductItem.kt @@ -0,0 +1,198 @@ +package com.example.store.ui.compositions + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +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.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Remove +import androidx.compose.material.icons.filled.Star +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +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.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil3.compose.AsyncImage +import com.example.store.theme.StoreAppTheme +import com.example.store.ui.screen.productdetails.Product + +@Composable +fun ProductItem( + product: Product, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + var quantity by remember { mutableIntStateOf(0) } + + Card( + modifier = modifier + .clickable { onClick() }, + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainer + ), + elevation = CardDefaults.cardElevation(4.dp), + shape = RoundedCornerShape(8.dp) + ) { + Column( + modifier = Modifier + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + AsyncImage( + model = product.imageUrl, + contentDescription = "Product Image", + modifier = Modifier + .width(150.dp) + .height(150.dp) + .background(color = MaterialTheme.colorScheme.outline) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = product.title, + fontSize = 14.sp, + textAlign = TextAlign.Start, + fontWeight = FontWeight.Bold, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .fillMaxWidth() + ) + Text( + text = "$${product.price}", + fontSize = 16.sp, + color = MaterialTheme.colorScheme.error, + textAlign = TextAlign.Start, + fontWeight = FontWeight.Bold, + modifier = Modifier.fillMaxWidth() + ) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + modifier = Modifier.fillMaxWidth() + ) { + Icon( + Icons.Default.Star, + contentDescription = "Rating", + tint = Color.Yellow.copy(alpha = .5f) + ) + Text( + text = ("${product.rating} (${product.reviewCount})"), + modifier = Modifier.padding(start = 4.dp), + color = MaterialTheme.colorScheme.outline + ) + } + } + + HorizontalDivider() + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + ) { + IconButton( + onClick = { if (quantity > 0) quantity-- }, + modifier = Modifier + ) { + Box( + modifier = Modifier + .clip(RoundedCornerShape(8.dp)) + .background(Color.Blue.copy(alpha = .7f)) + .padding(4.dp), +// .border(1.dp, MaterialTheme.colorScheme.surface), + contentAlignment = Alignment.Center + ) { + Icon( + Icons.Default.Remove, + contentDescription = "Decrease", + tint = MaterialTheme.colorScheme.surface + ) + } + } + + Text( + text = "$quantity", + style = MaterialTheme.typography.bodySmall, + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(horizontal = 8.dp) + ) + + IconButton( + onClick = { quantity++ } + ) { + Box( + modifier = Modifier + .clip(RoundedCornerShape(8.dp)) + .background(Color.Blue.copy(alpha = .7f)) + .padding(4.dp), + contentAlignment = Alignment.Center + ) { + Icon( + Icons.Default.Add, + contentDescription = "Increase", + tint = MaterialTheme.colorScheme.surface + ) + } + } + } + } +} + +@PreviewLightDark +@Composable +private fun PreviewProductDetailsScreen() { + StoreAppTheme { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + ProductItem( + onClick = {}, + product = Product( + category = "jewelery", + id = 1, + title = "WD 2TB Elements Portable External...", + imageUrl = "https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg", + price = 64.00, + rating = 3.3, + reviewCount = 203, + description = "USB 3.0 and USB 2.0 Compatibility Fast data transfers Improve PC Performance High Capacity;...", + quantity = 0 + ) + ) + } + } +} diff --git a/store/src/main/kotlin/com/example/store/ui/compositions/StoreAppBar.kt b/store/src/main/kotlin/com/example/store/ui/compositions/StoreAppBar.kt new file mode 100644 index 00000000..d7d5e80f --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/compositions/StoreAppBar.kt @@ -0,0 +1,75 @@ +package com.example.store.ui.compositions + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.ArrowBack +import androidx.compose.material.icons.rounded.BrightnessAuto +import androidx.compose.material.icons.rounded.DarkMode +import androidx.compose.material.icons.rounded.LightMode +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.PreviewLightDark +import com.example.store.theme.StoreAppTheme +import org.imaginativeworld.whynotcompose.base.models.UIThemeMode +import org.imaginativeworld.whynotcompose.base.utils.UIThemeController + +@Composable +fun StoreAppBar( + modifier: Modifier = Modifier, + title: String = "Store Overflow", + goBack: () -> Unit = {}, + toggleUIMode: () -> Unit = {} +) { + val uiThemeMode by UIThemeController.uiThemeMode.collectAsState() + + CenterAlignedTopAppBar( + modifier = modifier, + title = { + Text( + title, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontWeight = FontWeight.Bold + ) + }, + navigationIcon = { + IconButton(onClick = { + goBack() + }) { + Icon( + imageVector = Icons.AutoMirrored.Rounded.ArrowBack, + contentDescription = "Go back" + ) + } + }, + actions = { + IconButton(onClick = { + toggleUIMode() + }) { + Icon( + imageVector = when (uiThemeMode) { + UIThemeMode.AUTO -> Icons.Rounded.BrightnessAuto + UIThemeMode.LIGHT -> Icons.Rounded.LightMode + UIThemeMode.DARK -> Icons.Rounded.DarkMode + }, + contentDescription = "" + ) + } + } + ) +} + +@PreviewLightDark +@Composable +private fun StoreAppBarPreview() { + StoreAppTheme { + StoreAppBar() + } +} diff --git a/store/src/main/kotlin/com/example/store/ui/compositions/TabScreen.kt b/store/src/main/kotlin/com/example/store/ui/compositions/TabScreen.kt new file mode 100644 index 00000000..27c7f733 --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/compositions/TabScreen.kt @@ -0,0 +1,141 @@ +package com.example.store.ui.compositions + +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Category +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.ShoppingCart +import androidx.compose.material3.Icon +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import androidx.navigation.NavGraph.Companion.findStartDestination +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.rememberNavController +import com.example.store.theme.StoreAppTheme +import com.example.store.ui.screen.cart.CartScreen +import com.example.store.ui.screen.categories.CategoriesScreen +import com.example.store.ui.screen.categories.Category +import com.example.store.ui.screen.home.StoreHomeScreen +import com.example.store.ui.screen.productdetails.Product +import com.example.store.ui.screen.productdetails.dummyProducts + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun TabScreen( + userName: String, + product: List, + goBack: () -> Unit, + toggleUIMode: () -> Unit, + onCheckout: () -> Unit, + onProductClick: (Product) -> Unit, + onCategoryClick: (Category) -> Unit +) { + val navController = rememberNavController() + val navBackStackEntry by navController.currentBackStackEntryAsState() + val currentRoute = navBackStackEntry?.destination?.route + + Scaffold( + bottomBar = { + NavigationBar { + bottomNavigationItems().forEach { navigationItem -> + NavigationBarItem( + selected = currentRoute == navigationItem.route, + label = { Text(navigationItem.label) }, + icon = { + Icon( + navigationItem.icon, + contentDescription = navigationItem.label + ) + }, + onClick = { + if (currentRoute != navigationItem.route) { + navController.navigate(navigationItem.route) { + popUpTo(navController.graph.findStartDestination().id) { + saveState = true + } + launchSingleTop = true + restoreState = true + } + } + } + ) + } + } + } + ) { paddingValues -> + NavHost( + navController = navController, + startDestination = Screens.Home.route, + modifier = Modifier.padding(paddingValues) + ) { + composable(Screens.Home.route) { + StoreHomeScreen( + userName = userName, + toggleUIMode = toggleUIMode, + products = product, + onProductClick = onProductClick, + onCategoryClick = onCategoryClick + ) + } + composable(Screens.Categories.route) { + CategoriesScreen( + goBack = goBack, + toggleUIMode = toggleUIMode, + onCategoryClick = onCategoryClick + ) + } + composable(Screens.Cart.route) { + CartScreen( + products = product, + goBack = goBack, + onCheckout = onCheckout, + toggleUIMode = toggleUIMode + ) + } + } + } +} + +data class NavigationItem( + val label: String, + val icon: ImageVector, + val route: String +) + +fun bottomNavigationItems(): List = listOf( + NavigationItem("Home", Icons.Filled.Home, Screens.Home.route), + NavigationItem("Categories", Icons.Filled.Category, Screens.Categories.route), + NavigationItem("Cart", Icons.Filled.ShoppingCart, Screens.Cart.route) +) + +sealed class Screens(val route: String) { + object Home : Screens("home") + object Categories : Screens("categories") + object Cart : Screens("cart") +} + +@PreviewLightDark +@Composable +private fun TabScreenPreview() { + StoreAppTheme { + TabScreen( + userName = "John Doe", + product = dummyProducts, + goBack = {}, + toggleUIMode = {}, + onCheckout = {}, + onProductClick = {}, + onCategoryClick = {} + ) + } +} diff --git a/store/src/main/kotlin/com/example/store/ui/screen/StoreMainScreen.kt b/store/src/main/kotlin/com/example/store/ui/screen/StoreMainScreen.kt new file mode 100644 index 00000000..3ed97780 --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/screen/StoreMainScreen.kt @@ -0,0 +1,68 @@ +package com.example.store.ui.screen + +import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.navigation.compose.rememberNavController +import com.example.store.theme.StoreAppTheme +import org.imaginativeworld.whynotcompose.base.models.UIThemeMode +import org.imaginativeworld.whynotcompose.base.utils.UIThemeController + +@Composable +fun StoreMainScreen( + updateUiThemeMode: (UIThemeMode) -> Unit, + goBack: () -> Unit +) { + val uiThemeMode by UIThemeController.uiThemeMode.collectAsState() + val isSystemInDarkTheme = isSystemInDarkTheme() + + val isDarkMode by remember(isSystemInDarkTheme) { + derivedStateOf { + when (uiThemeMode) { + UIThemeMode.AUTO -> isSystemInDarkTheme + UIThemeMode.LIGHT -> false + UIThemeMode.DARK -> true + } + } + } + + StoreAppTheme( + darkTheme = isDarkMode + ) { + StoreMainScreenSkeleton( + updateUiThemeMode = updateUiThemeMode, + goBack = goBack + ) + } +} + +@Preview +@Composable +private fun StoreMainScreenSkeletonPreview() { + StoreAppTheme { + StoreMainScreenSkeleton() + } +} + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun StoreMainScreenSkeleton( + updateUiThemeMode: (UIThemeMode) -> Unit = {}, + goBack: () -> Unit = {} +) { + val navController = rememberNavController() + + StoreNavHost( + modifier = Modifier.background(MaterialTheme.colorScheme.background), + navController = navController, + updateUiThemeMode = updateUiThemeMode, + goBack = goBack + ) +} diff --git a/store/src/main/kotlin/com/example/store/ui/screen/StoreNavGraph.kt b/store/src/main/kotlin/com/example/store/ui/screen/StoreNavGraph.kt new file mode 100644 index 00000000..f657770d --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/screen/StoreNavGraph.kt @@ -0,0 +1,211 @@ +package com.example.store.ui.screen + +import LoginScreen +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.toRoute +import com.example.store.ui.compositions.TabScreen +import com.example.store.ui.screen.categories.CategoriesScreen +import com.example.store.ui.screen.categorieswiseproduct.CategoriesWiseProductScreen +import com.example.store.ui.screen.home.StoreHomeScreen +import com.example.store.ui.screen.productdetails.ProductDetailsScreen +import com.example.store.ui.screen.productdetails.dummyProducts +import com.example.store.ui.screen.splash.StoreSplashScreen +import kotlinx.serialization.Serializable +import org.imaginativeworld.whynotcompose.base.models.UIThemeMode +import org.imaginativeworld.whynotcompose.base.models.nextMode +import org.imaginativeworld.whynotcompose.base.utils.UIThemeController + +sealed class SplashScreen { + @Serializable + object Splash +} + +sealed class AuthScreen { + @Serializable + object Login +} + +sealed class MainScreen { + @Serializable + object TabScreen +} + +sealed class StoreScreen { + @Serializable + object StoreHome +} + +@Serializable +sealed class CategorieScreen { + @Serializable + object Categories +} + +@Serializable +sealed class CategoriesWiseProducts { + @Serializable + data class CategoriesWiseProduct(val categoryTitle: String) +} + +@Serializable +sealed class DetailsScreen { + @Serializable + data class ProductDetails(val productId: Int) +} + +@Serializable +sealed class CartScreen { + @Serializable + object Cart +} + +@Composable +fun StoreNavHost( + navController: NavHostController, + updateUiThemeMode: (UIThemeMode) -> Unit, + goBack: () -> Unit, + modifier: Modifier = Modifier +) { + NavHost( + modifier = modifier, + navController = navController, + startDestination = SplashScreen.Splash + ) { + composable { + StoreSplashScreen( + gotoHomeIndex = { + navController.navigate(AuthScreen.Login) { + popUpTo(SplashScreen.Splash) { inclusive = true } + } + } + ) + } + + composable { + val isDarkMode by UIThemeController.uiThemeMode.collectAsState() + + LoginScreen( + onLogin = { + navController.navigate(MainScreen.TabScreen) { + popUpTo(AuthScreen.Login) { inclusive = true } + } + }, + toggleUIMode = { + updateUiThemeMode(isDarkMode.nextMode()) + } + ) + } + + addStoreScreens( + navController = navController, + updateUiThemeMode = updateUiThemeMode, + goBack = goBack + ) + } +} + +private fun NavGraphBuilder.addStoreScreens( + navController: NavHostController, + updateUiThemeMode: (UIThemeMode) -> Unit, + goBack: () -> Unit +) { + composable { + val isDarkMode by UIThemeController.uiThemeMode.collectAsState() + + TabScreen( + userName = "John Doe", + product = dummyProducts, + goBack = { + navController.popBackStack() + }, + toggleUIMode = { + updateUiThemeMode(isDarkMode.nextMode()) + }, + onProductClick = { product -> + navController.navigate(DetailsScreen.ProductDetails(product.id)) + }, + onCategoryClick = { + navController.navigate(CategoriesWiseProducts.CategoriesWiseProduct(it.name)) + }, + onCheckout = { + navController.navigate(MainScreen.TabScreen) + } + ) + } + + composable { + val isDarkMode by UIThemeController.uiThemeMode.collectAsState() + + StoreHomeScreen( + userName = "John Doe", + toggleUIMode = { + updateUiThemeMode(isDarkMode.nextMode()) + }, + products = dummyProducts, + onProductClick = { product -> + navController.navigate(DetailsScreen.ProductDetails(product.id)) + }, + onCategoryClick = { + navController.navigate(CategoriesWiseProducts.CategoriesWiseProduct(it.name)) + } + ) + } + + composable { backStackEntry -> + val productDetails: DetailsScreen.ProductDetails = backStackEntry.toRoute() + val product = dummyProducts.find { it.id == productDetails.productId } + val isDarkMode by UIThemeController.uiThemeMode.collectAsState() + + product?.let { + ProductDetailsScreen( + product = it, + goBack = { + navController.popBackStack() + }, + toggleUIMode = { + updateUiThemeMode(isDarkMode.nextMode()) + } + ) + } + } + + composable { + val isDarkMode by UIThemeController.uiThemeMode.collectAsState() + CategoriesScreen( + goBack = { + navController.popBackStack() + }, + onCategoryClick = { + navController.navigate(CategoriesWiseProducts.CategoriesWiseProduct(it.name)) + }, + toggleUIMode = { + updateUiThemeMode(isDarkMode.nextMode()) + } + ) + } + + composable { backStackEntry -> + val categoryDetails: CategoriesWiseProducts.CategoriesWiseProduct = backStackEntry.toRoute() + val isDarkMode by UIThemeController.uiThemeMode.collectAsState() + val products = dummyProducts.filter { it.category == categoryDetails.categoryTitle } + CategoriesWiseProductScreen( + products = products, + onProductClick = { product -> + navController.navigate(DetailsScreen.ProductDetails(product.id)) + }, + goBack = { + navController.popBackStack() + }, + toggleUIMode = { + updateUiThemeMode(isDarkMode.nextMode()) + } + ) + } +} diff --git a/store/src/main/kotlin/com/example/store/ui/screen/cart/CartScreen.kt b/store/src/main/kotlin/com/example/store/ui/screen/cart/CartScreen.kt new file mode 100644 index 00000000..647c1cae --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/screen/cart/CartScreen.kt @@ -0,0 +1,188 @@ +package com.example.store.ui.screen.cart + +import android.widget.Toast +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.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableDoubleStateOf +import androidx.compose.runtime.mutableStateListOf +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.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.example.store.theme.StoreAppTheme +import com.example.store.ui.compositions.CartItemCard +import com.example.store.ui.screen.productdetails.Product +import com.example.store.ui.screen.productdetails.dummyProducts + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun CartScreen( + products: List, + goBack: () -> Unit, + onCheckout: () -> Unit, + toggleUIMode: () -> Unit +) { + CartScreenSkeleton( + products = products, + goBack = goBack, + onCheckout = onCheckout, + toggleUIMode = toggleUIMode + ) +} + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun CartScreenSkeleton( + products: List, + goBack: () -> Unit, + onCheckout: () -> Unit = {}, + toggleUIMode: () -> Unit = {} +) { + Scaffold( + topBar = { +// TopAppBar( +// title = {}, +// navigationIcon = {}, +// actions = { +// IconButton(onClick = goBack) { +// Icon(Icons.Default.Menu, contentDescription = "Back") +// } +// } +// ) +// StoreAppBar( +// title = "Store Overflow", +// goBack = goBack, +// toggleUIMode = toggleUIMode +// ) + } + ) { paddingValues -> + val context = LocalContext.current + val cartItems = remember { mutableStateListOf(*products.toTypedArray()) } + val totalPrice by remember { mutableDoubleStateOf(cartItems.sumOf { it.price * it.quantity }) } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .padding(horizontal = 16.dp) + ) { + Text( + text = "Cart", + fontSize = 28.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(bottom = 16.dp) + ) + + Column( + modifier = Modifier + .weight(1f) + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally + ) { + if (cartItems.isEmpty()) { + Text( + text = "Your cart is empty", + fontSize = 18.sp, + color = Color.Gray + ) + } else { + cartItems.forEach { product -> + CartItemCard( + product = product, + onQuantityChange = { newQuantity -> + if (newQuantity == 0) { + cartItems.remove(product) + } else { + product.quantity = newQuantity + } + } + ) + Spacer(modifier = Modifier.height(16.dp)) + } + } + } + + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Row( + modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "Total:", + fontSize = 18.sp, + fontWeight = FontWeight.Bold + ) + Text( + text = "$${"%.2f".format(totalPrice)}", + fontSize = 18.sp, + fontWeight = FontWeight.Bold + ) + } + + Spacer(modifier = Modifier.height(16.dp)) + + TextButton( + onClick = { + Toast.makeText(context, "Order Placed Complete", Toast.LENGTH_SHORT).show() + onCheckout() + }, + modifier = Modifier + .fillMaxWidth(), + colors = ButtonDefaults.buttonColors(containerColor = Color.Blue), + shape = MaterialTheme.shapes.medium + ) { + Text( + text = "Place Order", + fontSize = 18.sp, + color = Color.White + ) + } + } + } + } +} + +@PreviewLightDark +@Composable +private fun CartScreenSkeletonPreview() { + StoreAppTheme { + CartScreenSkeleton( + products = dummyProducts, + goBack = {}, + onCheckout = {}, + toggleUIMode = {} + ) + } +} diff --git a/store/src/main/kotlin/com/example/store/ui/screen/categories/CategoriesScreen.kt b/store/src/main/kotlin/com/example/store/ui/screen/categories/CategoriesScreen.kt new file mode 100644 index 00000000..d7939cee --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/screen/categories/CategoriesScreen.kt @@ -0,0 +1,171 @@ +package com.example.store.ui.screen.categories + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +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.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Scaffold +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.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil3.compose.AsyncImage +import com.example.store.theme.StoreAppTheme + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun CategoriesScreen( + goBack: () -> Unit, + onCategoryClick: (Category) -> Unit, + toggleUIMode: () -> Unit +) { + CategoriesScreenSkeleton( + goBack = goBack, + onCategoryClick = { + onCategoryClick(it) + }, + toggleUIMode = toggleUIMode + ) +} + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun CategoriesScreenSkeleton( + goBack: () -> Unit = {}, + toggleUIMode: () -> Unit = {}, + onCategoryClick: (Category) -> Unit = {} +) { + Scaffold( +// topBar = { +// StoreAppBar( +// title = "Category", +// goBack = goBack, +// toggleUIMode = toggleUIMode +// ) +// } + ) { innerPadding -> + Column( + modifier = Modifier + .padding(innerPadding) + .padding(16.dp) + .fillMaxSize() + ) { + Text( + text = "Categories", + fontSize = 28.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(bottom = 16.dp) + ) + LazyVerticalGrid( + modifier = Modifier, + columns = GridCells.Fixed(2), + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + items(categories.size) { index -> + val category = categories[index] + CategoryCard( + category = category, + modifier = Modifier + .fillMaxWidth(), + onClick = { + onCategoryClick(category) + } + ) + } + } + } + } +} + +@Composable +fun CategoryCard( + category: Category, + modifier: Modifier = Modifier, + onClick: () -> Unit = {} +) { + Box( + modifier = modifier + .size(150.dp) + .clip(RoundedCornerShape(12.dp)) + .clickable { + onClick() + } + ) { + AsyncImage( + model = category.imageUrl, + contentDescription = "", + contentScale = ContentScale.Crop, + modifier = Modifier.matchParentSize() + ) + + Box( + modifier = Modifier + .matchParentSize() + .background( + Brush.verticalGradient( + colors = listOf( + Color.Black.copy(alpha = 0.3f), + Color.Transparent + ) + ) + ) + .clip(RoundedCornerShape(12.dp)) + ) + Text( + text = category.name, + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + color = Color.White, + modifier = Modifier.align(Alignment.Center) + ) + } +} + +data class Category( + val id: Int, + val name: String, + val imageUrl: String +) + +val categories = listOf( + Category(id = 1, name = "Electronics", imageUrl = "https://fakestoreapi.com/img/61IBBVJvSDL._AC_SY879_.jpg"), + Category(id = 2, name = "Jewelery", imageUrl = "https://fakestoreapi.com/img/71pWzhdJNwL._AC_UL640_QL65_ML3_.jpg"), + Category(id = 3, name = "Men's Clothing", imageUrl = "Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops"), + Category(id = 4, name = "Women's Clothing", imageUrl = "https://fakestoreapi.com/img/61pHAEJ4NML._AC_UX679_.jpg") +) + +@PreviewLightDark +@Composable +private fun CategoriesScreenSkeletonPreview() { + StoreAppTheme { + CategoriesScreenSkeleton( + goBack = {}, + toggleUIMode = {} + ) + +// CategoryCard( +// category = Category( +// id = 1, +// name = "Electronics", +// imageUrl = "" +// ) +// ) + } +} diff --git a/store/src/main/kotlin/com/example/store/ui/screen/categorieswiseproduct/CategoriesWiseProductScreen.kt b/store/src/main/kotlin/com/example/store/ui/screen/categorieswiseproduct/CategoriesWiseProductScreen.kt new file mode 100644 index 00000000..907fbdd1 --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/screen/categorieswiseproduct/CategoriesWiseProductScreen.kt @@ -0,0 +1,69 @@ +package com.example.store.ui.screen.categorieswiseproduct + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.example.store.theme.StoreAppTheme +import com.example.store.ui.compositions.ProductItem +import com.example.store.ui.compositions.StoreAppBar +import com.example.store.ui.screen.productdetails.Product +import com.example.store.ui.screen.productdetails.dummyProducts + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun CategoriesWiseProductScreen( + products: List, + onProductClick: (Product) -> Unit, + goBack: () -> Unit, + toggleUIMode: () -> Unit +) { + Scaffold( + topBar = { + StoreAppBar( + title = "Category Wise Product", + goBack = goBack, + toggleUIMode = toggleUIMode + ) + } + ) { innerPadding -> + LazyVerticalGrid( + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + .padding(16.dp), + columns = GridCells.Fixed(2), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + items(products.size) { index -> + val product = products[index] + ProductItem( + product = product, + onClick = { onProductClick(product) }, + modifier = Modifier + ) + } + } + } +} + +@Preview +@Composable +private fun CategoriesWiseProductScreenPreview() { + StoreAppTheme { + val products = dummyProducts + CategoriesWiseProductScreen( + products = products, + onProductClick = {}, + goBack = {}, + toggleUIMode = {} + ) + } +} diff --git a/store/src/main/kotlin/com/example/store/ui/screen/home/StoreHomeScreen.kt b/store/src/main/kotlin/com/example/store/ui/screen/home/StoreHomeScreen.kt new file mode 100644 index 00000000..4d558fef --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/screen/home/StoreHomeScreen.kt @@ -0,0 +1,248 @@ +package com.example.store.ui.screen.home + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +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.WindowInsets +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.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.SportsBaseball +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +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.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil3.compose.AsyncImage +import com.example.store.theme.StoreAppTheme +import com.example.store.ui.compositions.ProductItem +import com.example.store.ui.screen.categories.Category +import com.example.store.ui.screen.categories.categories +import com.example.store.ui.screen.productdetails.Product +import com.example.store.ui.screen.productdetails.dummyProducts + +@Composable +fun StoreHomeScreen( + userName: String, + onCategoryClick: (Category) -> Unit, + products: List, + onProductClick: (Product) -> Unit, + toggleUIMode: () -> Unit +) { + StoreHomeSkeleton( + userName = userName, + categories = categories, + products = products, + onCategoryClick = onCategoryClick, + onProductClick = onProductClick, + toggleUIMode = toggleUIMode + ) +} + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun StoreHomeSkeleton( + userName: String, + categories: List, + products: List, + onCategoryClick: (Category) -> Unit, + onProductClick: (Product) -> Unit, + toggleUIMode: () -> Unit +) { + Scaffold( +// topBar = { +// StoreAppBar( +// toggleUIMode = toggleUIMode +// ) +// }, + contentWindowInsets = WindowInsets(0.dp, 0.dp, 0.dp, 0.dp) + ) { innerPadding -> + Column( + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + .padding(16.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "Welcome, $userName", + style = MaterialTheme.typography.headlineMedium, + fontSize = 24.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier + ) + + Image( + painter = painterResource(org.imaginativeworld.whynotcompose.common.compose.R.drawable.store), + contentDescription = "Profile Image", + modifier = Modifier + .size(30.dp) + .clip(CircleShape), + contentScale = ContentScale.Crop + ) + } + + Spacer(modifier = Modifier.height(30.dp)) + + LazyRow(horizontalArrangement = Arrangement.spacedBy(16.dp)) { + items(homeScreenImages.size) { + HomeScreenImage( + image = homeScreenImages[it], + modifier = Modifier + .width(300.dp) + .height(150.dp) + ) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + items(categories.size) { + CategoryItem( + category = categories[it], + modifier = Modifier, + onClick = { + onCategoryClick(categories[it]) + } + ) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + LazyVerticalGrid( + columns = GridCells.Fixed(2), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.fillMaxWidth() + ) { + items(products.size) { index -> + ProductItem( + product = products[index], + onClick = { + onProductClick(products[index]) + } + ) + } + } + } + } +} + +@Composable +fun CategoryItem( + category: Category, + modifier: Modifier = Modifier, + onClick: () -> Unit = {} +) { + Card( + modifier = modifier + .clickable( + onClick = { + onClick() + } + ), + shape = RoundedCornerShape(8.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.outlineVariant) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding( + vertical = 8.dp, + horizontal = 12.dp + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Icon( + imageVector = Icons.Outlined.SportsBaseball, + contentDescription = "" + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text( + text = category.name, + fontSize = 14.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier + ) + } + } +} + +@Composable +fun HomeScreenImage( + image: HomeScreenImage, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + AsyncImage( + model = image.imageUrl, + contentDescription = "Product Image", + modifier = Modifier + .fillMaxSize() + .clip(RoundedCornerShape(12.dp)) + .background(color = MaterialTheme.colorScheme.outline) + ) + } +} + +data class HomeScreenImage( + val imageUrl: String +) + +val homeScreenImages = listOf( + HomeScreenImage(imageUrl = "https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg"), + HomeScreenImage(imageUrl = "https://fakestoreapi.com/img/71-3HjGNDUL._AC_SY879._SX._UX._SY._UY_.jpg"), + HomeScreenImage(imageUrl = "https://fakestoreapi.com/img/61IBBVJvSDL._AC_SY879._SX._UX._SY._UY_.jpg") +) + +@PreviewLightDark +@Composable +private fun StoreHomeScreenPreview() { + StoreAppTheme { + StoreHomeScreen( + userName = "John Doe", + onCategoryClick = {}, + products = dummyProducts, + onProductClick = {}, + toggleUIMode = {} + ) + } +} diff --git a/store/src/main/kotlin/com/example/store/ui/screen/login/LogInScreen.kt b/store/src/main/kotlin/com/example/store/ui/screen/login/LogInScreen.kt new file mode 100644 index 00000000..d37419d8 --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/screen/login/LogInScreen.kt @@ -0,0 +1,229 @@ +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +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.wrapContentSize +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material.icons.filled.VisibilityOff +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +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.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.example.store.theme.StoreAppTheme +import org.imaginativeworld.whynotcompose.common.compose.theme.AppleSystemColor + +@Composable +fun LoginScreen( + onLogin: () -> Unit = {}, + toggleUIMode: () -> Unit = {} +) { + LoginSkeleton( + onLogin = onLogin, + toggleUIMode = toggleUIMode + ) +} + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun LoginSkeleton( + onLogin: () -> Unit, + toggleUIMode: () -> Unit = {} +) { + var username by remember { mutableStateOf("store") } + var password by remember { mutableStateOf("223355") } + var usernameError by rememberSaveable { mutableStateOf("") } + var passwordError by rememberSaveable { mutableStateOf("") } + var passwordVisible by rememberSaveable { mutableStateOf(false) } + + val gradientBrush = Brush.linearGradient( + colors = listOf( + AppleSystemColor.Purple, + AppleSystemColor.Blue + ) + ) + Scaffold( + topBar = { +// StoreAppBar( +// toggleUIMode = toggleUIMode +// ) + } + ) { innerPadding -> + Column( + modifier = Modifier + .padding(innerPadding) + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(200.dp) + .background(gradientBrush), + contentAlignment = Alignment.Center + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "Welcome to", + fontSize = 18.sp, + color = MaterialTheme.colorScheme.surface, + fontWeight = FontWeight.Medium + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "Store Overflow", + fontSize = 26.sp, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.surface + ) + } + } + Spacer(modifier = Modifier.height(20.dp)) + Column( + modifier = Modifier.padding(16.dp) + ) { + TextField( + label = { + Text( + text = "Username", + color = MaterialTheme.colorScheme.outline + ) + }, + colors = TextFieldDefaults.colors( + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent + ), + shape = RoundedCornerShape(12.dp), + value = username, + onValueChange = { + username = it + usernameError = validateUsername(it) + }, + modifier = Modifier + .fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(12.dp)) + + TextField( + value = password, + onValueChange = { + password = it + passwordError = validatePassword(it) + }, + label = { + Text( + text = "Password", + color = MaterialTheme.colorScheme.outline + ) + }, + colors = TextFieldDefaults.colors( + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent + ), + shape = RoundedCornerShape(12.dp), + singleLine = true, + visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), + trailingIcon = { + IconButton( + onClick = { passwordVisible = !passwordVisible } + ) { + Icon( + imageVector = if (passwordVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff, + contentDescription = "Toggle password visibility" + ) + } + }, + modifier = Modifier + .fillMaxWidth() + ) + Spacer(modifier = Modifier.height(16.dp)) + + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Button( + onClick = onLogin, + modifier = Modifier + .wrapContentSize(), + enabled = true, + colors = ButtonColors( + containerColor = Color.Blue.copy(alpha = 0.7f), + contentColor = MaterialTheme.colorScheme.surface, + disabledContainerColor = MaterialTheme.colorScheme.outlineVariant, + disabledContentColor = MaterialTheme.colorScheme.onPrimaryContainer + ), + shape = RoundedCornerShape(30.dp) + ) { + Text( + text = "Login", + fontSize = 18.sp, + modifier = Modifier + .padding( + vertical = 6.dp, + horizontal = 6.dp + ) + ) + } + } + } + } + } +} + +fun validateUsername(username: String): String = when { + username.isEmpty() -> "" + username.length < 3 -> "Username should be at least 3 characters long" + else -> "" +} + +fun validatePassword(password: String): String = when { + password.isEmpty() -> "" + password.length < 6 -> "Password should be at least 6 characters long" + else -> "" +} + +@PreviewLightDark +@Composable +private fun PreviewLoginScreen() { + StoreAppTheme { + LoginSkeleton( + onLogin = {}, + toggleUIMode = {} + ) + } +} diff --git a/store/src/main/kotlin/com/example/store/ui/screen/productdetails/ProductDetailsScreen.kt b/store/src/main/kotlin/com/example/store/ui/screen/productdetails/ProductDetailsScreen.kt new file mode 100644 index 00000000..0f0427e7 --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/screen/productdetails/ProductDetailsScreen.kt @@ -0,0 +1,336 @@ +package com.example.store.ui.screen.productdetails + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +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.Remove +import androidx.compose.material.icons.filled.Star +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +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.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil3.compose.AsyncImage +import com.example.store.theme.StoreAppTheme +import com.example.store.ui.compositions.StoreAppBar + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun ProductDetailsScreen( + product: Product, + goBack: () -> Unit = {}, + toggleUIMode: () -> Unit = {} +) { + ProductDetailsScreenSkeleton( + product = product, + goBack = goBack, + toggleUIMode = toggleUIMode + ) +} + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun ProductDetailsScreenSkeleton( + product: Product, + goBack: () -> Unit = {}, + toggleUIMode: () -> Unit = {} +) { + Scaffold( + topBar = { + StoreAppBar( + title = "Product Details", + goBack = goBack, + toggleUIMode = toggleUIMode + ) + } + ) { innerPadding -> + var quantity by remember { mutableIntStateOf(0) } + val scrollState = rememberScrollState() + Column( + modifier = Modifier + .padding(innerPadding) + .padding(16.dp) + .verticalScroll(scrollState) + ) { + AsyncImage( + model = product.imageUrl, + contentDescription = "Product Image", + modifier = Modifier + .fillMaxWidth() + .height(500.dp) + .background(MaterialTheme.colorScheme.outline) + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = product.title, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(bottom = 8.dp), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + maxLines = 1 + + ) + + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + Icons.Default.Star, + contentDescription = "Rating", + tint = Color.Yellow + ) + Text( + text = ("${product.rating} (${product.reviewCount})"), + modifier = Modifier.padding(start = 4.dp), + color = MaterialTheme.colorScheme.outline + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = product.description, + style = MaterialTheme.typography.bodyMedium, + fontSize = 16.sp + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "$${product.price}", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.error, + fontWeight = FontWeight.Bold, + fontSize = 16.sp + ) + + Row( + verticalAlignment = Alignment.CenterVertically + ) { + IconButton( + onClick = { if (quantity > 0) quantity-- }, + modifier = Modifier + ) { + Box( + modifier = Modifier + .padding(4.dp) + .clip(RoundedCornerShape(8.dp)) + .background(Color.Blue) + .padding(4.dp) + ) { + Icon( + Icons.Default.Remove, + contentDescription = "Decrease", + tint = MaterialTheme.colorScheme.surface + ) + } + } + + Text( + text = "$quantity", + style = MaterialTheme.typography.bodySmall, + fontSize = 16.sp, + modifier = Modifier + .padding(horizontal = 8.dp) + ) + + IconButton( + onClick = { quantity++ } + ) { + Box( + modifier = Modifier + .padding(4.dp) + .clip(RoundedCornerShape(8.dp)) + .background(Color.Blue) + .padding(4.dp) + ) { + Icon( + Icons.Default.Add, + contentDescription = "Increase", + tint = MaterialTheme.colorScheme.surface + ) + } + } + } + } + } + } +} + +data class Product( + val category: String, + val id: Int, + val title: String, + val imageUrl: String, + val price: Double, + val rating: Double, + val reviewCount: Int, + val description: String, + var quantity: Int +) + +val dummyProducts = listOf( + Product( + category = "Backpack", + id = 1, + title = "Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops", + imageUrl = "https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg", + price = 109.95, + rating = 4.5, + reviewCount = 120, + description = "Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday", + quantity = 10 + ), + Product( + category = "T-shirt", + id = 2, + title = "Mens Casual Premium Slim Fit T-Shirts", + imageUrl = "https://fakestoreapi.com/img/71-3HjGNDUL._AC_SY879._SX._UX._SY._UY_.jpg", + price = 22.3, + rating = 4.5, + reviewCount = 259, + description = "Slim-fitting style, contrast raglan long sleeve, three-button henley placket, light weight & soft fabric for breathable and comfortable wearing.", + quantity = 8 + ), + Product( + category = "Jacket", + id = 3, + title = "Mens Cotton Jacket", + imageUrl = "https://fakestoreapi.com/img/71li-ujtlUL._AC_UX679_.jpg", + price = 55.99, + rating = 4.5, + reviewCount = 500, + description = "Great outerwear jackets for Spring/Autumn/Winter, suitable for many occasions, such as working, hiking, camping, mountain/rock climbing, cycling.", + quantity = 5 + ), + Product( + category = "Clothing", + id = 4, + title = "Mens Casual Slim Fit", + imageUrl = "https://fakestoreapi.com/img/71YXzeOuslL._AC_UY879_.jpg", + price = 15.99, + rating = 4.5, + reviewCount = 430, + description = "The color could be slightly different between on the screen and in practice.", + quantity = 12 + ), + Product( + category = "Jewelry", + id = 5, + title = "John Hardy Women's Legends Naga Gold & Silver Dragon Station Chain Bracelet", + imageUrl = "https://fakestoreapi.com/img/71pWzhdJNwL._AC_UL640_QL65_ML3_.jpg", + price = 695.0, + rating = 4.5, + reviewCount = 400, + description = "From our Legends Collection, the Naga was inspired by the mythical water dragon that protects the ocean's pearl.", + quantity = 7 + ), + Product( + category = "Jewelry", + id = 6, + title = "Solid Gold Petite Micropave", + imageUrl = "https://fakestoreapi.com/img/61sbMiUnoGL._AC_UL640_QL65_ML3_.jpg", + price = 168.0, + rating = 4.5, + reviewCount = 70, + description = "Satisfaction Guaranteed. Return or exchange any order within 30 days.", + quantity = 10 + ), + Product( + category = "Jewelry", + id = 7, + title = "White Gold Plated Princess", + imageUrl = "https://fakestoreapi.com/img/71YAIFU48IL._AC_UL640_QL65_ML3_.jpg", + price = 9.99, + rating = 4.5, + reviewCount = 400, + description = "Classic Created Wedding Engagement Solitaire Diamond Promise Ring for Her.", + quantity = 15 + ), + Product( + category = "Jewelry", + id = 8, + title = "Pierced Owl Rose Gold Plated Stainless Steel Double", + imageUrl = "https://fakestoreapi.com/img/51UDEzMJVpL._AC_UL640_QL65_ML3_.jpg", + price = 10.99, + rating = 4.5, + reviewCount = 100, + description = "Rose Gold Plated Double Flared Tunnel Plug Earrings.", + quantity = 3 + ), + Product( + category = "External Hard Drive", + id = 9, + title = "WD 2TB Elements Portable External Hard Drive - USB 3.0", + imageUrl = "https://fakestoreapi.com/img/61IBBVJvSDL._AC_SY879_.jpg", + price = 64.0, + rating = 4.5, + reviewCount = 203, + description = "USB 3.0 and USB 2.0 Compatibility Fast data transfers.", + quantity = 4 + ), + Product( + category = "Storage", + id = 10, + title = "SanDisk SSD PLUS 1TB Internal SSD - SATA III 6 Gb/s", + imageUrl = "https://fakestoreapi.com/img/61U7T1koQqL._AC_SX679_.jpg", + price = 109.0, + rating = 4.7, + reviewCount = 470, + description = "Easy upgrade for faster boot up, shutdown, application load and response.", + quantity = 6 + ) +) + +@Preview(showBackground = true) +@Composable +private fun ProductDetailsScreenSkeletonPreview() { + StoreAppTheme { + val sampleProduct = Product( + category = "Storage", + id = 10, + title = "SanDisk SSD PLUS 1TB Internal SSD - SATA III 6 Gb/s", + imageUrl = "https://fakestoreapi.com/img/61U7T1koQqL._AC_SX679_.jpg", + price = 109.0, + rating = 4.7, + reviewCount = 470, + description = "Easy upgrade for faster boot up, shutdown, application load and response.", + quantity = 6 + ) + + ProductDetailsScreenSkeleton( + product = sampleProduct + ) + } +} diff --git a/store/src/main/kotlin/com/example/store/ui/screen/profile/ProfileScreen.kt b/store/src/main/kotlin/com/example/store/ui/screen/profile/ProfileScreen.kt new file mode 100644 index 00000000..16566d7e --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/screen/profile/ProfileScreen.kt @@ -0,0 +1,187 @@ +package com.example.store.ui.screen.profile + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.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.size +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.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +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.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import org.imaginativeworld.whynotcompose.store.R + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun ProfileScreen( + goBack: () -> Unit, + onOrdersClick: () -> Unit, + onSignOutClick: () -> Unit +) { + Scaffold( +// topBar = { +// TopAppBar( +// title = { Text(text = "Profile") }, +// navigationIcon = { +// IconButton(onClick = goBack) { +// Icon(Icons.Default.ArrowBack, contentDescription = "Back") +// } +// }, +// actions = { +// TextButton(onClick = goBack) { +// Text(text = "Done", color = Color.Blue) +// } +// } +// ) +// } + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(120.dp) + .padding(horizontal = 16.dp) + .clip(RoundedCornerShape(12.dp)) + .background(MaterialTheme.colorScheme.surfaceContainer), + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(org.imaginativeworld.whynotcompose.common.compose.R.drawable.store), + contentDescription = "Profile Image", + modifier = Modifier + .size(100.dp) + .clip(CircleShape), + contentScale = ContentScale.Crop + ) + } + + Spacer(modifier = Modifier.height(30.dp)) + + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) + ) { + Text( + text = "DETAILS", + fontSize = 14.sp, + color = Color.Gray, + textAlign = TextAlign.Start, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Column( + modifier = Modifier + .clip(RoundedCornerShape(12.dp)) + .background(MaterialTheme.colorScheme.surfaceContainer) + .padding(16.dp) + ) { + ProfileInfoRow(title = "Name", value = "John Doe") + ProfileInfoRow(title = "Username", value = "johnd") + ProfileInfoRow(title = "Email", value = "john@gmail.com") + ProfileInfoRow(title = "Phone", value = "1-570-236-7033") + ProfileInfoRow(title = "Address", value = "New Road, Kilcoole") + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + Button( + onClick = onOrdersClick, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.surfaceContainer), + shape = MaterialTheme.shapes.medium + ) { + Text( + text = "Orders", + color = Color.Blue, + fontSize = 18.sp + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + Button( + onClick = onSignOutClick, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.surfaceContainer), + shape = MaterialTheme.shapes.medium + ) { + Text( + text = "Sign Out", + color = MaterialTheme.colorScheme.error, + fontSize = 18.sp + ) + } + } + } +} + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun ProfileInfoRow(title: String, value: String) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = title, + fontSize = 16.sp + ) + Text( + text = value, + fontSize = 16.sp, + fontWeight = FontWeight.Medium + ) + } +} + +@PreviewLightDark +@Composable +private fun ProfileScreenPreview() { + ProfileScreen( + goBack = {}, + onOrdersClick = {}, + onSignOutClick = {} + ) +} diff --git a/store/src/main/kotlin/com/example/store/ui/screen/splash/SplashScreen.kt b/store/src/main/kotlin/com/example/store/ui/screen/splash/SplashScreen.kt new file mode 100644 index 00000000..fc02289c --- /dev/null +++ b/store/src/main/kotlin/com/example/store/ui/screen/splash/SplashScreen.kt @@ -0,0 +1,54 @@ +package com.example.store.ui.screen.splash + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.PreviewLightDark +import com.example.store.theme.StoreAppTheme +import kotlinx.coroutines.delay + +@Composable +fun StoreSplashScreen( + gotoHomeIndex: () -> Unit = {} +) { + LaunchedEffect(gotoHomeIndex) { + delay(1000) + + gotoHomeIndex() + } + + StoreSplashScreenSkeleton() +} + +@PreviewLightDark +@Composable +private fun SplashScreenSkeletonPreview() { + StoreAppTheme { + StoreSplashScreenSkeleton() + } +} + +@Suppress("ktlint:compose:modifier-missing-check") +@Composable +fun StoreSplashScreenSkeleton() { + Scaffold { innerPadding -> + Box( + Modifier + .padding(innerPadding) + .fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text( + text = "Store", + style = MaterialTheme.typography.displayLarge + ) + } + } +} diff --git a/store/src/main/res/drawable/baseline_visibility_24.xml b/store/src/main/res/drawable/baseline_visibility_24.xml new file mode 100644 index 00000000..b8e84326 --- /dev/null +++ b/store/src/main/res/drawable/baseline_visibility_24.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/store/src/main/res/values/colors.xml b/store/src/main/res/values/colors.xml new file mode 100644 index 00000000..f8c6127d --- /dev/null +++ b/store/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/store/src/main/res/values/strings.xml b/store/src/main/res/values/strings.xml new file mode 100644 index 00000000..6f5da54f --- /dev/null +++ b/store/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Store + \ No newline at end of file diff --git a/store/src/main/res/values/themes.xml b/store/src/main/res/values/themes.xml new file mode 100644 index 00000000..38622fe2 --- /dev/null +++ b/store/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +