├── .gitignore
├── .idea
├── .gitignore
├── AndroidProjectSystem.xml
├── appInsightsSettings.xml
├── compiler.xml
├── deploymentTargetSelector.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── kotlinc.xml
├── migrations.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── harry
│ │ └── sokomart
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-playstore.png
│ ├── java
│ │ └── com
│ │ │ └── harry
│ │ │ └── sokomart
│ │ │ ├── MainActivity.kt
│ │ │ ├── data
│ │ │ ├── ProductDao.kt
│ │ │ ├── ProductDatabase.kt
│ │ │ ├── UserDao.kt
│ │ │ └── UserDatabase.kt
│ │ │ ├── model
│ │ │ ├── Product.kt
│ │ │ └── User.kt
│ │ │ ├── navigation
│ │ │ ├── AppNavHost.kt
│ │ │ └── Routes.kt
│ │ │ ├── repository
│ │ │ ├── ProductRepository.kt
│ │ │ └── UserRepository.kt
│ │ │ ├── ui
│ │ │ ├── screens
│ │ │ │ ├── about
│ │ │ │ │ └── AboutScreen.kt
│ │ │ │ ├── auth
│ │ │ │ │ ├── LoginScreen.kt
│ │ │ │ │ └── RegistrationScreen.kt
│ │ │ │ ├── contact
│ │ │ │ │ └── ContactScreen.kt
│ │ │ │ ├── dashboard
│ │ │ │ │ └── DashboardScreen.kt
│ │ │ │ ├── form
│ │ │ │ │ └── FormScreen.kt
│ │ │ │ ├── home
│ │ │ │ │ └── HomeScreen.kt
│ │ │ │ ├── intent
│ │ │ │ │ └── IntentScreen.kt
│ │ │ │ ├── item
│ │ │ │ │ └── ItemScreen.kt
│ │ │ │ ├── more
│ │ │ │ │ └── MoreScreen.kt
│ │ │ │ ├── newscreen
│ │ │ │ │ └── NewScreen.kt
│ │ │ │ ├── paylink
│ │ │ │ │ ├── createlink.kt
│ │ │ │ │ └── linkpreview.kt
│ │ │ │ ├── paylinkauthentication
│ │ │ │ │ ├── signin.kt
│ │ │ │ │ └── signup.kt
│ │ │ │ ├── paylinkcommunity
│ │ │ │ │ └── communityscreen.kt
│ │ │ │ ├── paylinkhome
│ │ │ │ │ ├── PHome.kt
│ │ │ │ │ ├── scaffold.kt
│ │ │ │ │ └── splash.kt
│ │ │ │ ├── paylinkprofile
│ │ │ │ │ └── Profile.kt
│ │ │ │ ├── products
│ │ │ │ │ ├── AddProductScreen.kt
│ │ │ │ │ ├── EditProductScreen.kt
│ │ │ │ │ └── ProductListScreen.kt
│ │ │ │ ├── service
│ │ │ │ │ └── ServiceScreen.kt
│ │ │ │ ├── splash
│ │ │ │ │ └── SplashScreen.kt
│ │ │ │ └── start
│ │ │ │ │ └── StartScreen.kt
│ │ │ └── theme
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ └── Type.kt
│ │ │ └── viewmodel
│ │ │ ├── AuthViewModel.kt
│ │ │ └── ProductViewModel.kt
│ └── res
│ │ ├── drawable
│ │ ├── download.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_launcher_foreground.xml
│ │ ├── img.png
│ │ ├── img_1.png
│ │ ├── img_10.png
│ │ ├── img_11.png
│ │ ├── img_12.png
│ │ ├── img_13.png
│ │ ├── img_14.png
│ │ ├── img_15.png
│ │ ├── img_16.png
│ │ ├── img_2.png
│ │ ├── img_3.png
│ │ ├── img_4.png
│ │ ├── img_5.png
│ │ ├── img_6.png
│ │ ├── img_7.png
│ │ ├── img_8.png
│ │ ├── img_9.png
│ │ ├── name.xml
│ │ ├── price.xml
│ │ ├── product.xml
│ │ ├── visibility.xml
│ │ └── visibilityoff.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ └── data_extraction_rules.xml
│ └── test
│ └── java
│ └── com
│ └── harry
│ └── sokomart
│ └── ExampleUnitTest.kt
├── build.gradle.kts
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/AndroidProjectSystem.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/appInsightsSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
25 |
26 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/deploymentTargetSelector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/migrations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.application)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.compose)
5 | id ("kotlin-kapt")
6 | }
7 |
8 | android {
9 | namespace = "com.harry.sokomart"
10 | compileSdk = 35
11 |
12 | defaultConfig {
13 | applicationId = "com.harry.sokomart"
14 | minSdk = 21
15 | targetSdk = 35
16 | versionCode = 1
17 | versionName = "1.0"
18 |
19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
20 | }
21 |
22 | buildTypes {
23 | release {
24 | isMinifyEnabled = false
25 | proguardFiles(
26 | getDefaultProguardFile("proguard-android-optimize.txt"),
27 | "proguard-rules.pro"
28 | )
29 | }
30 | }
31 | compileOptions {
32 | sourceCompatibility = JavaVersion.VERSION_11
33 | targetCompatibility = JavaVersion.VERSION_11
34 | }
35 | kotlinOptions {
36 | jvmTarget = "11"
37 | }
38 | buildFeatures {
39 | compose = true
40 | }
41 | }
42 |
43 | dependencies {
44 |
45 | implementation(libs.androidx.core.ktx)
46 | implementation(libs.androidx.lifecycle.runtime.ktx)
47 | implementation(libs.androidx.activity.compose)
48 | implementation(platform(libs.androidx.compose.bom))
49 | implementation(libs.androidx.ui)
50 | implementation(libs.androidx.ui.graphics)
51 | implementation(libs.androidx.ui.tooling.preview)
52 | implementation(libs.androidx.material3)
53 | implementation(libs.androidx.navigation.runtime.android)
54 | implementation(libs.androidx.runtime.android)
55 | implementation(libs.androidx.navigation.compose)
56 | testImplementation(libs.junit)
57 | androidTestImplementation(libs.androidx.junit)
58 | androidTestImplementation(libs.androidx.espresso.core)
59 | androidTestImplementation(platform(libs.androidx.compose.bom))
60 | androidTestImplementation(libs.androidx.ui.test.junit4)
61 | debugImplementation(libs.androidx.ui.tooling)
62 | debugImplementation(libs.androidx.ui.test.manifest)
63 |
64 | // Room
65 | implementation("androidx.room:room-runtime:2.6.1")
66 | kapt("androidx.room:room-compiler:2.6.1")
67 | implementation("androidx.room:room-ktx:2.6.1")
68 |
69 | // Image Loading (Coil for Jetpack Compose)
70 | implementation ("io.coil-kt:coil-compose:2.4.0")
71 |
72 | //livedata
73 | implementation("androidx.compose.runtime:runtime-livedata:1.6.4")
74 | }
75 |
76 |
77 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/harry/sokomart/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.harry.sokomart", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
15 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.activity.enableEdgeToEdge
7 | import com.harry.sokomart.navigation.AppNavHost
8 |
9 | class MainActivity : ComponentActivity() {
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | super.onCreate(savedInstanceState)
12 | enableEdgeToEdge()
13 | setContent {
14 |
15 | AppNavHost()
16 |
17 | }
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/data/ProductDao.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.data
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.room.*
5 | import com.harry.sokomart.model.Product
6 |
7 | @Dao
8 | interface ProductDao {
9 | @Query("SELECT * FROM products")
10 | fun getAllProducts(): LiveData>
11 |
12 | @Insert
13 | suspend fun insertProduct(product: Product)
14 |
15 | @Update
16 | suspend fun updateProduct(product: Product)
17 |
18 | @Delete
19 | suspend fun deleteProduct(product: Product)
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/data/ProductDatabase.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.data
2 |
3 | import android.content.Context
4 | import androidx.room.Database
5 | import androidx.room.Room
6 | import androidx.room.RoomDatabase
7 | import com.harry.sokomart.model.Product
8 |
9 | @Database(entities = [Product::class], version = 1, exportSchema = false)
10 | abstract class ProductDatabase : RoomDatabase() {
11 | abstract fun productDao(): ProductDao
12 |
13 | companion object {
14 | @Volatile
15 | private var INSTANCE: ProductDatabase? = null
16 |
17 | fun getDatabase(context: Context): ProductDatabase {
18 | return INSTANCE ?: synchronized(this) {
19 | val instance = Room.databaseBuilder(
20 | context.applicationContext,
21 | ProductDatabase::class.java,
22 | "product_db"
23 | ).build()
24 | INSTANCE = instance
25 | instance
26 | }
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/data/UserDao.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.data
2 |
3 |
4 | import androidx.room.Dao
5 | import androidx.room.Insert
6 | import androidx.room.OnConflictStrategy
7 | import androidx.room.Query
8 | import com.harry.sokomart.model.User
9 |
10 |
11 | @Dao
12 | interface UserDao {
13 | @Insert(onConflict = OnConflictStrategy.REPLACE)
14 | suspend fun registerUser(user: User)
15 |
16 | @Query("SELECT * FROM users WHERE email = :email AND password = :password")
17 | suspend fun loginUser(email: String, password: String): User?
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/data/UserDatabase.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.data
2 |
3 |
4 | import android.content.Context
5 | import androidx.room.Database
6 | import androidx.room.Room
7 | import androidx.room.RoomDatabase
8 | import com.harry.sokomart.model.User
9 |
10 | @Database(entities = [User::class], version = 2, exportSchema = false)
11 | abstract class UserDatabase : RoomDatabase() {
12 | abstract fun userDao(): UserDao
13 |
14 | companion object {
15 | @Volatile
16 | private var INSTANCE: UserDatabase? = null
17 |
18 | fun getDatabase(context: Context): UserDatabase {
19 | return INSTANCE ?: synchronized(this) {
20 | val instance = Room.databaseBuilder(
21 | context.applicationContext,
22 | UserDatabase::class.java,
23 | "user_database"
24 | )
25 | .fallbackToDestructiveMigration() // DANGEROUS IN PRODUCTION, OK FOR NOW
26 | .build()
27 | INSTANCE = instance
28 | instance
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/model/Product.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.model
2 |
3 |
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 |
7 | @Entity(tableName = "products")
8 | data class Product(
9 | @PrimaryKey(autoGenerate = true) val id: Int = 0,
10 | val name: String,
11 | val price: Double,
12 | val phone: String,
13 | val imagePath: String
14 | )
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/model/User.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.model
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity(tableName = "users")
7 | data class User(
8 | @PrimaryKey(autoGenerate = true) val id: Int = 0,
9 | val username: String,
10 | val email: String,
11 | val password: String,
12 | val role: String // "admin" or "user"
13 | )
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/navigation/AppNavHost.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.navigation
2 |
3 | import PHome
4 | import android.os.Build
5 | import androidx.annotation.RequiresApi
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.platform.LocalContext
9 | import androidx.lifecycle.viewmodel.compose.viewModel
10 | import androidx.navigation.NavHostController
11 | import androidx.navigation.NavType
12 | import androidx.navigation.compose.NavHost
13 | import androidx.navigation.compose.composable
14 | import androidx.navigation.compose.rememberNavController
15 | import androidx.navigation.navArgument
16 | import com.harry.sokomart.data.UserDatabase
17 | import com.harry.sokomart.repository.UserRepository
18 | import com.harry.sokomart.ui.screens.about.AboutScreen
19 | import com.harry.sokomart.ui.screens.auth.LoginScreen
20 | import com.harry.sokomart.ui.screens.auth.RegisterScreen
21 | import com.harry.sokomart.ui.screens.form.FormScreen
22 | import com.harry.sokomart.ui.screens.home.HomeScreen
23 | import com.harry.sokomart.ui.screens.intent.IntentScreen
24 | import com.harry.sokomart.ui.screens.item.ItemScreen
25 | import com.harry.sokomart.ui.screens.more.MoreScreen
26 | import com.harry.sokomart.ui.screens.paylinkauthentication.SignInScreen
27 | import com.harry.sokomart.ui.screens.paylinkauthentication.SignInScreenPreview
28 | import com.harry.sokomart.ui.screens.products.AddProductScreen
29 | import com.harry.sokomart.ui.screens.products.EditProductScreen
30 | import com.harry.sokomart.ui.screens.products.ProductListScreen
31 | import com.harry.sokomart.ui.screens.service.ServiceScreen
32 | import com.harry.sokomart.ui.screens.splash.SplashScreen
33 | import com.harry.sokomart.ui.screens.start.StartScreen
34 | import com.harry.sokomart.viewmodel.AuthViewModel
35 | import com.harry.sokomart.viewmodel.ProductViewModel
36 |
37 | @RequiresApi(Build.VERSION_CODES.M)
38 | @Composable
39 | fun AppNavHost(
40 | modifier: Modifier = Modifier,
41 | navController: NavHostController = rememberNavController(),
42 | startDestination: String = ROUT_SPLASH,
43 | productViewModel: ProductViewModel = viewModel(),
44 | ) {
45 | val context = LocalContext.current
46 |
47 | NavHost(
48 | navController = navController,
49 | startDestination = startDestination,
50 | modifier = modifier
51 | ) {
52 | composable(ROUT_HOME) {
53 | HomeScreen(navController)
54 | }
55 | composable(ROUT_ABOUT) {
56 | AboutScreen(navController)
57 | }
58 | composable(ROUT_ITEM) {
59 | ItemScreen(navController)
60 | }
61 | composable(ROUT_START) {
62 | StartScreen(navController)
63 | }
64 | composable(ROUT_INTENT) {
65 | IntentScreen(navController)
66 | }
67 | composable(ROUT_MORE) {
68 | MoreScreen(navController)
69 | }
70 | composable(ROUT_DASHBOARD) {
71 | DashboardScreen(navController)
72 | }
73 | composable(ROUT_SERVICE) {
74 | ServiceScreen(navController)
75 | }
76 | composable(ROUT_SPLASHS) {
77 | Splash(navController)
78 | }
79 | composable(ROUT_SPLASH) {
80 | SplashScreen(navController)
81 | }
82 | composable(ROUT_FORM) {
83 | FormScreen(navController)
84 | }
85 | composable(ROUT_PHOME) {
86 | PHome(navController)
87 | }
88 |
89 | //AUTHENTICATION
90 |
91 | // Initialize Room Database and Repository for Authentication
92 | val appDatabase = UserDatabase.getDatabase(context)
93 | val authRepository = UserRepository(appDatabase.userDao())
94 | val authViewModel: AuthViewModel = AuthViewModel(authRepository)
95 | composable(ROUT_REGISTER) {
96 | RegisterScreen(authViewModel, navController) {
97 | navController.navigate(ROUT_LOGIN) {
98 | popUpTo(ROUT_REGISTER) { inclusive = true }
99 | }
100 | }
101 | }
102 |
103 | composable(ROUT_LOGIN) {
104 | LoginScreen(authViewModel, navController) {
105 | navController.navigate(ROUT_HOME) {
106 | popUpTo(ROUT_LOGIN) { inclusive = true }
107 | }
108 | }
109 | }
110 |
111 | // PRODUCTS
112 | composable(ROUT_ADD_PRODUCT) {
113 | AddProductScreen(navController, productViewModel)
114 | }
115 |
116 | composable(ROUT_PRODUCT_LIST) {
117 | ProductListScreen(navController, productViewModel)
118 | }
119 |
120 | composable(
121 | route = ROUT_EDIT_PRODUCT,
122 | arguments = listOf(navArgument("productId") { type = NavType.IntType })
123 | ) { backStackEntry ->
124 | val productId = backStackEntry.arguments?.getInt("productId")
125 | if (productId != null) {
126 | EditProductScreen(productId, navController, productViewModel)
127 | }
128 | }
129 |
130 |
131 |
132 |
133 |
134 | }
135 | }
136 |
137 | @Composable
138 | fun Splashscreen(x0: NavHostController) {
139 | TODO("Not yet implemented")
140 | }
141 |
142 | @Composable
143 | fun Splash(x0: NavHostController) {
144 | TODO("Not yet implemented")
145 | }
146 |
147 | @Composable
148 | fun DashboardScreen(x0: NavHostController) {
149 | TODO("Not yet implemented")
150 | }
151 |
152 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/navigation/Routes.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.navigation
2 |
3 | const val ROUT_HOME = "home"
4 | const val ROUT_ABOUT = "about"
5 | const val ROUT_ITEM = "item"
6 | const val ROUT_START = "start"
7 | const val ROUT_INTENT = "intent"
8 | const val ROUT_MORE = "more"
9 | const val ROUT_DASHBOARD = "dashboard"
10 | const val ROUT_SERVICE = "service"
11 | const val ROUT_SPLASH = "splash"
12 | const val ROUT_SPLASHS = "splashs"
13 | const val ROUT_NEWSCREEN = "newscreen"
14 | const val ROUT_FORM = "form"
15 | const val ROUT_PHOME = "phome"
16 | const val ROUT_PROFILE = "profile"
17 |
18 | //auth
19 | const val ROUT_REGISTER = "Register"
20 | const val ROUT_LOGIN = "Login"
21 | const val ROUT_LOGIN = "Login"
22 |
23 | //Products
24 |
25 | const val ROUT_ADD_PRODUCT = "add_product"
26 | const val ROUT_PRODUCT_LIST = "product_list"
27 | const val ROUT_EDIT_PRODUCT = "edit_product/{productId}"
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/repository/ProductRepository.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.repository
2 |
3 | import android.content.Context
4 | import com.harry.sokomart.data.ProductDatabase
5 | import com.harry.sokomart.model.Product
6 |
7 | class ProductRepository(context: Context) {
8 | private val productDao = ProductDatabase.getDatabase(context).productDao()
9 |
10 | suspend fun insertProduct(product: Product) {
11 | productDao.insertProduct(product)
12 | }
13 |
14 | fun getAllProducts() = productDao.getAllProducts()
15 |
16 | suspend fun deleteProduct(product: Product) = productDao.deleteProduct(product)
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/repository/UserRepository.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.repository
2 |
3 | import com.harry.sokomart.data.UserDao
4 | import com.harry.sokomart.model.User
5 |
6 |
7 | class UserRepository(private val userDao: UserDao) {
8 | suspend fun registerUser(user: User) {
9 | userDao.registerUser(user)
10 | }
11 |
12 | suspend fun loginUser(email: String, password: String): User? {
13 | return userDao.loginUser(email, password)
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/about/AboutScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.about
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.tooling.preview.Preview
5 | import androidx.navigation.NavController
6 | import androidx.navigation.compose.rememberNavController
7 |
8 | @Composable
9 | fun AboutScreen(navController: NavController){
10 |
11 |
12 | }
13 |
14 | @Preview(showBackground = true)
15 | @Composable
16 | fun AboutScreenPreview(){
17 | AboutScreen(navController= rememberNavController())
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/auth/LoginScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.auth
2 |
3 | import android.widget.Toast
4 | import androidx.compose.animation.AnimatedVisibility
5 | import androidx.compose.animation.core.tween
6 | import androidx.compose.animation.fadeIn
7 | import androidx.compose.animation.fadeOut
8 | import androidx.compose.foundation.background
9 | import androidx.compose.foundation.layout.*
10 | import androidx.compose.foundation.shape.RoundedCornerShape
11 | import androidx.compose.foundation.text.KeyboardOptions
12 | import androidx.compose.material.icons.Icons
13 | import androidx.compose.material.icons.filled.Email
14 | import androidx.compose.material.icons.filled.Lock
15 | import androidx.compose.material3.*
16 | import androidx.compose.runtime.Composable
17 | import androidx.compose.runtime.LaunchedEffect
18 | import androidx.compose.runtime.getValue
19 | import androidx.compose.runtime.mutableStateOf
20 | import androidx.compose.runtime.remember
21 | import androidx.compose.runtime.setValue
22 | import androidx.compose.ui.Alignment
23 | import androidx.compose.ui.Modifier
24 | import androidx.compose.ui.graphics.Brush
25 | import androidx.compose.ui.graphics.Color
26 | import androidx.compose.ui.platform.LocalContext
27 | import androidx.compose.ui.res.painterResource
28 | import androidx.compose.ui.text.font.FontFamily
29 | import androidx.compose.ui.text.input.KeyboardType
30 | import androidx.compose.ui.text.input.PasswordVisualTransformation
31 | import androidx.compose.ui.text.input.VisualTransformation
32 | import androidx.compose.ui.unit.dp
33 | import androidx.compose.ui.unit.sp
34 | import androidx.navigation.NavController
35 | import com.harry.sokomart.navigation.ROUT_HOME
36 | import com.harry.sokomart.navigation.ROUT_PROFILE
37 | import com.harry.sokomart.navigation.ROUT_REGISTER
38 | import com.harry.sokomart.viewmodel.AuthViewModel
39 | import com.harry.sokomart.R
40 |
41 | @Composable
42 | fun LoginScreen(
43 | authViewModel: AuthViewModel,
44 | navController: NavController,
45 | onLoginSuccess: () -> Unit
46 | ) {
47 | var email by remember { mutableStateOf("") }
48 | var password by remember { mutableStateOf("") }
49 | var passwordVisible by remember { mutableStateOf(false) }
50 | val context = LocalContext.current
51 |
52 | // Observe login logic
53 | LaunchedEffect(authViewModel) {
54 | authViewModel.loggedInUser = { user ->
55 | if (user == null) {
56 | Toast.makeText(context, "Invalid Credentials", Toast.LENGTH_SHORT).show()
57 | } else {
58 | if (user.role == "admin") {
59 | navController.navigate(ROUT_PROFILE) {
60 | }
61 | } else {
62 | navController.navigate(ROUT_HOME) {
63 | }
64 | }
65 | }
66 | }
67 | }
68 | //End of login logic
69 |
70 | Column(
71 | modifier = Modifier
72 | .fillMaxSize()
73 | .padding(20.dp),
74 | verticalArrangement = Arrangement.Center,
75 | horizontalAlignment = Alignment.CenterHorizontally
76 | ) {
77 | // Animated Welcome Text
78 | AnimatedVisibility(
79 | visible = true,
80 | enter = fadeIn(animationSpec = tween(1000)),
81 | exit = fadeOut(animationSpec = tween(1000))
82 | ) {
83 | Text(
84 | text = "Welcome Back!",
85 | fontSize = 40.sp,
86 | fontFamily = FontFamily.Cursive
87 | )
88 | }
89 |
90 | Spacer(modifier = Modifier.height(24.dp))
91 |
92 | // Email Input
93 | OutlinedTextField(
94 | value = email,
95 | onValueChange = { email = it },
96 | label = { Text("Email") },
97 | leadingIcon = { Icon(Icons.Filled.Email, contentDescription = "Email Icon") },
98 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
99 | modifier = Modifier.fillMaxWidth(),
100 | shape = RoundedCornerShape(12.dp)
101 | )
102 |
103 | Spacer(modifier = Modifier.height(12.dp))
104 |
105 | // Password Input with Show/Hide Toggle
106 | OutlinedTextField(
107 | value = password,
108 | onValueChange = { password = it },
109 | label = { Text("Password") },
110 | leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "Password Icon") },
111 | visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
112 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
113 | modifier = Modifier.fillMaxWidth(),
114 | shape = RoundedCornerShape(12.dp),
115 | trailingIcon = {
116 | val image = if (passwordVisible) painterResource(R.drawable.visibility) else painterResource(R.drawable.visibilityoff)
117 | IconButton(onClick = { passwordVisible = !passwordVisible }) {
118 | Icon(image, contentDescription = if (passwordVisible) "Hide Password" else "Show Password")
119 | }
120 | }
121 | )
122 |
123 | Spacer(modifier = Modifier.height(20.dp))
124 |
125 | // Gradient Login Button
126 | Box(
127 | modifier = Modifier
128 | .fillMaxWidth()
129 | .height(50.dp)
130 | .background(
131 | brush = Brush.horizontalGradient(
132 | colors = listOf(Color(0xFF00C6FF), Color(0xFF0072FF))
133 | ),
134 | shape = RoundedCornerShape(12.dp)
135 | ),
136 | contentAlignment = Alignment.Center
137 | ) {
138 | Button(
139 | onClick = {
140 | if (email.isBlank() || password.isBlank()) {
141 | Toast.makeText(context, "Please enter email and password", Toast.LENGTH_SHORT).show()
142 | } else {
143 | authViewModel.loginUser(email, password)
144 | }
145 | },
146 | modifier = Modifier.fillMaxSize(),
147 | colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent),
148 | shape = RoundedCornerShape(12.dp)
149 | ) {
150 | Text("Login", color = Color.White)
151 | }
152 | }
153 |
154 | Spacer(modifier = Modifier.height(16.dp))
155 |
156 | // Register Navigation Button
157 | TextButton(onClick = { navController.navigate(ROUT_REGISTER) }) {
158 | Text("Don't have an account? Register")
159 | }
160 | }
161 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/auth/RegistrationScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.auth
2 |
3 | import android.widget.Toast
4 | import androidx.compose.animation.AnimatedVisibility
5 | import androidx.compose.animation.core.LinearEasing
6 | import androidx.compose.animation.core.animateFloatAsState
7 | import androidx.compose.animation.core.tween
8 | import androidx.compose.animation.fadeIn
9 | import androidx.compose.animation.fadeOut
10 | import androidx.compose.foundation.background
11 | import androidx.compose.foundation.layout.*
12 | import androidx.compose.foundation.text.KeyboardOptions
13 | import androidx.compose.material.icons.Icons
14 | import androidx.compose.material.icons.filled.*
15 | import androidx.compose.material3.*
16 | import androidx.compose.runtime.*
17 | import androidx.compose.ui.Alignment
18 | import androidx.compose.ui.Modifier
19 | import androidx.compose.ui.graphics.Brush
20 | import androidx.compose.ui.graphics.Color
21 | import androidx.compose.ui.platform.LocalContext
22 | import androidx.compose.ui.res.painterResource
23 | import androidx.compose.ui.text.font.FontFamily
24 | import androidx.compose.ui.text.input.*
25 | import androidx.compose.ui.unit.dp
26 | import androidx.compose.ui.unit.sp
27 | import androidx.navigation.NavController
28 | import com.harry.sokomart.model.User
29 | import com.harry.sokomart.navigation.ROUT_LOGIN
30 | import com.harry.sokomart.viewmodel.AuthViewModel
31 | import com.harry.sokomart.R
32 |
33 | @OptIn(ExperimentalMaterial3Api::class)
34 | @Composable
35 | fun RegisterScreen(
36 | authViewModel: AuthViewModel,
37 | navController: NavController,
38 | onRegisterSuccess: () -> Unit
39 | ) {
40 | var username by remember { mutableStateOf("") }
41 | var email by remember { mutableStateOf("") }
42 | var password by remember { mutableStateOf("") }
43 | var confirmPassword by remember { mutableStateOf("") }
44 | var passwordVisible by remember { mutableStateOf(false) }
45 | var confirmPasswordVisible by remember { mutableStateOf(false) }
46 | val context = LocalContext.current
47 | val animatedAlpha by animateFloatAsState(
48 | targetValue = 1f,
49 | animationSpec = tween(durationMillis = 1500, easing = LinearEasing),
50 | label = "Animated Alpha"
51 | )
52 |
53 | Column(
54 | modifier = Modifier
55 | .fillMaxSize()
56 | .padding(16.dp),
57 | verticalArrangement = Arrangement.Center,
58 | horizontalAlignment = Alignment.CenterHorizontally
59 | ) {
60 | Spacer(modifier = Modifier.height(8.dp))
61 | AnimatedVisibility(visible = true, enter = fadeIn(), exit = fadeOut()) {
62 | Text(
63 | "Create Your Account",
64 | fontSize = 40.sp,
65 | fontFamily = FontFamily.Cursive
66 | )
67 | }
68 |
69 | Spacer(modifier = Modifier.height(16.dp))
70 |
71 | //Username
72 | OutlinedTextField(
73 | value = username,
74 | onValueChange = { username = it },
75 | label = { Text("Username") },
76 | leadingIcon = { Icon(Icons.Filled.Person, contentDescription = "Username Icon") },
77 | modifier = Modifier.fillMaxWidth()
78 | )
79 | //End of username
80 |
81 |
82 |
83 | Spacer(modifier = Modifier.height(8.dp))
84 |
85 | //Email
86 | OutlinedTextField(
87 | value = email,
88 | onValueChange = { email = it },
89 | label = { Text("Email") },
90 | leadingIcon = { Icon(Icons.Filled.Email, contentDescription = "Email Icon") },
91 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
92 | modifier = Modifier.fillMaxWidth()
93 | )
94 | //End of email
95 |
96 | Spacer(modifier = Modifier.height(8.dp))
97 |
98 |
99 | //Role
100 | var role by remember { mutableStateOf("user") }
101 | val roleOptions = listOf("user", "admin")
102 | var expanded by remember { mutableStateOf(false) }
103 |
104 | ExposedDropdownMenuBox(
105 | expanded = expanded,
106 | onExpandedChange = { expanded = !expanded }
107 | ) {
108 | OutlinedTextField(
109 | value = role,
110 | onValueChange = {},
111 | readOnly = true,
112 | label = { Text("Select Role") },
113 | trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
114 | modifier = Modifier.menuAnchor().fillMaxWidth()
115 | )
116 | ExposedDropdownMenu(
117 | expanded = expanded,
118 | onDismissRequest = { expanded = false }
119 | ) {
120 | roleOptions.forEach { selectionOption ->
121 | DropdownMenuItem(
122 | text = { Text(selectionOption) },
123 | onClick = {
124 | role = selectionOption
125 | expanded = false
126 | }
127 | )
128 | }
129 | }
130 | }
131 | //End of role
132 |
133 |
134 |
135 |
136 |
137 |
138 | // Password Input Field with Show/Hide Toggle
139 | OutlinedTextField(
140 | value = password,
141 | onValueChange = { password = it },
142 | label = { Text("Password") },
143 | visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
144 | leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "Password Icon") },
145 | trailingIcon = {
146 | val image = if (passwordVisible) painterResource(R.drawable.visibility) else painterResource(R.drawable.visibilityoff)
147 | IconButton(onClick = { passwordVisible = !passwordVisible }) {
148 | Icon(image, contentDescription = if (passwordVisible) "Hide Password" else "Show Password")
149 | }
150 | },
151 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
152 | modifier = Modifier.fillMaxWidth()
153 | )
154 |
155 | Spacer(modifier = Modifier.height(8.dp))
156 |
157 | // Confirm Password Input Field with Show/Hide Toggle
158 | OutlinedTextField(
159 | value = confirmPassword,
160 | onValueChange = { confirmPassword = it },
161 | label = { Text("Confirm Password") },
162 | visualTransformation = if (confirmPasswordVisible) VisualTransformation.None else PasswordVisualTransformation(),
163 | leadingIcon = { Icon(Icons.Filled.Lock, contentDescription = "Confirm Password Icon") },
164 | trailingIcon = {
165 | val image = if (confirmPasswordVisible) painterResource(R.drawable.visibility) else painterResource(R.drawable.visibilityoff)
166 | IconButton(onClick = { confirmPasswordVisible = !confirmPasswordVisible }) {
167 | Icon(image, contentDescription = if (confirmPasswordVisible) "Hide Password" else "Show Password")
168 | }
169 | },
170 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
171 | modifier = Modifier.fillMaxWidth()
172 | )
173 |
174 | Spacer(modifier = Modifier.height(5.dp))
175 |
176 | Box(
177 | modifier = Modifier
178 | .fillMaxWidth()
179 | .height(50.dp)
180 | .background(
181 | brush = Brush.horizontalGradient(
182 | colors = listOf(Color(0xFF00C6FF), Color(0xFF0072FF))
183 | ),
184 | shape = MaterialTheme.shapes.medium
185 | ),
186 | contentAlignment = Alignment.Center
187 | ) {
188 | Button(
189 | onClick = {
190 | if (username.isBlank() || email.isBlank() || password.isBlank() || confirmPassword.isBlank()) {
191 | Toast.makeText(context, "All fields are required", Toast.LENGTH_SHORT).show()
192 | } else if (password != confirmPassword) {
193 | Toast.makeText(context, "Passwords do not match", Toast.LENGTH_SHORT).show()
194 | } else {
195 | authViewModel.registerUser(User(username = username, email = email, role = role, password = password))
196 | onRegisterSuccess()
197 | }
198 | },
199 | modifier = Modifier.fillMaxSize(),
200 | colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent)
201 | ) {
202 | Text("Register", color = Color.White)
203 | }
204 | }
205 |
206 | Spacer(modifier = Modifier.height(5.dp))
207 |
208 | TextButton(
209 | onClick = { navController.navigate(ROUT_LOGIN) }
210 | ) {
211 | Text("Already have an account? Login")
212 | }
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/contact/ContactScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.contact
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.Spacer
5 | import androidx.compose.foundation.layout.fillMaxSize
6 | import androidx.compose.foundation.layout.height
7 | import androidx.compose.foundation.layout.padding
8 | import androidx.compose.material.icons.Icons
9 | import androidx.compose.material.icons.filled.Add
10 | import androidx.compose.material.icons.filled.ArrowBack
11 | import androidx.compose.material.icons.filled.Favorite
12 | import androidx.compose.material.icons.filled.Home
13 | import androidx.compose.material.icons.filled.Person
14 | import androidx.compose.material3.ExperimentalMaterial3Api
15 | import androidx.compose.material3.FloatingActionButton
16 | import androidx.compose.material3.Icon
17 | import androidx.compose.material3.IconButton
18 | import androidx.compose.material3.NavigationBar
19 | import androidx.compose.material3.NavigationBarItem
20 | import androidx.compose.material3.Scaffold
21 | import androidx.compose.material3.Text
22 | import androidx.compose.material3.TopAppBar
23 | import androidx.compose.material3.TopAppBarDefaults
24 | import androidx.compose.runtime.Composable
25 | import androidx.compose.runtime.getValue
26 | import androidx.compose.runtime.mutableStateOf
27 | import androidx.compose.runtime.remember
28 | import androidx.compose.runtime.setValue
29 | import androidx.compose.ui.Modifier
30 | import androidx.compose.ui.graphics.Color
31 | import androidx.navigation.NavController
32 | import androidx.compose.ui.tooling.preview.Preview
33 | import androidx.compose.ui.unit.dp
34 | import androidx.compose.ui.unit.sp
35 | import androidx.navigation.compose.rememberNavController
36 |
37 | @OptIn(ExperimentalMaterial3Api::class)
38 | @Composable
39 | fun ContactScreen(navController: NavController){
40 | //Scaffold
41 |
42 | var selectedIndex by remember { mutableStateOf(0) }
43 |
44 | Scaffold(
45 | //TopBar
46 | topBar = {
47 | TopAppBar(
48 | title = { Text("Item Screen") },
49 | navigationIcon = {
50 | IconButton(onClick = { /* Handle back/nav */ }) {
51 | Icon(Icons.Default.ArrowBack, contentDescription = "Back")
52 | }
53 | },
54 | colors = TopAppBarDefaults.topAppBarColors(
55 | containerColor = Color.LightGray,
56 | titleContentColor = Color.White,
57 | navigationIconContentColor = Color.White
58 | )
59 | )
60 | },
61 |
62 | //BottomBar
63 | bottomBar = {
64 | NavigationBar(
65 | containerColor = Color.LightGray
66 | ){
67 | NavigationBarItem(
68 | icon = { Icon(Icons.Default.Home, contentDescription = "Home") },
69 | label = { Text("Home") },
70 | selected = selectedIndex == 0,
71 | onClick = { selectedIndex = 0
72 | //navController.navigate(ROUT_HOME)
73 | }
74 | )
75 | NavigationBarItem(
76 | icon = { Icon(Icons.Default.Favorite, contentDescription = "Favorites") },
77 | label = { Text("Favorites") },
78 | selected = selectedIndex == 1,
79 | onClick = { selectedIndex = 1
80 | // navController.navigate(ROUT_HOME)
81 | }
82 | )
83 | NavigationBarItem(
84 | icon = { Icon(Icons.Default.Person, contentDescription = "Profile") },
85 | label = { Text("Profile") },
86 | selected = selectedIndex == 2,
87 | onClick = { selectedIndex = 2
88 | // navController.navigate(ROUT_HOME)
89 | }
90 | )
91 |
92 | }
93 | },
94 |
95 | //FloatingActionButton
96 | floatingActionButton = {
97 | FloatingActionButton(
98 | onClick = { /* Add action */ },
99 | containerColor = Color.LightGray
100 | ) {
101 | Icon(Icons.Default.Add, contentDescription = "Add")
102 | }
103 | },
104 | content = { paddingValues ->
105 | Column(
106 | modifier = Modifier
107 | .padding(paddingValues).fillMaxSize()
108 | ) {
109 |
110 |
111 | //Main Contents of the page
112 | Text(text = "Welcome to Homescreen Screen", fontSize = 20.sp)
113 | Spacer(modifier = Modifier.height(8.dp))
114 | Text("This is where the main content goes.")
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | }
128 | }
129 | )
130 |
131 | //End of scaffold
132 |
133 |
134 |
135 |
136 |
137 | }
138 |
139 |
140 |
141 |
142 | @Preview(showBackground = true)
143 | @Composable
144 | fun ContactScreenPreview(){
145 | ContactScreen(navController= rememberNavController())
146 | }
147 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/dashboard/DashboardScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.dashboard
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.foundation.clickable
5 | import androidx.compose.foundation.layout.Arrangement
6 | import androidx.compose.foundation.layout.Column
7 | import androidx.compose.foundation.layout.Row
8 | import androidx.compose.foundation.layout.Spacer
9 | import androidx.compose.foundation.layout.fillMaxSize
10 | import androidx.compose.foundation.layout.fillMaxWidth
11 | import androidx.compose.foundation.layout.height
12 | import androidx.compose.foundation.layout.padding
13 | import androidx.compose.foundation.layout.size
14 | import androidx.compose.foundation.layout.width
15 | import androidx.compose.foundation.shape.RoundedCornerShape
16 | import androidx.compose.material3.Card
17 | import androidx.compose.material3.CardDefaults
18 | import androidx.compose.material3.Text
19 | import androidx.compose.runtime.Composable
20 | import androidx.compose.ui.Alignment
21 | import androidx.compose.ui.Modifier
22 | import androidx.compose.ui.draw.clip
23 | import androidx.compose.ui.res.painterResource
24 | import androidx.compose.ui.tooling.preview.Preview
25 | import androidx.compose.ui.unit.dp
26 | import androidx.compose.ui.unit.sp
27 | import androidx.navigation.NavController
28 | import androidx.navigation.compose.rememberNavController
29 | import com.harry.sokomart.R
30 | import com.harry.sokomart.navigation.ROUT_ITEM
31 | import com.harry.sokomart.navigation.ROUT_START
32 | import com.harry.sokomart.ui.theme.mytheme
33 |
34 | @Composable
35 | fun AboutScreen(navController: NavController){
36 | Column (
37 | modifier = Modifier.fillMaxSize(),
38 | ){
39 | //card start
40 | Card (
41 | modifier = Modifier.fillMaxWidth().height(300.dp),
42 | shape = RoundedCornerShape(bottomStart = 40.dp, bottomEnd = 40.dp),
43 | colors = CardDefaults.cardColors(mytheme)
44 | ){
45 | Column (
46 | modifier = Modifier.fillMaxSize(),
47 | horizontalAlignment = Alignment.CenterHorizontally,
48 | verticalArrangement = Arrangement.Center
49 | ){
50 | Image(
51 | painter = painterResource(R.drawable.img_14),
52 | contentDescription = "home",
53 | modifier = Modifier.size(200.dp).clip(shape = RoundedCornerShape(10.dp)),
54 | )
55 | Text(
56 | text = "Kai & Karo",
57 | fontSize = 48.sp,
58 |
59 | )
60 |
61 | }
62 | }
63 | //end of card
64 | Row (
65 | modifier = Modifier.padding(20.dp)
66 | ){
67 | Spacer(modifier = Modifier.width(20.dp))
68 | Card (
69 | modifier = Modifier.width(150.dp).height(180.dp).clickable{
70 | navController.navigate(ROUT_START)
71 | },
72 | elevation = CardDefaults.cardElevation(10.dp)
73 | ){
74 | Column (
75 | modifier = Modifier.fillMaxSize(),
76 | horizontalAlignment = Alignment.CenterHorizontally,
77 | verticalArrangement = Arrangement.Center,
78 | ){
79 | Image(
80 | painter = painterResource(R.drawable.img_9),
81 | contentDescription = "home",
82 | modifier = Modifier.size(100.dp).clip(shape = RoundedCornerShape(10.dp)),
83 | )
84 | Text(
85 | text = "Home"
86 | )
87 | }
88 | }
89 | Spacer(modifier = Modifier.width(20.dp))
90 | Card (
91 | modifier = Modifier.width(150.dp).height(180.dp),
92 | elevation = CardDefaults.cardElevation(10.dp)
93 | ){
94 | Column (
95 | modifier = Modifier.fillMaxSize(),
96 | horizontalAlignment = Alignment.CenterHorizontally,
97 | verticalArrangement = Arrangement.Center,
98 | ){
99 | Image(
100 | painter = painterResource(R.drawable.img_10),
101 | contentDescription = "About",
102 | modifier = Modifier.size(100.dp).clip(shape = RoundedCornerShape(10.dp)),
103 | )
104 | Text(
105 | text = "About"
106 | )
107 | }
108 | }
109 | Spacer(modifier = Modifier.height(20.dp))
110 | }
111 | Row (
112 | modifier = Modifier.padding(20.dp)
113 | ){
114 | Spacer(modifier = Modifier.width(20.dp))
115 | Card (
116 | modifier = Modifier.width(150.dp).height(180.dp),
117 | elevation = CardDefaults.cardElevation(10.dp)
118 | ){
119 | Column (
120 | modifier = Modifier.fillMaxSize(),
121 | horizontalAlignment = Alignment.CenterHorizontally,
122 | verticalArrangement = Arrangement.Center,
123 | ){
124 | Image(
125 | painter = painterResource(R.drawable.img_12),
126 | contentDescription = "home",
127 | modifier = Modifier.size(100.dp).clip(shape = RoundedCornerShape(10.dp)),
128 | )
129 | Text(
130 | text = "Contact"
131 | )
132 | }
133 | }
134 | Spacer(modifier = Modifier.width(20.dp))
135 | Card (
136 | modifier = Modifier.width(150.dp).height(180.dp).clickable{
137 | navController.navigate(ROUT_ITEM)
138 | },
139 | elevation = CardDefaults.cardElevation(10.dp)
140 | ){
141 | Column (
142 | modifier = Modifier.fillMaxSize(),
143 | horizontalAlignment = Alignment.CenterHorizontally,
144 | verticalArrangement = Arrangement.Center,
145 | ){
146 | Image(
147 | painter = painterResource(R.drawable.img_11),
148 | contentDescription = "home",
149 | modifier = Modifier.size(100.dp).clip(shape = RoundedCornerShape(10.dp)),
150 | )
151 | Text(
152 | text = "Products"
153 | )
154 | }
155 | }
156 | Spacer(modifier = Modifier.height(20.dp))
157 | }
158 | }
159 |
160 | }
161 |
162 | @Preview(showBackground = true)
163 | @Composable
164 | fun AboutScreenPreview(){
165 | AboutScreen(navController= rememberNavController())
166 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/form/FormScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.form
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.Spacer
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.foundation.layout.fillMaxWidth
9 | import androidx.compose.foundation.layout.height
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.foundation.layout.size
12 | import androidx.compose.foundation.shape.RoundedCornerShape
13 | import androidx.compose.foundation.text.KeyboardOptions
14 | import androidx.compose.material.icons.Icons
15 | import androidx.compose.material.icons.filled.Email
16 | import androidx.compose.material.icons.filled.Face
17 | import androidx.compose.material.icons.filled.Lock
18 | import androidx.compose.material.icons.filled.Person
19 | import androidx.compose.material3.Button
20 | import androidx.compose.material3.ButtonDefaults
21 | import androidx.compose.material3.Icon
22 | import androidx.compose.material3.OutlinedTextField
23 | import androidx.compose.material3.OutlinedTextFieldDefaults
24 | import androidx.compose.material3.Text
25 | import androidx.compose.runtime.Composable
26 | import androidx.compose.runtime.getValue
27 | import androidx.compose.runtime.mutableStateOf
28 | import androidx.compose.runtime.remember
29 | import androidx.compose.runtime.setValue
30 | import androidx.compose.ui.Alignment
31 | import androidx.compose.ui.Modifier
32 | import androidx.compose.ui.res.painterResource
33 | import androidx.compose.ui.text.input.KeyboardType
34 | import androidx.compose.ui.text.input.PasswordVisualTransformation
35 | import androidx.compose.ui.tooling.preview.Preview
36 | import androidx.compose.ui.unit.dp
37 | import androidx.compose.ui.unit.sp
38 | import androidx.navigation.NavController
39 | import androidx.navigation.compose.rememberNavController
40 | import com.harry.sokomart.R
41 | import com.harry.sokomart.ui.theme.blue
42 | import com.harry.sokomart.ui.theme.mytheme
43 |
44 | @Composable
45 | fun FormScreen(navController: NavController){
46 | Column (
47 | modifier = Modifier.fillMaxSize(),
48 | horizontalAlignment = Alignment.CenterHorizontally,
49 | verticalArrangement = Arrangement.Center
50 | ){
51 | Image(
52 | painter = painterResource(id = R.drawable.img),
53 | contentDescription = "home",
54 | modifier = Modifier.size(250.dp),
55 | )
56 | Text(
57 | text = "Sign Up",
58 | fontSize = 50.sp,
59 | color = mytheme
60 | )
61 | Spacer(modifier = Modifier.height(10.dp))
62 |
63 | //variables
64 | var fullname by remember { mutableStateOf("") }
65 | var username by remember { mutableStateOf("") }
66 | var email by remember { mutableStateOf("") }
67 | var password by remember { mutableStateOf("") }
68 |
69 | //fullname
70 | OutlinedTextField(
71 | value = fullname,
72 | onValueChange = { fullname = it },
73 | modifier = Modifier.fillMaxWidth().padding(start = 15.dp, end = 15.dp),
74 | leadingIcon = {
75 | Icon(
76 | imageVector = Icons.Default.Person,
77 | contentDescription = "",
78 | tint = mytheme,
79 | )
80 | },
81 | label = {
82 | Text(
83 | text = "Full Name"
84 | )
85 | },
86 | colors = OutlinedTextFieldDefaults.colors(
87 | focusedBorderColor = blue,
88 | unfocusedBorderColor = mytheme,
89 | focusedLabelColor = blue,
90 | unfocusedLabelColor = mytheme,
91 | cursorColor = blue
92 | ),
93 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
94 |
95 | )
96 | //end of fullname
97 | //username
98 | OutlinedTextField(
99 | value = username,
100 | onValueChange = { username = it },
101 | modifier = Modifier.fillMaxWidth().padding(start = 15.dp, end = 15.dp),
102 | leadingIcon = {
103 | Icon(
104 | imageVector = Icons.Default.Face,
105 | contentDescription = "",
106 | tint = mytheme,
107 | )
108 | },
109 | label = {Text(text = "Username")},
110 | colors = OutlinedTextFieldDefaults.colors(
111 | focusedBorderColor = blue,
112 | unfocusedBorderColor = mytheme,
113 | focusedLabelColor = blue,
114 | unfocusedLabelColor = mytheme,
115 | cursorColor = blue
116 | ),
117 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
118 |
119 | )
120 | //end of username
121 | //email
122 | OutlinedTextField(
123 | value = email,
124 | onValueChange = { email = it },
125 | modifier = Modifier.fillMaxWidth().padding(start = 15.dp, end = 15.dp),
126 | leadingIcon = {
127 | Icon(
128 | imageVector = Icons.Default.Email,
129 | contentDescription = "",
130 | tint = mytheme,
131 | )
132 | },
133 | label = {Text(text = "Email")},
134 | colors = OutlinedTextFieldDefaults.colors(
135 | focusedBorderColor = blue,
136 | unfocusedBorderColor = mytheme,
137 | focusedLabelColor = blue,
138 | unfocusedLabelColor = mytheme,
139 | cursorColor = blue
140 | ),
141 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email)
142 |
143 | )
144 | //end of email
145 | //password
146 | OutlinedTextField(
147 | value = password,
148 | onValueChange = { password = it },
149 | modifier = Modifier.fillMaxWidth().padding(start = 15.dp, end = 15.dp),
150 | leadingIcon = {
151 | Icon(
152 | imageVector = Icons.Default.Lock,
153 | contentDescription = "",
154 | tint = mytheme,
155 | )
156 | },
157 | label = {Text(text = "Password")},
158 | colors = OutlinedTextFieldDefaults.colors(
159 | focusedBorderColor = blue,
160 | unfocusedBorderColor = mytheme,
161 | focusedLabelColor = blue,
162 | unfocusedLabelColor = mytheme,
163 | cursorColor = blue
164 | ),
165 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
166 | visualTransformation = PasswordVisualTransformation()
167 |
168 | )
169 | //end of password
170 | Spacer(modifier = Modifier.height(10.dp))
171 | //buton start
172 | Button(
173 | onClick = {},
174 | shape = RoundedCornerShape(10.dp),
175 | colors = ButtonDefaults.buttonColors(mytheme),
176 | modifier = Modifier.fillMaxWidth().padding(start = 20.dp, end = 20.dp)
177 | ) {
178 | Text(text = "Register")
179 | }
180 | //buton end
181 | }
182 | }
183 |
184 | @Preview(showBackground = true)
185 | @Composable
186 | fun FormScreenPreview(){
187 | FormScreen(navController= rememberNavController())
188 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/home/HomeScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.home
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.fillMaxSize
5 | import androidx.compose.foundation.shape.RoundedCornerShape
6 | import androidx.compose.material3.Button
7 | import androidx.compose.material3.ButtonDefaults
8 | import androidx.compose.material3.Text
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.draw.paint
12 | import androidx.compose.ui.layout.ContentScale
13 | import androidx.compose.ui.res.painterResource
14 | import androidx.compose.ui.text.font.FontStyle
15 | import androidx.compose.ui.text.font.FontWeight
16 | import androidx.compose.ui.tooling.preview.Preview
17 | import androidx.compose.ui.unit.dp
18 | import androidx.compose.ui.unit.sp
19 | import androidx.navigation.NavController
20 | import androidx.navigation.compose.rememberNavController
21 | import com.harry.sokomart.R
22 | import com.harry.sokomart.ui.theme.newcyan
23 |
24 | @Composable
25 | fun HomeScreen(navController: NavController){
26 | Column (
27 | modifier = Modifier.fillMaxSize().paint(painter = painterResource(R.drawable.img_11), contentScale = ContentScale.FillBounds)
28 | ) {
29 |
30 | Text(
31 | text = "SokoMart",
32 | fontSize = 20.sp,
33 | fontWeight = FontWeight.ExtraBold
34 | )
35 | Text(
36 | text = "An Ecommerce application",
37 | fontStyle = FontStyle.Italic
38 | )
39 | Text(
40 | text = "SokoMart is a commerce application that enables the buying and selling of goods and services over the internet, encompassing various functionalities like product browsing, shopping carts, payment processing, and order tracking",
41 | )
42 | Button(
43 | onClick = {},
44 | shape = RoundedCornerShape(10.dp),
45 | colors = ButtonDefaults.buttonColors(newcyan)
46 | ) {
47 | Text(text = "See more")
48 | }
49 | }
50 |
51 | }
52 |
53 |
54 | @Preview(showBackground = true)
55 | @Composable
56 | fun HomeScreenPreview(){
57 | HomeScreen(navController= rememberNavController())
58 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/intent/IntentScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.intent
2 |
3 | import android.content.Intent
4 | import android.provider.MediaStore
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.Spacer
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.foundation.layout.fillMaxWidth
9 | import androidx.compose.foundation.layout.height
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.foundation.shape.RoundedCornerShape
12 | import androidx.compose.material.icons.Icons
13 | import androidx.compose.material.icons.filled.Menu
14 | import androidx.compose.material.icons.filled.Settings
15 | import androidx.compose.material.icons.filled.Share
16 | import androidx.compose.material3.Button
17 | import androidx.compose.material3.ButtonDefaults
18 | import androidx.compose.material3.ExperimentalMaterial3Api
19 | import androidx.compose.material3.Icon
20 | import androidx.compose.material3.IconButton
21 | import androidx.compose.material3.Text
22 | import androidx.compose.material3.TopAppBar
23 | import androidx.compose.material3.TopAppBarDefaults
24 | import androidx.compose.runtime.Composable
25 | import androidx.compose.ui.Modifier
26 | import androidx.compose.ui.platform.LocalContext
27 | import androidx.compose.ui.tooling.preview.Preview
28 | import androidx.compose.ui.unit.dp
29 | import androidx.core.net.toUri
30 | import androidx.navigation.NavController
31 | import androidx.navigation.compose.rememberNavController
32 | import com.harry.sokomart.ui.theme.mytheme
33 | import com.harry.sokomart.ui.theme.white
34 |
35 | @OptIn(ExperimentalMaterial3Api::class)
36 | @Composable
37 | fun IntentScreen(navController: NavController){
38 | Column (
39 | modifier = Modifier.fillMaxSize()
40 | ){
41 | val mContext = LocalContext.current
42 | //TopAppBar
43 | TopAppBar(
44 | title = {
45 | Text(text = "Kai & Karo | intents")
46 | },
47 | colors = TopAppBarDefaults.topAppBarColors(
48 | containerColor = mytheme,
49 | titleContentColor = white,
50 | navigationIconContentColor = white,
51 | actionIconContentColor = white
52 | ),
53 | navigationIcon = {
54 | IconButton(onClick = {}) {
55 | Icon(imageVector = Icons.Default.Menu, contentDescription = "")
56 |
57 | }
58 | },
59 |
60 | actions = {
61 | IconButton(onClick = {}) {
62 | Icon(imageVector = Icons.Default.Share, contentDescription = "")
63 | }
64 | IconButton(onClick = {}) {
65 | Icon(imageVector = Icons.Default.Settings, contentDescription = "")
66 | }
67 | },
68 |
69 | )
70 | //End of TopAppBar
71 | Spacer(modifier = Modifier.height(10.dp))
72 |
73 |
74 | //Mpesa
75 | Button(
76 | onClick = {
77 | val simToolKitLaunchIntent =
78 | mContext.packageManager.getLaunchIntentForPackage("com.android.stk")
79 | simToolKitLaunchIntent?.let { mContext.startActivity(it) }
80 | },
81 | shape = RoundedCornerShape(10.dp),
82 | colors = ButtonDefaults.buttonColors(mytheme),
83 | modifier = Modifier.fillMaxWidth().padding(start = 20.dp, end = 20.dp)
84 | ) {
85 | Text(text = "Mpesa")
86 | }
87 |
88 | Spacer(modifier = Modifier.height(10.dp))
89 |
90 | //sms
91 | Button(
92 | onClick = {
93 | val smsIntent=Intent(Intent.ACTION_SENDTO)
94 | smsIntent.data="smsto:0797752627".toUri()
95 | smsIntent.putExtra("sms_body","Hello Glory,how was your day?")
96 | mContext.startActivity(smsIntent)
97 | },
98 | shape = RoundedCornerShape(10.dp),
99 | colors = ButtonDefaults.buttonColors(mytheme),
100 | modifier = Modifier.fillMaxWidth().padding(start = 20.dp, end = 20.dp)
101 | ) {
102 | Text(text = "SMS")
103 | }
104 |
105 | Spacer(modifier = Modifier.height(10.dp))
106 |
107 | Button(
108 | onClick = {
109 | val callIntent=Intent(Intent.ACTION_DIAL)
110 | callIntent.data="tel:0720245837".toUri()
111 | mContext.startActivity(callIntent)
112 | },
113 | shape = RoundedCornerShape(10.dp),
114 | colors = ButtonDefaults.buttonColors(mytheme),
115 | modifier = Modifier.fillMaxWidth().padding(start = 20.dp, end = 20.dp)
116 | ) {
117 | Text(text = "Call")
118 | }
119 |
120 | Spacer(modifier = Modifier.height(10.dp))
121 |
122 | //camera
123 | Button(
124 | onClick = {
125 | val cameraIntent=Intent(MediaStore.ACTION_IMAGE_CAPTURE)
126 | if (cameraIntent.resolveActivity(mContext.packageManager)!=null){
127 | mContext.startActivity(cameraIntent)
128 | }else{
129 | println("Camera app is not available")
130 | }
131 | },
132 | shape = RoundedCornerShape(10.dp),
133 | colors = ButtonDefaults.buttonColors(mytheme),
134 | modifier = Modifier.fillMaxWidth().padding(start = 20.dp, end = 20.dp)
135 | ) {
136 | Text(text = "Camera")
137 | }
138 |
139 | Spacer(modifier = Modifier.height(10.dp))
140 |
141 | //email
142 | Button(
143 | onClick = {
144 | val shareIntent = Intent(Intent.ACTION_SEND)
145 | shareIntent.type = "text/plain"
146 | shareIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf("akinyiglory2@gmail.com"))
147 | shareIntent.putExtra(Intent.EXTRA_SUBJECT, "subject")
148 | shareIntent.putExtra(Intent.EXTRA_TEXT, "Hello, this is the email body")
149 | mContext.startActivity(shareIntent)
150 | },
151 | shape = RoundedCornerShape(10.dp),
152 | colors = ButtonDefaults.buttonColors(mytheme),
153 | modifier = Modifier.fillMaxWidth().padding(start = 20.dp, end = 20.dp)
154 | ) {
155 | Text(text = "Email")
156 | }
157 | }
158 | }
159 |
160 | @Preview(showBackground = true)
161 | @Composable
162 | fun IntentScreenPreview(){
163 | IntentScreen(navController= rememberNavController())
164 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/more/MoreScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.more
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.foundation.horizontalScroll
5 | import androidx.compose.foundation.layout.Box
6 | import androidx.compose.foundation.layout.Column
7 | import androidx.compose.foundation.layout.Row
8 | import androidx.compose.foundation.layout.Spacer
9 | import androidx.compose.foundation.layout.fillMaxSize
10 | import androidx.compose.foundation.layout.fillMaxWidth
11 | import androidx.compose.foundation.layout.height
12 | import androidx.compose.foundation.layout.padding
13 | import androidx.compose.foundation.layout.width
14 | import androidx.compose.foundation.rememberScrollState
15 | import androidx.compose.foundation.shape.RoundedCornerShape
16 | import androidx.compose.material.icons.Icons
17 | import androidx.compose.material.icons.filled.Favorite
18 | import androidx.compose.material.icons.filled.Menu
19 | import androidx.compose.material.icons.filled.Settings
20 | import androidx.compose.material3.Button
21 | import androidx.compose.material3.ButtonDefaults
22 | import androidx.compose.material3.ExperimentalMaterial3Api
23 | import androidx.compose.material3.Icon
24 | import androidx.compose.material3.IconButton
25 | import androidx.compose.material3.Text
26 | import androidx.compose.material3.TopAppBar
27 | import androidx.compose.material3.TopAppBarDefaults
28 | import androidx.compose.runtime.Composable
29 | import androidx.compose.ui.Alignment
30 | import androidx.compose.ui.Modifier
31 | import androidx.compose.ui.platform.LocalContext
32 | import androidx.compose.ui.res.painterResource
33 | import androidx.compose.ui.text.font.FontWeight
34 | import androidx.compose.ui.tooling.preview.Preview
35 | import androidx.compose.ui.unit.dp
36 | import androidx.compose.ui.unit.sp
37 | import androidx.navigation.NavController
38 | import androidx.navigation.compose.rememberNavController
39 | import com.harry.sokomart.R
40 | import com.harry.sokomart.navigation.ROUT_PHOME
41 | import com.harry.sokomart.ui.theme.mytheme
42 | import com.harry.sokomart.ui.theme.white
43 |
44 | @OptIn(ExperimentalMaterial3Api::class)
45 | @Composable
46 | fun MoreScreen(navController: NavController){
47 | Column (
48 | modifier = Modifier.fillMaxSize()
49 | ){
50 | val mContext = LocalContext.current
51 | //TopAppBar
52 | TopAppBar(
53 | title = {
54 | Text(text = "Kai & Karo | Product")
55 | },
56 | colors = TopAppBarDefaults.topAppBarColors(
57 | containerColor = mytheme,
58 | titleContentColor = white,
59 | navigationIconContentColor = white,
60 | actionIconContentColor = white
61 | ),
62 | navigationIcon = {
63 | IconButton(onClick = {}) {
64 | Icon(imageVector = Icons.Default.Menu, contentDescription = "")
65 |
66 | }
67 | },
68 |
69 | actions = {
70 | IconButton(onClick = {}) {
71 | Icon(imageVector = Icons.Default.Settings, contentDescription = "")
72 | }
73 | },
74 |
75 | )
76 | //End of TopAppBar
77 | Spacer(modifier = Modifier.height(20.dp))
78 | //box start
79 | Box (
80 | modifier = Modifier.fillMaxWidth().height(250.dp)
81 |
82 | ){
83 | Text(
84 | text = "Porsche Cayenne",
85 | fontWeight = FontWeight.Bold,
86 | fontSize = 30.sp
87 | )
88 | Spacer(modifier = Modifier.height(5.dp))
89 | Image(
90 | painter = painterResource(id = R.drawable.img),
91 | contentDescription = "home",
92 | modifier = Modifier.fillMaxSize()
93 | )
94 | Icon(
95 | imageVector = Icons.Default.Favorite, contentDescription = "",
96 | modifier = Modifier.align(alignment = Alignment.TopEnd).padding(8.dp)
97 | )
98 |
99 | }
100 | //end of box
101 | Spacer(modifier = Modifier.height(20.dp))
102 | Text(
103 | text = "Porsche Cayenne",
104 | fontWeight = FontWeight.Bold,
105 | fontSize = 30.sp
106 | )
107 | Spacer(modifier = Modifier.height(20.dp))
108 | //row start
109 | Row (
110 | modifier = Modifier.padding(start = 20.dp).horizontalScroll(rememberScrollState())
111 | ){
112 | Image(
113 | painter = painterResource(id = R.drawable.img),
114 | contentDescription = "home",
115 | modifier = Modifier.fillMaxWidth(),
116 | )
117 | Image(
118 | painter = painterResource(id = R.drawable.img_6),
119 | contentDescription = "home",
120 | modifier = Modifier.fillMaxWidth(),
121 | )
122 | Image(
123 | painter = painterResource(id = R.drawable.img_7),
124 | contentDescription = "home",
125 | modifier = Modifier.fillMaxWidth(),
126 | )
127 | Image(
128 | painter = painterResource(id = R.drawable.img_8),
129 | contentDescription = "home",
130 | modifier = Modifier.fillMaxWidth(),
131 | )
132 | }
133 | //row end
134 | Button(
135 | onClick = {
136 | navController.navigate(ROUT_PHOME)
137 | },
138 | shape = RoundedCornerShape(10.dp),
139 | colors = ButtonDefaults.buttonColors(mytheme),
140 | modifier = Modifier.fillMaxWidth().padding(start = 20.dp, end = 20.dp)
141 | ) {
142 | Text(text = "Purchase Here")
143 | }
144 | Spacer(modifier = Modifier.width(10.dp))
145 | Text(
146 | text = "Reviews",
147 | fontWeight = FontWeight.Bold,
148 | fontSize = 30.sp,
149 | modifier = Modifier.padding(start = 10.dp, top = 10.dp)
150 | )
151 | //Text(
152 | // text = "This mid-size, two-row luxury crossover is unnaturally light on its feet, with chassis tuning derived from over two decades of performance-SUV supremacy.\n" +
153 | // "US86,695.00 · 4.8 · Review by Drew Dorian\n" +
154 | // "\u200ETested: 2024 Porsche... · \u200E2025 Porsche Cayenne Hybrid · \u200EView Photos · \u200E2024",
155 | //fontSize = 18.dp,
156 | // )
157 |
158 | }
159 |
160 | }
161 |
162 | @Preview(showBackground = true)
163 | @Composable
164 | fun MoreScreenPreview(){
165 | MoreScreen(navController= rememberNavController())
166 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/newscreen/NewScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.newscreen
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.horizontalScroll
6 | import androidx.compose.foundation.layout.*
7 | import androidx.compose.foundation.rememberScrollState
8 | import androidx.compose.foundation.shape.CircleShape
9 | import androidx.compose.foundation.shape.RoundedCornerShape
10 | import androidx.compose.foundation.verticalScroll
11 | import androidx.compose.material.icons.Icons
12 | import androidx.compose.material.icons.filled.*
13 | import androidx.compose.material3.*
14 | import androidx.compose.runtime.*
15 | import androidx.compose.ui.Alignment
16 | import androidx.compose.ui.Modifier
17 | import androidx.compose.ui.draw.clip
18 | import androidx.compose.ui.graphics.Color
19 | import androidx.compose.ui.graphics.vector.ImageVector
20 | import androidx.compose.ui.res.painterResource
21 | import androidx.compose.ui.text.font.FontWeight
22 | import androidx.compose.ui.tooling.preview.Preview
23 | import androidx.compose.ui.unit.dp
24 | import androidx.compose.ui.unit.sp
25 | import androidx.navigation.NavController
26 | import androidx.navigation.compose.rememberNavController
27 | import com.harry.sokomart.ui.theme.blue
28 | import com.harry.sokomart.ui.theme.green
29 | import com.harry.sokomart.ui.theme.orange
30 | import com.harry.sokomart.R
31 |
32 | @OptIn(ExperimentalMaterial3Api::class)
33 | @Composable
34 | fun AboutScreen(navController: NavController) {
35 | var selectedIndex by remember { mutableStateOf(0) }
36 |
37 | Scaffold(
38 | floatingActionButton = {
39 | FloatingActionButton(onClick = { /* TODO: Add action */ }) {
40 | Icon(Icons.Default.Add, contentDescription = "Add")
41 | }
42 | },
43 | floatingActionButtonPosition = FabPosition.Center,
44 | bottomBar = {
45 | NavigationBar(containerColor = Color.White) {
46 | NavigationBarItem(
47 | icon = { Icon(Icons.Default.Home, contentDescription = "Home") },
48 | label = { Text("Home") },
49 | selected = selectedIndex == 0,
50 | onClick = { selectedIndex = 0 }
51 | )
52 | NavigationBarItem(
53 | icon = { Icon(Icons.Default.Favorite, contentDescription = "Tasks") },
54 | label = { Text("Tasks") },
55 | selected = selectedIndex == 1,
56 | onClick = { selectedIndex = 1 }
57 | )
58 | Spacer(modifier = Modifier.weight(1f, true))
59 | NavigationBarItem(
60 | icon = { Icon(Icons.Default.Menu, contentDescription = "Tasks") },
61 | label = { Text("Tasks") },
62 | selected = selectedIndex == 2,
63 | onClick = { selectedIndex = 2 }
64 | )
65 | NavigationBarItem(
66 | icon = { Icon(Icons.Default.Person, contentDescription = "Profile") },
67 | label = { Text("Profile") },
68 | selected = selectedIndex == 3,
69 | onClick = { selectedIndex = 3 }
70 | )
71 |
72 | }
73 | },
74 | content = { padding ->
75 | Column(
76 | modifier = Modifier
77 | .padding(padding)
78 | .padding(16.dp)
79 | .fillMaxSize()
80 | .verticalScroll(rememberScrollState())
81 | ) {
82 | // Header
83 | Spacer(modifier = Modifier.height(40.dp))
84 | Row(
85 | modifier = Modifier.fillMaxWidth().padding(start = 20.dp).horizontalScroll(rememberScrollState()),
86 | horizontalArrangement = Arrangement.SpaceBetween,
87 | verticalAlignment = Alignment.CenterVertically
88 | ) {
89 | Column {
90 | Text("Hi Samantha", fontSize = 24.sp, fontWeight = FontWeight.Bold)
91 | Text("Here are your projects", color = Color.Gray)
92 | }
93 | Icon(
94 | imageVector = Icons.Default.Person,
95 | contentDescription = "Avatar",
96 | modifier = Modifier.size(48.dp)
97 | )
98 | }
99 |
100 | Spacer(modifier = Modifier.height(50.dp))
101 | Row (){
102 | Card (
103 | modifier = Modifier.width(150.dp).height(250.dp),
104 | elevation = CardDefaults.cardElevation(10.dp),
105 | colors = CardDefaults.cardColors(blue)
106 | ){
107 | Column (
108 | modifier = Modifier.fillMaxSize(),
109 | horizontalAlignment = Alignment.CenterHorizontally,
110 | verticalArrangement = Arrangement.Center,
111 | ){
112 | Image(
113 | painter = painterResource(R.drawable.img_15),
114 | contentDescription = "home",
115 | modifier = Modifier.size(100.dp).clip(shape = RoundedCornerShape(10.dp)),
116 | )
117 | Text(
118 | text = "Contact"
119 | )
120 | }
121 | }
122 | Spacer(modifier = Modifier.width(19.dp))
123 | Card (
124 | modifier = Modifier.width(150.dp).height(250.dp),
125 | elevation = CardDefaults.cardElevation(10.dp),
126 | colors = CardDefaults.cardColors(orange)
127 | ){
128 | Column (
129 | modifier = Modifier.fillMaxSize(),
130 | horizontalAlignment = Alignment.CenterHorizontally,
131 | verticalArrangement = Arrangement.Center,
132 | ){
133 | Image(
134 | painter = painterResource(R.drawable.img_16),
135 | contentDescription = "home",
136 | modifier = Modifier.size(100.dp).clip(shape = RoundedCornerShape(10.dp)),
137 | )
138 | Text(
139 | text = "Contact"
140 | )
141 | }
142 | }
143 | Spacer(modifier = Modifier.width(19.dp))
144 | Card (
145 | modifier = Modifier.width(150.dp).height(250.dp),
146 | elevation = CardDefaults.cardElevation(10.dp),
147 | colors = CardDefaults.cardColors(green)
148 | ){
149 | Column (
150 | modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()),
151 | horizontalAlignment = Alignment.CenterHorizontally,
152 | verticalArrangement = Arrangement.Center,
153 | ){
154 | Image(
155 | painter = painterResource(R.drawable.img_12),
156 | contentDescription = "home",
157 | modifier = Modifier.size(100.dp).clip(shape = RoundedCornerShape(10.dp)),
158 | )
159 | Text(
160 | text = "Contact"
161 | )
162 | }
163 | }
164 | }
165 |
166 |
167 |
168 | Spacer(modifier = Modifier.height(24.dp))
169 | Text("Personal Tasks", fontSize = 18.sp, fontWeight = FontWeight.SemiBold)
170 | Spacer(modifier = Modifier.height(12.dp))
171 |
172 | PersonalTaskCard(
173 | title = "NDA Review for website project",
174 | time = "Today • 10pm",
175 | icon = Icons.Default.Done,
176 | iconBg = Color(0xFFFDD835)
177 | )
178 | Spacer(modifier = Modifier.height(8.dp))
179 | PersonalTaskCard(
180 | title = "Email Reply for Green Project",
181 | time = "Today • 10pm",
182 | icon = Icons.Default.Email,
183 | iconBg = Color(0xFF29B6F6)
184 | )
185 | }
186 | }
187 | )
188 | }
189 |
190 | @Composable
191 | fun ProjectCard(title: String, backgroundColor: Color, icon: ImageVector) {
192 | Card(
193 | modifier = Modifier
194 | .size(width = 180.dp, height = 120.dp),
195 | shape = RoundedCornerShape(16.dp),
196 | colors = CardDefaults.cardColors(containerColor = backgroundColor)
197 | ) {
198 | Column(
199 | modifier = Modifier
200 | .padding(12.dp),
201 | verticalArrangement = Arrangement.SpaceBetween
202 | ) {
203 | Icon(icon, contentDescription = null, tint = Color.White)
204 | Text(title, color = Color.White, fontWeight = FontWeight.Bold)
205 | Text("1.2 tasks", color = Color.White.copy(alpha = 0.7f), fontSize = 12.sp)
206 | }
207 | }
208 | }
209 |
210 | @Composable
211 | fun PersonalTaskCard(title: String, time: String, icon: ImageVector, iconBg: Color) {
212 | Card(
213 | modifier = Modifier.fillMaxWidth(),
214 | shape = RoundedCornerShape(12.dp),
215 | elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
216 | ) {
217 | Row(
218 | modifier = Modifier
219 | .padding(12.dp),
220 | verticalAlignment = Alignment.CenterVertically
221 | ) {
222 | Box(
223 | modifier = Modifier
224 | .size(36.dp)
225 | .background(iconBg, shape = CircleShape),
226 | contentAlignment = Alignment.Center
227 | ) {
228 | Icon(icon, contentDescription = null, tint = Color.White)
229 | }
230 | Spacer(modifier = Modifier.width(12.dp))
231 | Column {
232 | Text(title, fontWeight = FontWeight.Bold)
233 | Text(time, fontSize = 12.sp, color = Color.Gray)
234 | }
235 | }
236 | }
237 | }
238 |
239 | @Preview(showBackground = true)
240 | @Composable
241 | fun AboutScreenPreview() {
242 | AboutScreen(navController = rememberNavController())
243 | }
244 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/paylink/createlink.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.paylink
2 |
3 | import androidx.compose.foundation.layout.*
4 | import androidx.compose.foundation.text.KeyboardOptions
5 | import androidx.compose.material3.*
6 | import androidx.compose.runtime.*
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.graphics.Color
9 | import androidx.compose.ui.text.input.KeyboardType
10 | import androidx.compose.ui.unit.dp
11 | import androidx.compose.ui.unit.sp
12 | import androidx.navigation.NavController
13 | import androidx.compose.ui.tooling.preview.Preview
14 |
15 | @Composable
16 | fun CreateLinkScreen(navController: NavController? = null) {
17 | var recipient by remember { mutableStateOf("") }
18 | var description by remember { mutableStateOf("") }
19 | var amount by remember { mutableStateOf("") }
20 |
21 | Column(
22 | modifier = Modifier
23 | .fillMaxSize()
24 | .padding(24.dp),
25 | verticalArrangement = Arrangement.spacedBy(20.dp)
26 | ) {
27 | Text(
28 | text = "Create Payment Link",
29 | style = MaterialTheme.typography.headlineSmall,
30 | color = MaterialTheme.colorScheme.primary
31 | )
32 |
33 | OutlinedTextField(
34 | value = recipient,
35 | onValueChange = { recipient = it },
36 | label = { Text("Recipient Name") },
37 | modifier = Modifier.fillMaxWidth()
38 | )
39 |
40 | OutlinedTextField(
41 | value = description,
42 | onValueChange = { description = it },
43 | label = { Text("Description") },
44 | modifier = Modifier.fillMaxWidth()
45 | )
46 |
47 | OutlinedTextField(
48 | value = amount,
49 | onValueChange = { amount = it },
50 | label = { Text("Amount") },
51 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
52 | modifier = Modifier.fillMaxWidth()
53 | )
54 |
55 | Button(
56 | onClick = {
57 | // You'd call your backend API here to create the link
58 | // Example: viewModel.createLink(...)
59 | },
60 | modifier = Modifier.fillMaxWidth(),
61 | colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF00796B))
62 | ) {
63 | Text("Generate Link", fontSize = 18.sp)
64 | }
65 | }
66 | }
67 |
68 | @Preview(showBackground = true)
69 | @Composable
70 | fun CreateLinkScreenPreview() {
71 | CreateLinkScreen()
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/paylink/linkpreview.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.paylink
2 |
3 | import android.content.Intent
4 | import android.net.Uri
5 | import androidx.compose.foundation.clickable
6 | import androidx.compose.foundation.layout.*
7 | import androidx.compose.material.icons.Icons
8 | import androidx.compose.material.icons.filled.CheckCircle
9 | import androidx.compose.material.icons.filled.Home
10 | import androidx.compose.material.icons.filled.Share
11 | import androidx.compose.material3.*
12 | import androidx.compose.runtime.Composable
13 | import androidx.compose.ui.Alignment
14 | import androidx.compose.ui.Modifier
15 | import androidx.compose.ui.graphics.Color
16 | import androidx.compose.ui.platform.LocalContext
17 | import androidx.compose.ui.text.style.TextDecoration
18 | import androidx.compose.ui.tooling.preview.Preview
19 | import androidx.compose.ui.unit.dp
20 | import androidx.compose.ui.unit.sp
21 | import androidx.navigation.NavController
22 | import androidx.navigation.compose.rememberNavController
23 |
24 | @Composable
25 | fun LinkSuccessScreen(
26 | navController: NavController,
27 | recipientName: String,
28 | description: String,
29 | amount: String,
30 | linkUrl: String
31 | ) {
32 | val context = LocalContext.current
33 |
34 | Column(
35 | modifier = Modifier
36 | .fillMaxSize()
37 | .padding(24.dp),
38 | verticalArrangement = Arrangement.Center,
39 | horizontalAlignment = Alignment.CenterHorizontally
40 | ) {
41 | Icon(
42 | imageVector = Icons.Default.CheckCircle,
43 | contentDescription = "Success",
44 | tint = Color(0xFF4CAF50),
45 | modifier = Modifier.size(80.dp)
46 | )
47 |
48 | Spacer(modifier = Modifier.height(16.dp))
49 |
50 | Text(
51 | text = "Link Created Successfully!",
52 | fontSize = 22.sp,
53 | style = MaterialTheme.typography.headlineSmall
54 | )
55 |
56 | Spacer(modifier = Modifier.height(16.dp))
57 |
58 | Card(
59 | modifier = Modifier.fillMaxWidth(),
60 | colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
61 | ) {
62 | Column(modifier = Modifier.padding(16.dp)) {
63 | Text("Recipient: $recipientName", fontSize = 16.sp)
64 | Text("Description: $description", fontSize = 16.sp)
65 | Text("Amount: $amount", fontSize = 16.sp)
66 |
67 | Spacer(modifier = Modifier.height(8.dp))
68 |
69 | Text(
70 | text = linkUrl,
71 | color = MaterialTheme.colorScheme.primary,
72 | textDecoration = TextDecoration.Underline,
73 | modifier = Modifier.clickable {
74 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse(linkUrl))
75 | context.startActivity(intent)
76 | }
77 | )
78 | }
79 | }
80 |
81 | Spacer(modifier = Modifier.height(24.dp))
82 |
83 | Button(
84 | onClick = {
85 | // TODO: Add share functionality
86 | },
87 | modifier = Modifier.fillMaxWidth()
88 | ) {
89 | Icon(Icons.Default.Share, contentDescription = null)
90 | Spacer(modifier = Modifier.width(8.dp))
91 | Text("Share Link")
92 | }
93 |
94 | Spacer(modifier = Modifier.height(12.dp))
95 |
96 | OutlinedButton(
97 | onClick = {
98 | // navController.navigate("home") or popBackStack
99 | },
100 | modifier = Modifier.fillMaxWidth()
101 | ) {
102 | Icon(Icons.Default.Home, contentDescription = null)
103 | Spacer(modifier = Modifier.width(8.dp))
104 | Text("Back to Home")
105 | }
106 | }
107 | }
108 |
109 | @Preview(showBackground = true)
110 | @Composable
111 | fun LinkSuccessScreenPreview() {
112 | MaterialTheme {
113 | LinkSuccessScreen(
114 | navController = rememberNavController(),
115 | recipientName = "John Doe",
116 | description = "For groceries",
117 | amount = "$25.00",
118 | linkUrl = "https://paylink.app/pay/abc123"
119 | )
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/paylinkauthentication/signin.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.paylinkauthentication
2 |
3 | import androidx.compose.foundation.layout.*
4 | import androidx.compose.foundation.text.KeyboardOptions
5 | import androidx.compose.material3.*
6 | import androidx.compose.runtime.*
7 | import androidx.compose.ui.Alignment
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.text.input.KeyboardType
10 | import androidx.compose.ui.text.input.PasswordVisualTransformation
11 | import androidx.compose.ui.tooling.preview.Preview
12 | import androidx.compose.ui.unit.dp
13 | import androidx.navigation.NavController
14 | import androidx.navigation.compose.rememberNavController
15 |
16 | @Composable
17 | fun SignInScreen(
18 | navController: NavController
19 | ) {
20 | var email by remember { mutableStateOf("") }
21 | var password by remember { mutableStateOf("") }
22 |
23 | Column(
24 | modifier = Modifier
25 | .fillMaxSize()
26 | .padding(24.dp),
27 | verticalArrangement = Arrangement.Center
28 | ) {
29 | Text("Welcome Back!", style = MaterialTheme.typography.headlineSmall)
30 | Spacer(modifier = Modifier.height(24.dp))
31 |
32 | OutlinedTextField(
33 | value = email,
34 | onValueChange = { email = it },
35 | label = { Text("Email") },
36 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
37 | modifier = Modifier.fillMaxWidth()
38 | )
39 |
40 | Spacer(modifier = Modifier.height(12.dp))
41 |
42 | OutlinedTextField(
43 | value = password,
44 | onValueChange = { password = it },
45 | label = { Text("Password") },
46 | visualTransformation = PasswordVisualTransformation(),
47 | modifier = Modifier.fillMaxWidth()
48 | )
49 |
50 | Spacer(modifier = Modifier.height(24.dp))
51 |
52 | Button(
53 | onClick = {
54 | // TODO: Call Sign In API
55 | },
56 | modifier = Modifier.fillMaxWidth()
57 | ) {
58 | Text("Sign In")
59 | }
60 |
61 | Spacer(modifier = Modifier.height(12.dp))
62 |
63 | TextButton(
64 | onClick = {
65 | // TODO: Navigate to SignUpScreen
66 | },
67 | modifier = Modifier.align(Alignment.CenterHorizontally)
68 | ) {
69 | Text("Don't have an account? Sign Up")
70 | }
71 | }
72 | }
73 |
74 | @Preview(showBackground = true)
75 | @Composable
76 | fun SignInScreenPreview() {
77 | MaterialTheme {
78 | SignInScreen(navController = rememberNavController())
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/paylinkauthentication/signup.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.foundation.layout.*
2 | import androidx.compose.foundation.text.KeyboardOptions
3 | import androidx.compose.material3.*
4 | import androidx.compose.runtime.*
5 | import androidx.compose.ui.Alignment
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.text.input.KeyboardType
8 | import androidx.compose.ui.text.input.PasswordVisualTransformation
9 | import androidx.compose.ui.tooling.preview.Preview
10 | import androidx.compose.ui.unit.dp
11 | import androidx.navigation.NavController
12 | import androidx.navigation.compose.rememberNavController
13 |
14 | @Composable
15 | fun SignUpScreen(navController: NavController) {
16 | var username by remember { mutableStateOf("") }
17 | var email by remember { mutableStateOf("") }
18 | var password by remember { mutableStateOf("") }
19 | var businessName by remember { mutableStateOf("") }
20 | var businessDesc by remember { mutableStateOf("") }
21 |
22 | Column(
23 | modifier = Modifier
24 | .fillMaxSize()
25 | .padding(24.dp),
26 | verticalArrangement = Arrangement.Center
27 | ) {
28 | Text("Create Account", style = MaterialTheme.typography.headlineSmall)
29 | Spacer(modifier = Modifier.height(24.dp))
30 |
31 | OutlinedTextField(
32 | value = username,
33 | onValueChange = { username = it },
34 | label = { Text("Username") },
35 | modifier = Modifier.fillMaxWidth()
36 | )
37 |
38 | Spacer(modifier = Modifier.height(12.dp))
39 |
40 | OutlinedTextField(
41 | value = email,
42 | onValueChange = { email = it },
43 | label = { Text("Email") },
44 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
45 | modifier = Modifier.fillMaxWidth()
46 | )
47 |
48 | Spacer(modifier = Modifier.height(12.dp))
49 |
50 | OutlinedTextField(
51 | value = password,
52 | onValueChange = { password = it },
53 | label = { Text("Password") },
54 | visualTransformation = PasswordVisualTransformation(),
55 | modifier = Modifier.fillMaxWidth()
56 | )
57 |
58 | Spacer(modifier = Modifier.height(24.dp))
59 |
60 | Text(
61 | text = "Register Your Business",
62 | style = MaterialTheme.typography.titleMedium,
63 | modifier = Modifier.padding(bottom = 8.dp)
64 | )
65 |
66 | OutlinedTextField(
67 | value = businessName,
68 | onValueChange = { businessName = it },
69 | label = { Text("Business Name") },
70 | modifier = Modifier.fillMaxWidth()
71 | )
72 |
73 | Spacer(modifier = Modifier.height(12.dp))
74 |
75 | OutlinedTextField(
76 | value = businessDesc,
77 | onValueChange = { businessDesc = it },
78 | label = { Text("Business Description") },
79 | modifier = Modifier.fillMaxWidth()
80 | )
81 |
82 | Spacer(modifier = Modifier.height(24.dp))
83 |
84 | Button(
85 | onClick = {
86 | // TODO: Submit all fields to backend
87 | },
88 | modifier = Modifier.fillMaxWidth()
89 | ) {
90 | Text("Sign Up & Register Business")
91 | }
92 |
93 | Spacer(modifier = Modifier.height(12.dp))
94 |
95 | TextButton(
96 | onClick = {
97 | // TODO: Navigate to SignInScreen
98 | },
99 | modifier = Modifier.align(Alignment.CenterHorizontally)
100 | ) {
101 | Text("Already have an account? Sign In")
102 | }
103 | }
104 | }
105 |
106 | @Preview(showBackground = true)
107 | @Composable
108 | fun SignUpScreenPreview() {
109 | MaterialTheme {
110 | SignUpScreen(navController = rememberNavController())
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/paylinkcommunity/communityscreen.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.foundation.Image
2 | import androidx.compose.foundation.clickable
3 | import androidx.compose.foundation.layout.*
4 | import androidx.compose.foundation.lazy.LazyColumn
5 | import androidx.compose.foundation.lazy.items
6 | import androidx.compose.foundation.shape.CircleShape
7 | import androidx.compose.material.icons.Icons
8 | import androidx.compose.material.icons.filled.Add
9 | import androidx.compose.material.icons.filled.Face
10 | import androidx.compose.material.icons.filled.Home
11 | import androidx.compose.material.icons.filled.Person
12 | import androidx.compose.material.icons.outlined.Share
13 | import androidx.compose.material3.*
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.runtime.remember
16 | import androidx.compose.ui.Alignment
17 | import androidx.compose.ui.Modifier
18 | import androidx.compose.ui.draw.clip
19 | import androidx.compose.ui.graphics.Color
20 | import androidx.compose.ui.unit.dp
21 | import androidx.compose.ui.unit.sp
22 | import androidx.navigation.NavController
23 | import androidx.navigation.compose.rememberNavController
24 | import androidx.compose.ui.text.font.FontWeight
25 | import androidx.compose.ui.tooling.preview.Preview
26 |
27 | data class Community(val id: String, val name: String)
28 |
29 | @Composable
30 | fun YourCommunitiesScreen(navController: NavController, username: String = "Username", communities: List) {
31 | Scaffold(
32 | bottomBar = {
33 | BottomNavBar()
34 | },
35 | containerColor = Color(0xFFF8F8F8)
36 | ) { padding ->
37 | Column(
38 | modifier = Modifier
39 | .padding(padding)
40 | .padding(16.dp)
41 | .fillMaxSize()
42 | ) {
43 | // Header
44 | Row(
45 | modifier = Modifier.fillMaxWidth(),
46 | verticalAlignment = Alignment.CenterVertically,
47 | horizontalArrangement = Arrangement.SpaceBetween
48 | ) {
49 | Row(verticalAlignment = Alignment.CenterVertically) {
50 | Surface(
51 | modifier = Modifier
52 | .size(48.dp)
53 | .clip(CircleShape),
54 | color = Color(0xFFDDDDDD)
55 | ) {
56 | // Placeholder for profile picture
57 | Icon(
58 | imageVector = Icons.Default.Person,
59 | contentDescription = "Profile",
60 | modifier = Modifier.padding(8.dp)
61 | )
62 | }
63 | Spacer(modifier = Modifier.width(12.dp))
64 | Column {
65 | Text("Hi there", fontSize = 14.sp, color = Color.Gray)
66 | Text(username, fontSize = 18.sp, fontWeight = FontWeight.SemiBold)
67 | }
68 | }
69 | IconButton(onClick = { /* Share action */ }) {
70 | Icon(Icons.Outlined.Share, contentDescription = "Share")
71 | }
72 | }
73 |
74 | Spacer(modifier = Modifier.height(24.dp))
75 |
76 | // Title
77 | Text("Your Communities", fontSize = 20.sp, fontWeight = FontWeight.Bold)
78 |
79 | Spacer(modifier = Modifier.height(12.dp))
80 |
81 | // Community List
82 | LazyColumn {
83 | items(communities) { community ->
84 | CommunityCard(community = community) {
85 | // navController.navigate("community/${community.id}")
86 | }
87 | Spacer(modifier = Modifier.height(12.dp))
88 | }
89 | }
90 | }
91 | }
92 | }
93 |
94 | @Composable
95 | fun CommunityCard(community: Community, onClick: () -> Unit) {
96 | Card(
97 | modifier = Modifier
98 | .fillMaxWidth()
99 | .clickable { onClick() },
100 | colors = CardDefaults.cardColors(containerColor = Color.White),
101 | elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
102 | ) {
103 | Row(
104 | verticalAlignment = Alignment.CenterVertically,
105 | modifier = Modifier.padding(16.dp)
106 | ) {
107 | Surface(
108 | modifier = Modifier
109 | .size(40.dp)
110 | .clip(CircleShape),
111 | color = Color(0xFFDDDDDD)
112 | ) {
113 | Icon(Icons.Default.Person, contentDescription = null, modifier = Modifier.padding(8.dp))
114 | }
115 | Spacer(modifier = Modifier.width(16.dp))
116 | Text(community.name, fontSize = 16.sp, fontWeight = FontWeight.Medium)
117 | }
118 | }
119 | }
120 |
121 | @Composable
122 | fun BottomNavBar() {
123 | NavigationBar {
124 | NavigationBarItem(
125 | icon = { Icon(Icons.Default.Home, contentDescription = "Home") },
126 | selected = true,
127 | onClick = { /* Already on Home */ }
128 | )
129 | NavigationBarItem(
130 | icon = { Icon(Icons.Default.Add, contentDescription = "Add") },
131 | selected = false,
132 | onClick = { /* Add new link */ }
133 | )
134 | NavigationBarItem(
135 | icon = { Icon(Icons.Default.Person, contentDescription = "Communities") },
136 | selected = false,
137 | onClick = { /* Navigate to Communities */ }
138 | )
139 | NavigationBarItem(
140 | icon = { Icon(Icons.Default.Face, contentDescription = "Profile") },
141 | selected = false,
142 | onClick = { /* Navigate to Profile */ }
143 | )
144 | }
145 | }
146 |
147 | @Preview(showBackground = true)
148 | @Composable
149 | fun YourCommunitiesScreenPreview() {
150 | val navController = rememberNavController()
151 | val demoCommunities = listOf(
152 | Community("1", "Community 1"),
153 | Community("2", "Community 2")
154 | )
155 | YourCommunitiesScreen(navController = navController, communities = demoCommunities)
156 | }
157 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/paylinkhome/PHome.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.foundation.layout.*
2 | import androidx.compose.foundation.shape.RoundedCornerShape
3 | import androidx.compose.material.icons.Icons
4 | import androidx.compose.material.icons.filled.*
5 | import androidx.compose.material3.*
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Alignment
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.text.font.FontWeight
10 | import androidx.compose.ui.tooling.preview.Preview
11 | import androidx.compose.ui.unit.dp
12 | import androidx.navigation.NavController
13 | import androidx.navigation.compose.rememberNavController
14 |
15 | @Composable
16 | fun PHome(navController: NavController) {
17 | Column(modifier = Modifier.fillMaxSize().padding(30.dp)) {
18 |
19 | // Top bar with profile and share icon
20 | Row(
21 | modifier = Modifier.fillMaxWidth(),
22 | horizontalArrangement = Arrangement.SpaceBetween,
23 | verticalAlignment = Alignment.CenterVertically
24 | ) {
25 | Row(verticalAlignment = Alignment.CenterVertically) {
26 | Icon(Icons.Default.AccountCircle, contentDescription = "Profile", modifier = Modifier.size(40.dp))
27 | Spacer(modifier = Modifier.width(8.dp))
28 | Column {
29 | Text("Hi there", style = MaterialTheme.typography.bodyMedium)
30 | Text("", fontWeight = FontWeight.Bold)
31 | }
32 | }
33 | Icon(Icons.Default.Share, contentDescription = "Share")
34 | }
35 |
36 | Spacer(modifier = Modifier.height(16.dp))
37 | Text("Ready to manage your links today?", style = MaterialTheme.typography.bodyLarge)
38 |
39 | Spacer(modifier = Modifier.height(16.dp))
40 |
41 | // Navigation buttons
42 | Button(
43 | onClick = { /* nav to Create Link */ },
44 | modifier = Modifier.fillMaxWidth(),
45 | shape = RoundedCornerShape(8.dp)
46 | ) {
47 | Icon(Icons.Default.Create, contentDescription = null)
48 | Spacer(modifier = Modifier.width(8.dp))
49 | Text("Create Link")
50 | }
51 |
52 | Spacer(modifier = Modifier.height(8.dp))
53 | Button(
54 | onClick = { /* nav to Your Communities */ },
55 | modifier = Modifier.fillMaxWidth(),
56 | shape = RoundedCornerShape(8.dp)
57 | ) {
58 | Icon(Icons.Default.Person, contentDescription = null)
59 | Spacer(modifier = Modifier.width(8.dp))
60 | Text("Your Communities")
61 | }
62 |
63 | Spacer(modifier = Modifier.height(8.dp))
64 | Button(
65 | onClick = { /* nav to Payment History */ },
66 | modifier = Modifier.fillMaxWidth(),
67 | shape = RoundedCornerShape(8.dp)
68 | ) {
69 | Icon(Icons.Default.Menu, contentDescription = null)
70 | Spacer(modifier = Modifier.width(8.dp))
71 | Text("Payment History")
72 | }
73 |
74 | Spacer(modifier = Modifier.height(8.dp))
75 | Button(
76 | onClick = { /* nav to User Profile */ },
77 | modifier = Modifier.fillMaxWidth(),
78 | shape = RoundedCornerShape(8.dp)
79 | ) {
80 | Icon(Icons.Default.Person, contentDescription = null)
81 | Spacer(modifier = Modifier.width(8.dp))
82 | Text("User Profile")
83 | }
84 |
85 | Spacer(modifier = Modifier.height(24.dp))
86 | Text("Your Links", style = MaterialTheme.typography.titleMedium)
87 |
88 | // Filter buttons
89 | Row(modifier = Modifier.padding(vertical = 8.dp)) {
90 | Button(onClick = { /* Show All */ }, modifier = Modifier.padding(end = 8.dp)) {
91 | Text("All")
92 | }
93 | Button(onClick = { /* Show Pending */ }, modifier = Modifier.padding(end = 8.dp)) {
94 | Text("Pending")
95 | }
96 | Button(onClick = { /* Show Completed */ }) {
97 | Text("Completed")
98 | }
99 | }
100 |
101 | // List of Link Items
102 | LinkItem("Recipient 1", "Description of Link 1", "$10.00", "Apr 14")
103 | LinkItem("Recipient 2", "Description of Link 2", "$12.50", "Apr 13")
104 | LinkItem("Recipient 3", "Description of Link 3", "$20.00", "Apr 12")
105 |
106 | Spacer(modifier = Modifier.weight(1f))
107 |
108 | // Bottom navigation
109 | NavigationBar {
110 | NavigationBarItem(
111 | icon = { Icon(Icons.Default.Home, contentDescription = "Home") },
112 | selected = true,
113 | onClick = { /* Already on Home */ }
114 | )
115 | NavigationBarItem(
116 | icon = { Icon(Icons.Default.Add, contentDescription = "Add") },
117 | selected = false,
118 | onClick = { /* Add new link */ }
119 | )
120 | NavigationBarItem(
121 | icon = { Icon(Icons.Default.Person, contentDescription = "Communities") },
122 | selected = false,
123 | onClick = { /* Navigate to Communities */ }
124 | )
125 | NavigationBarItem(
126 | icon = { Icon(Icons.Default.Face, contentDescription = "Profile") },
127 | selected = false,
128 | onClick = { /* Navigate to Profile */ }
129 | )
130 | }
131 | }
132 | }
133 |
134 | @Composable
135 | fun LinkItem(name: String, description: String, amount: String, dateCreated: String) {
136 | Card(
137 | modifier = Modifier
138 | .fillMaxWidth()
139 | .padding(vertical = 4.dp),
140 | elevation = CardDefaults.cardElevation(4.dp)
141 | ) {
142 | Column(modifier = Modifier.padding(16.dp)) {
143 | Text("Name of recipient: $name", fontWeight = FontWeight.Bold)
144 | Text("Description: $description")
145 | Text("Amount: $amount")
146 | Text("Date created: $dateCreated", style = MaterialTheme.typography.bodySmall)
147 | }
148 | }
149 | }
150 |
151 | @Preview(showBackground = true)
152 | @Composable
153 | fun PHomePreview() {
154 | MaterialTheme {
155 | PHome(navController = rememberNavController())
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/paylinkhome/scaffold.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.paylinkhome
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/paylinkhome/splash.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.paylinkhome
2 |
3 | import androidx.compose.foundation.layout.*
4 | import androidx.compose.material.icons.Icons
5 | import androidx.compose.material.icons.filled.ThumbUp
6 | import androidx.compose.material3.*
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.runtime.LaunchedEffect
9 | import androidx.compose.ui.Alignment
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.graphics.Color
12 | import androidx.compose.ui.tooling.preview.Preview
13 | import androidx.compose.ui.unit.dp
14 | import androidx.compose.ui.unit.sp
15 | import androidx.navigation.NavController
16 | import androidx.navigation.compose.rememberNavController
17 | import kotlinx.coroutines.delay
18 |
19 | @Composable
20 | fun SplashScreen(navController: NavController) {
21 | // Navigate to SignIn after 2 seconds (you can later check auth state here)
22 | LaunchedEffect(Unit) {
23 | delay(2000)
24 | navController.navigate("sign_in") {
25 | popUpTo("splash") { inclusive = true } // clear backstack
26 | }
27 | }
28 |
29 | Surface(
30 | modifier = Modifier.fillMaxSize(),
31 | color = MaterialTheme.colorScheme.primaryContainer
32 | ) {
33 | Column(
34 | modifier = Modifier.fillMaxSize().padding(32.dp),
35 | verticalArrangement = Arrangement.Center,
36 | horizontalAlignment = Alignment.CenterHorizontally
37 | ) {
38 | Icon(
39 | imageVector = Icons.Default.ThumbUp,
40 | contentDescription = "App Logo",
41 | tint = Color(0xFF4CAF50),
42 | modifier = Modifier.size(80.dp)
43 | )
44 |
45 | Spacer(modifier = Modifier.height(16.dp))
46 |
47 | Text(
48 | text = "Paylink",
49 | style = MaterialTheme.typography.headlineLarge.copy(fontSize = 32.sp)
50 | )
51 | }
52 | }
53 | }
54 |
55 | @Preview(showBackground = true)
56 | @Composable
57 | fun SplashScreenPreview() {
58 | MaterialTheme {
59 | SplashScreen(navController = rememberNavController())
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/paylinkprofile/Profile.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.paylinkprofile
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Box
6 | import androidx.compose.foundation.layout.Column
7 | import androidx.compose.foundation.layout.Row
8 | import androidx.compose.foundation.layout.Spacer
9 | import androidx.compose.foundation.layout.fillMaxSize
10 | import androidx.compose.foundation.layout.fillMaxWidth
11 | import androidx.compose.foundation.layout.height
12 | import androidx.compose.foundation.layout.padding
13 | import androidx.compose.foundation.layout.size
14 | import androidx.compose.foundation.layout.width
15 | import androidx.compose.foundation.shape.CircleShape
16 | import androidx.compose.foundation.shape.RoundedCornerShape
17 | import androidx.compose.material.icons.Icons
18 | import androidx.compose.material.icons.filled.ArrowBack
19 | import androidx.compose.material.icons.filled.ExitToApp
20 | import androidx.compose.material.icons.filled.Home
21 | import androidx.compose.material.icons.filled.Settings
22 | import androidx.compose.material3.Button
23 | import androidx.compose.material3.ButtonDefaults
24 | import androidx.compose.material3.Icon
25 | import androidx.compose.material3.IconButton
26 | import androidx.compose.material3.MaterialTheme
27 | import androidx.compose.material3.Text
28 | import androidx.compose.runtime.Composable
29 | import androidx.compose.ui.Alignment
30 | import androidx.compose.ui.Modifier
31 | import androidx.compose.ui.text.font.FontWeight
32 | import androidx.compose.ui.text.style.TextAlign
33 | import androidx.compose.ui.tooling.preview.Preview
34 | import androidx.compose.ui.unit.dp
35 | import androidx.compose.ui.unit.sp
36 | import androidx.navigation.NavController
37 | import androidx.navigation.compose.rememberNavController
38 | import com.harry.sokomart.ui.theme.gray
39 | import com.harry.sokomart.ui.theme.white
40 |
41 | @Composable
42 | fun ProfileScreen(navController: NavController) {
43 | Column(modifier = Modifier
44 | .fillMaxSize()
45 | .padding(16.dp)) {
46 |
47 | // Top bar
48 | Row(
49 | modifier = Modifier.fillMaxWidth(),
50 | horizontalArrangement = Arrangement.SpaceBetween,
51 | verticalAlignment = Alignment.CenterVertically
52 | ) {
53 | IconButton(onClick = { navController.popBackStack() }) {
54 | Icon(Icons.Default.ArrowBack, contentDescription = "Back")
55 | }
56 | Text(
57 | "Profile",
58 | )
59 | IconButton(onClick = { /* Settings */ }) {
60 | Icon(Icons.Default.Settings, contentDescription = "Settings")
61 | }
62 | }
63 |
64 | Spacer(modifier = Modifier.height(16.dp))
65 |
66 | // Profile image
67 | Box(
68 | modifier = Modifier
69 | .size(100.dp)
70 | .align(Alignment.CenterHorizontally)
71 | .background(gray, CircleShape)
72 | )
73 |
74 | Spacer(modifier = Modifier.height(16.dp))
75 |
76 | // User info
77 | Column(
78 | horizontalAlignment = Alignment.CenterHorizontally,
79 | verticalArrangement = Arrangement.Center
80 | ) {
81 | Text("Username", style = MaterialTheme.typography.bodyMedium)
82 | Text("Business Name", style = MaterialTheme.typography.bodyMedium)
83 | Text("email@example.com", style = MaterialTheme.typography.bodyMedium)
84 | Text("+1234567890", style = MaterialTheme.typography.bodyMedium)
85 | }
86 |
87 | Spacer(modifier = Modifier.height(30.dp))
88 |
89 | // Stats row
90 | Spacer(modifier = Modifier.width(100.dp))
91 | Row(
92 | modifier = Modifier.fillMaxWidth(),
93 | horizontalArrangement = Arrangement.SpaceEvenly
94 | ) {
95 | StatItem("5", "Links Created")
96 | StatItem("2", "Pending")
97 | StatItem("3", "Completed")
98 | StatItem("2", "Communities\nJoined")
99 | }
100 |
101 | Spacer(modifier = Modifier.height(24.dp))
102 |
103 | // Navigation buttons
104 | Button(
105 | onClick = { navController.navigate("home") },
106 | modifier = Modifier.fillMaxWidth()
107 | ) {
108 | Icon(Icons.Default.Home, contentDescription = null)
109 | Spacer(Modifier.width(8.dp))
110 | Text("Home Screen")
111 | }
112 |
113 | Spacer(modifier = Modifier.height(16.dp))
114 |
115 | Button(
116 | onClick = { /* Handle logout */ },
117 | modifier = Modifier.fillMaxWidth(),
118 | colors = ButtonDefaults.buttonColors()
119 | ) {
120 | Icon(Icons.Default.ExitToApp, contentDescription = null)
121 | Spacer(Modifier.width(8.dp))
122 | Text(text = "Log Out", color = white)
123 | }
124 | }
125 | }
126 |
127 | @Composable
128 | fun StatItem(value: String, label: String) {
129 | Column(horizontalAlignment = Alignment.CenterHorizontally) {
130 | Box(
131 | modifier = Modifier
132 | .size(40.dp)
133 | .background(MaterialTheme.colorScheme.primary, shape = RoundedCornerShape(8.dp)),
134 | contentAlignment = Alignment.Center
135 | ) {
136 | Text(value, color = white, fontWeight = FontWeight.Bold)
137 | }
138 | Spacer(modifier = Modifier.height(4.dp))
139 | Text(label, textAlign = TextAlign.Center, fontSize = 12.sp)
140 | }
141 | }
142 |
143 | @Preview(showBackground = true)
144 | @Composable
145 | fun ProfilePreview() {
146 | ProfileScreen(navController = rememberNavController())
147 | }
148 |
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/products/AddProductScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.products
2 |
3 |
4 | import android.net.Uri
5 | import android.util.Log
6 | import androidx.activity.compose.rememberLauncherForActivityResult
7 | import androidx.activity.result.contract.ActivityResultContracts
8 | import androidx.compose.foundation.Image
9 | import androidx.compose.foundation.background
10 | import androidx.compose.foundation.clickable
11 | import androidx.compose.foundation.layout.*
12 | import androidx.compose.foundation.shape.RoundedCornerShape
13 | import androidx.compose.material.icons.Icons
14 | import androidx.compose.material.icons.filled.*
15 | import androidx.compose.material3.*
16 | import androidx.compose.runtime.*
17 | import androidx.compose.ui.Alignment
18 | import androidx.compose.ui.Modifier
19 | import androidx.compose.ui.graphics.Color
20 | import androidx.compose.ui.layout.ContentScale
21 | import androidx.compose.ui.platform.LocalContext
22 | import androidx.compose.ui.text.font.FontWeight
23 | import androidx.compose.ui.unit.dp
24 | import androidx.compose.ui.unit.sp
25 | import androidx.navigation.NavController
26 | import coil.compose.rememberAsyncImagePainter
27 | import com.harry.sokomart.navigation.ROUT_ADD_PRODUCT
28 | import com.harry.sokomart.navigation.ROUT_PRODUCT_LIST
29 | import com.harry.sokomart.viewmodel.ProductViewModel
30 |
31 | @OptIn(ExperimentalMaterial3Api::class)
32 | @Composable
33 | fun AddProductScreen(navController: NavController, viewModel: ProductViewModel) {
34 | var name by remember { mutableStateOf("") }
35 | var price by remember { mutableStateOf("") }
36 | var phone by remember { mutableStateOf("") }
37 | var imageUri by remember { mutableStateOf(null) }
38 | var showMenu by remember { mutableStateOf(false) }
39 |
40 | val context = LocalContext.current
41 |
42 | // Image Picker Launcher
43 | val imagePicker = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
44 | uri?.let {
45 | imageUri = it
46 | Log.d("ImagePicker", "Selected image URI: $it")
47 | }
48 | }
49 |
50 | Scaffold(
51 | topBar = {
52 | TopAppBar(
53 | title = { Text("Add Product", fontSize = 20.sp, fontWeight = FontWeight.Bold) },
54 | colors = TopAppBarDefaults.mediumTopAppBarColors(Color.LightGray),
55 | navigationIcon = {
56 | IconButton(onClick = { navController.popBackStack() }) {
57 | Icon(imageVector = Icons.Default.KeyboardArrowLeft, contentDescription = "Back")
58 | }
59 | },
60 | actions = {
61 | IconButton(onClick = { showMenu = true }) {
62 | Icon(imageVector = Icons.Default.MoreVert, contentDescription = "Menu")
63 | }
64 | DropdownMenu(
65 | expanded = showMenu,
66 | onDismissRequest = { showMenu = false }
67 | ) {
68 | DropdownMenuItem(
69 | text = { Text("Product List") },
70 | onClick = {
71 | navController.navigate(ROUT_PRODUCT_LIST)
72 | showMenu = false
73 | }
74 | )
75 | DropdownMenuItem(
76 | text = { Text("Add Product") },
77 | onClick = {
78 | navController.navigate(ROUT_ADD_PRODUCT)
79 | showMenu = false
80 | }
81 | )
82 | }
83 | }
84 | )
85 | },
86 | bottomBar = {
87 | BottomNavigationBar(navController)
88 | },
89 | content = { paddingValues ->
90 | Column(
91 | modifier = Modifier
92 | .fillMaxSize()
93 | .padding(paddingValues)
94 | .padding(16.dp),
95 | horizontalAlignment = Alignment.CenterHorizontally
96 | ) {
97 | // Product Name
98 | OutlinedTextField(
99 | value = name,
100 | onValueChange = { name = it },
101 | label = { Text("Product Name") },
102 | //leadingIcon = { Icon(painter = painterResource(R.drawable.product), contentDescription = "Name") },
103 | modifier = Modifier.fillMaxWidth()
104 | )
105 |
106 | Spacer(modifier = Modifier.height(10.dp))
107 |
108 | // Product Price
109 | OutlinedTextField(
110 | value = price,
111 | onValueChange = { price = it },
112 | label = { Text("Product Price") },
113 | // leadingIcon = { Icon(painter = painterResource(R.drawable.price), contentDescription = "Price") },
114 | modifier = Modifier.fillMaxWidth()
115 | )
116 |
117 |
118 |
119 | Spacer(modifier = Modifier.height(10.dp))
120 |
121 | // Phone Number
122 | OutlinedTextField(
123 | value = phone,
124 | onValueChange = { phone = it },
125 | label = { Text("Phone Number") },
126 | //leadingIcon = { Icon(painter = painterResource(R.drawable.phone), contentDescription = "Price") },
127 | modifier = Modifier.fillMaxWidth()
128 | )
129 |
130 |
131 | Spacer(modifier = Modifier.height(16.dp))
132 |
133 | // Image Picker Box
134 | Box(
135 | modifier = Modifier
136 | .size(200.dp)
137 | .background(Color.LightGray, shape = RoundedCornerShape(10.dp))
138 | .clickable { imagePicker.launch("image/*") },
139 | contentAlignment = Alignment.Center
140 | ) {
141 | if (imageUri != null) {
142 | Image(
143 | painter = rememberAsyncImagePainter(model = imageUri),
144 | contentDescription = "Selected Image",
145 | modifier = Modifier.fillMaxSize(),
146 | contentScale = ContentScale.Crop
147 | )
148 | } else {
149 | Column(horizontalAlignment = Alignment.CenterHorizontally) {
150 | //Icon(painter = painterResource(R.drawable.image), contentDescription = "Pick Image")
151 | Text("Tap to pick image", color = Color.DarkGray)
152 | }
153 | }
154 | }
155 |
156 | Spacer(modifier = Modifier.height(20.dp))
157 |
158 | // Add Product Button
159 | Button(
160 | onClick = {
161 | val priceValue = price.toDoubleOrNull()
162 | if (priceValue != null) {
163 | imageUri?.toString()?.let { viewModel.addProduct(name, priceValue, phone,it) }
164 | navController.popBackStack()
165 | }
166 | },
167 | modifier = Modifier.fillMaxWidth(),
168 | shape = RoundedCornerShape(8.dp),
169 | colors = ButtonDefaults.buttonColors(Color.LightGray)
170 | ) {
171 | Text("Add Product", fontSize = 18.sp, fontWeight = FontWeight.Bold)
172 | }
173 | }
174 | }
175 | )
176 | }
177 |
178 | // Bottom Navigation Bar Component
179 | @Composable
180 | fun BottomNavigationBar(navController: NavController) {
181 | NavigationBar(
182 | containerColor = Color(0xFF6F6A72),
183 | contentColor = Color.White
184 | ) {
185 | NavigationBarItem(
186 | selected = false,
187 | onClick = { navController.navigate(ROUT_PRODUCT_LIST) },
188 | icon = { Icon(Icons.Default.Home, contentDescription = "Product List") },
189 | label = { Text("Home") }
190 | )
191 | NavigationBarItem(
192 | selected = false,
193 | onClick = { navController.navigate(ROUT_ADD_PRODUCT) },
194 | icon = { Icon(Icons.Default.AddCircle, contentDescription = "Add Product") },
195 | label = { Text("Add") }
196 | )
197 |
198 |
199 |
200 | }
201 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/products/EditProductScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.products
2 |
3 | import android.net.Uri
4 | import android.widget.Toast
5 | import androidx.activity.compose.rememberLauncherForActivityResult
6 | import androidx.activity.result.contract.ActivityResultContracts
7 | import androidx.compose.foundation.Image
8 | import androidx.compose.foundation.layout.*
9 | import androidx.compose.foundation.text.KeyboardOptions
10 | import androidx.compose.material.icons.Icons
11 | import androidx.compose.material.icons.filled.ArrowBack
12 | import androidx.compose.material.icons.filled.Menu
13 | import androidx.compose.material.icons.filled.MoreVert
14 | import androidx.compose.material3.*
15 | import androidx.compose.runtime.*
16 | import androidx.compose.ui.Alignment
17 | import androidx.compose.ui.Modifier
18 | import androidx.compose.ui.graphics.Color
19 | import androidx.compose.ui.platform.LocalContext
20 | import androidx.compose.ui.text.input.KeyboardType
21 | import androidx.compose.runtime.livedata.observeAsState
22 | import androidx.compose.ui.unit.dp
23 | import androidx.navigation.NavController
24 | import coil.compose.rememberAsyncImagePainter
25 | import com.harry.sokomart.navigation.ROUT_ADD_PRODUCT
26 | import com.harry.sokomart.navigation.ROUT_PRODUCT_LIST
27 | import com.harry.sokomart.viewmodel.ProductViewModel
28 |
29 | @OptIn(ExperimentalMaterial3Api::class)
30 | @Composable
31 | fun EditProductScreen(productId: Int?, navController: NavController, viewModel: ProductViewModel) {
32 | val context = LocalContext.current
33 | val productList by viewModel.allProducts.observeAsState(emptyList())
34 |
35 | // Ensure productId is valid
36 | val product = remember(productList) { productList.find { it.id == productId } }
37 |
38 | // Track state variables only when product is found
39 | var name by remember { mutableStateOf(product?.name ?: "") }
40 | var price by remember { mutableStateOf(product?.price?.toString() ?: "") }
41 | var imagePath by remember { mutableStateOf(product?.imagePath ?: "") }
42 | var showMenu by remember { mutableStateOf(false) }
43 |
44 | // Image picker
45 | val imagePicker = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
46 | uri?.let {
47 | imagePath = it.toString()
48 | Toast.makeText(context, "Image Selected!", Toast.LENGTH_SHORT).show()
49 | }
50 | }
51 |
52 | Scaffold(
53 | topBar = {
54 | TopAppBar(
55 | title = { Text("Edit Product") },
56 | navigationIcon = {
57 | IconButton(onClick = { navController.popBackStack() }) {
58 | Icon(Icons.Default.ArrowBack, contentDescription = "Back")
59 | }
60 | },
61 | actions = {
62 | IconButton(onClick = { showMenu = true }) {
63 | Icon(Icons.Default.MoreVert, contentDescription = "Menu")
64 | }
65 | DropdownMenu(
66 | expanded = showMenu,
67 | onDismissRequest = { showMenu = false }
68 | ) {
69 | DropdownMenuItem(
70 | text = { Text("Home") },
71 | onClick = {
72 | navController.navigate(ROUT_PRODUCT_LIST)
73 | showMenu = false
74 | }
75 | )
76 | DropdownMenuItem(
77 | text = { Text("Add Product") },
78 | onClick = {
79 | navController.navigate(ROUT_ADD_PRODUCT)
80 | showMenu = false
81 | }
82 | )
83 | }
84 | }
85 | )
86 | },
87 | bottomBar = { BottomNavigationBar2(navController) }
88 | ) { paddingValues ->
89 | Column(
90 | modifier = Modifier
91 | .fillMaxSize()
92 | .padding(paddingValues)
93 | .padding(16.dp),
94 | horizontalAlignment = Alignment.CenterHorizontally
95 | ) {
96 | if (product != null) {
97 | OutlinedTextField(
98 | value = name,
99 | onValueChange = { name = it },
100 | label = { Text("Product Name") },
101 | modifier = Modifier.fillMaxWidth()
102 | )
103 | Spacer(modifier = Modifier.height(8.dp))
104 |
105 | OutlinedTextField(
106 | value = price,
107 | onValueChange = { price = it },
108 | label = { Text("Product Price") },
109 | keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
110 | modifier = Modifier.fillMaxWidth()
111 | )
112 | Spacer(modifier = Modifier.height(8.dp))
113 |
114 | // Display Image
115 | Image(
116 | painter = rememberAsyncImagePainter(model = Uri.parse(imagePath)),
117 | contentDescription = "Product Image",
118 | modifier = Modifier
119 | .size(150.dp)
120 | .padding(8.dp)
121 | )
122 |
123 | Button(
124 | onClick = { imagePicker.launch("image/*") },
125 | modifier = Modifier.fillMaxWidth()
126 | .padding(start = 40.dp, end = 40.dp),
127 | colors = ButtonDefaults.buttonColors(Color.LightGray)
128 | ) {
129 | Text("Change Image")
130 | }
131 | Spacer(modifier = Modifier.height(16.dp))
132 |
133 | Button(
134 | onClick = {
135 | val updatedPrice = price.toDoubleOrNull()
136 | if (updatedPrice != null) {
137 | viewModel.updateProduct(product.copy(name = name, price = updatedPrice, imagePath = imagePath))
138 | Toast.makeText(context, "Product Updated!", Toast.LENGTH_SHORT).show()
139 | navController.popBackStack()
140 | } else {
141 | Toast.makeText(context, "Invalid price entered!", Toast.LENGTH_SHORT).show()
142 | }
143 | },
144 | modifier = Modifier.fillMaxWidth()
145 | .padding(start = 40.dp, end = 40.dp),
146 | colors = ButtonDefaults.buttonColors(Color.Black)
147 | ) {
148 | Text("Update Product")
149 | }
150 | } else {
151 | Text(text = "Product not found", color = MaterialTheme.colorScheme.error)
152 | Button(onClick = { navController.popBackStack() }) {
153 | Text("Go Back")
154 | }
155 | }
156 | }
157 | }
158 | }
159 |
160 | // Bottom Navigation Bar
161 | @Composable
162 | fun BottomNavigationBar2(navController: NavController) {
163 | NavigationBar(
164 | containerColor = Color(0xFF6F6A72),
165 | contentColor = Color.White
166 | ) {
167 | NavigationBarItem(
168 | selected = false,
169 | onClick = { navController.navigate(ROUT_PRODUCT_LIST) },
170 | icon = { Icon(Icons.Default.Menu, contentDescription = "Product List") },
171 | label = { Text("Products") }
172 | )
173 | NavigationBarItem(
174 | selected = false,
175 | onClick = { navController.navigate(ROUT_ADD_PRODUCT) },
176 | icon = { Icon(Icons.Default.Menu, contentDescription = "Add Product") },
177 | label = { Text("Add") }
178 | )
179 | }
180 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/products/ProductListScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.products
2 |
3 | import android.content.ContentValues
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.graphics.Bitmap
7 | import android.graphics.BitmapFactory
8 | import android.graphics.pdf.PdfDocument
9 | import android.net.Uri
10 | import android.os.Build
11 | import android.os.Environment
12 | import android.provider.MediaStore
13 | import android.widget.Toast
14 | import androidx.annotation.RequiresApi
15 | import androidx.compose.foundation.Image
16 | import androidx.compose.foundation.background
17 | import androidx.compose.foundation.clickable
18 | import androidx.compose.foundation.layout.*
19 | import androidx.compose.foundation.lazy.LazyColumn
20 | import androidx.compose.foundation.shape.RoundedCornerShape
21 | import androidx.compose.material.icons.Icons
22 | import androidx.compose.material.icons.filled.*
23 | import androidx.compose.material3.*
24 | import androidx.compose.runtime.*
25 | import androidx.compose.runtime.livedata.observeAsState
26 | import androidx.compose.ui.Alignment
27 | import androidx.compose.ui.Modifier
28 | import androidx.compose.ui.graphics.Color
29 | import androidx.compose.ui.graphics.Brush
30 | import androidx.compose.ui.graphics.painter.Painter
31 | import androidx.compose.ui.layout.ContentScale
32 | import androidx.compose.ui.platform.LocalContext
33 | import androidx.compose.ui.res.painterResource
34 | import androidx.compose.ui.text.font.FontWeight
35 | import androidx.compose.ui.unit.dp
36 | import androidx.compose.ui.unit.sp
37 | import androidx.core.net.toUri
38 | import androidx.navigation.NavController
39 | import coil.compose.rememberAsyncImagePainter
40 | import com.harry.sokomart.model.Product
41 | import com.harry.sokomart.navigation.ROUT_ADD_PRODUCT
42 | import com.harry.sokomart.navigation.ROUT_EDIT_PRODUCT
43 | import com.harry.sokomart.navigation.ROUT_PRODUCT_LIST
44 | import com.harry.sokomart.viewmodel.ProductViewModel
45 | import java.io.IOException
46 | import java.io.OutputStream
47 | import com.harry.sokomart.R
48 |
49 | @RequiresApi(Build.VERSION_CODES.Q)
50 | @OptIn(ExperimentalMaterial3Api::class)
51 | @Composable
52 | fun ProductListScreen(navController: NavController, viewModel: ProductViewModel) {
53 | val productList by viewModel.allProducts.observeAsState(emptyList())
54 | var showMenu by remember { mutableStateOf(false) }
55 | var searchQuery by remember { mutableStateOf("") }
56 |
57 | val filteredProducts = productList.filter {
58 | it.name.contains(searchQuery, ignoreCase = true)
59 | }
60 |
61 | Scaffold(
62 | topBar = {
63 | Column {
64 | TopAppBar(
65 | title = { Text("Products", fontSize = 20.sp) },
66 | colors = TopAppBarDefaults.mediumTopAppBarColors(Color.LightGray),
67 | actions = {
68 | IconButton(onClick = { showMenu = true }) {
69 | Icon(imageVector = Icons.Default.MoreVert, contentDescription = "Menu")
70 | }
71 | DropdownMenu(
72 | expanded = showMenu,
73 | onDismissRequest = { showMenu = false }
74 | ) {
75 | DropdownMenuItem(
76 | text = { Text("Product List") },
77 | onClick = {
78 | navController.navigate(ROUT_PRODUCT_LIST)
79 | showMenu = false
80 | }
81 | )
82 | DropdownMenuItem(
83 | text = { Text("Add Product") },
84 | onClick = {
85 | navController.navigate(ROUT_ADD_PRODUCT)
86 | showMenu = false
87 | }
88 | )
89 | }
90 | }
91 | )
92 |
93 |
94 | //Search Bar
95 | OutlinedTextField(
96 | value = searchQuery,
97 | onValueChange = { searchQuery = it },
98 | modifier = Modifier
99 | .fillMaxWidth()
100 | .padding(horizontal = 12.dp, vertical = 8.dp),
101 | placeholder = { Text("Search products...") },
102 | singleLine = true,
103 | leadingIcon = {
104 | Icon(
105 | imageVector = Icons.Default.Search,
106 | contentDescription = "Search",
107 | tint = Color.Gray
108 | )
109 | },
110 | colors = OutlinedTextFieldDefaults.colors(
111 | focusedBorderColor = Color.Black, // Border color when focused
112 | unfocusedBorderColor = Color.Gray, // Border color when not focused
113 | focusedTextColor = Color.Black,
114 | unfocusedTextColor = Color.DarkGray
115 | )
116 | )
117 | }
118 | },
119 | bottomBar = { BottomNavigationBar1(navController) }
120 | ) { paddingValues ->
121 | Column(
122 | modifier = Modifier
123 | .fillMaxSize()
124 | .padding(paddingValues)
125 | .padding(16.dp)
126 | ) {
127 | LazyColumn {
128 | items(filteredProducts.size) { index ->
129 | ProductItem(navController, filteredProducts[index], viewModel)
130 | }
131 | }
132 | }
133 | }
134 | }
135 |
136 | @RequiresApi(Build.VERSION_CODES.Q)
137 | @Composable
138 | fun ProductItem(navController: NavController, product: Product, viewModel: ProductViewModel) {
139 | val painter: Painter = rememberAsyncImagePainter(
140 | model = product.imagePath?.let { Uri.parse(it) } ?: Uri.EMPTY
141 | )
142 | val context = LocalContext.current
143 |
144 | Card(
145 | modifier = Modifier
146 | .fillMaxWidth()
147 | .padding(vertical = 8.dp)
148 | .clickable {
149 | if (product.id != 0) {
150 | navController.navigate(ROUT_EDIT_PRODUCT)
151 | }
152 | },
153 | shape = RoundedCornerShape(12.dp),
154 | elevation = CardDefaults.cardElevation(4.dp)
155 | ) {
156 | Box(modifier = Modifier.fillMaxWidth()) {
157 | // Product Image
158 | Image(
159 | painter = painter,
160 | contentDescription = "Product Image",
161 | modifier = Modifier
162 | .fillMaxWidth()
163 | .height(200.dp),
164 | contentScale = ContentScale.Crop
165 | )
166 |
167 | // Gradient Overlay
168 | Box(
169 | modifier = Modifier
170 | .fillMaxWidth()
171 | .height(120.dp)
172 | .align(Alignment.BottomStart)
173 | .background(
174 | Brush.verticalGradient(
175 | colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.7f))
176 | )
177 | )
178 | )
179 |
180 | // Product Info
181 | Column(
182 | modifier = Modifier
183 | .align(Alignment.BottomStart)
184 | .padding(start = 12.dp, bottom = 60.dp)
185 | ) {
186 | Text(
187 | text = product.name,
188 | fontSize = 20.sp,
189 | fontWeight = FontWeight.Bold,
190 | color = Color.White
191 | )
192 | Text(
193 | text = "Price: Ksh${product.price}",
194 | fontSize = 16.sp,
195 | color = Color.White
196 | )
197 | }
198 |
199 | // Buttons (Message, Edit, Delete, Download PDF)
200 | Box(
201 | modifier = Modifier
202 | .fillMaxWidth()
203 | .padding(8.dp)
204 | .align(Alignment.BottomEnd)
205 | ) {
206 | Row(
207 | horizontalArrangement = Arrangement.spacedBy(8.dp)
208 | ) {
209 | // Message Seller
210 | OutlinedButton(
211 | onClick = {
212 | val smsIntent = Intent(Intent.ACTION_SENDTO)
213 | smsIntent.data = "smsto:${product.phone}".toUri()
214 | smsIntent.putExtra("sms_body", "Hello Seller,...?")
215 | context.startActivity(smsIntent)
216 | },
217 | shape = RoundedCornerShape(8.dp),
218 | ) {
219 | Row {
220 | Icon(
221 | imageVector = Icons.Default.Send,
222 | contentDescription = "Message Seller"
223 | )
224 | Spacer(modifier = Modifier.width(3.dp))
225 | Text(text = "Message Seller")
226 | }
227 | }
228 |
229 | // Edit Product
230 | IconButton(
231 | onClick = {
232 | navController.navigate(ROUT_EDIT_PRODUCT)
233 | }
234 | ) {
235 | Icon(
236 | imageVector = Icons.Default.Edit,
237 | contentDescription = "Edit",
238 | tint = Color.White
239 | )
240 | }
241 |
242 | // Delete Product
243 | IconButton(
244 | onClick = { viewModel.deleteProduct(product) }
245 | ) {
246 | Icon(
247 | imageVector = Icons.Default.Delete,
248 | contentDescription = "Delete",
249 | tint = Color.White
250 | )
251 | }
252 |
253 | // Download PDF
254 | IconButton(
255 | onClick = { generateProductPDF(context, product) }
256 | ) {
257 | Icon(
258 | painter = painterResource(R.drawable.download),
259 | contentDescription = "",
260 | tint = Color.White
261 | )
262 | }
263 | }
264 | }
265 | }
266 | }
267 | }
268 |
269 | @RequiresApi(Build.VERSION_CODES.Q)
270 | fun generateProductPDF(context: Context, product: Product) {
271 | val pdfDocument = PdfDocument()
272 | val pageInfo = PdfDocument.PageInfo.Builder(300, 500, 1).create()
273 | val page = pdfDocument.startPage(pageInfo)
274 | val canvas = page.canvas
275 | val paint = android.graphics.Paint()
276 |
277 | val bitmap: Bitmap? = try {
278 | product.imagePath?.let {
279 | val uri = Uri.parse(it)
280 | context.contentResolver.openInputStream(uri)?.use { inputStream ->
281 | BitmapFactory.decodeStream(inputStream)
282 | }
283 | }
284 | } catch (e: Exception) {
285 | e.printStackTrace()
286 | null
287 | }
288 |
289 | bitmap?.let {
290 | val scaledBitmap = Bitmap.createScaledBitmap(it, 250, 150, false)
291 | canvas.drawBitmap(scaledBitmap, 25f, 20f, paint)
292 | }
293 |
294 | paint.textSize = 16f
295 | paint.isFakeBoldText = true
296 | canvas.drawText("Product Details", 80f, 200f, paint)
297 |
298 | paint.textSize = 12f
299 | paint.isFakeBoldText = false
300 | canvas.drawText("Name: ${product.name}", 50f, 230f, paint)
301 | canvas.drawText("Price: Ksh${product.price}", 50f, 250f, paint)
302 | canvas.drawText("Seller Phone: ${product.phone}", 50f, 270f, paint)
303 |
304 | pdfDocument.finishPage(page)
305 |
306 | // Save PDF using MediaStore (Scoped Storage)
307 | val fileName = "${product.name}_Details.pdf"
308 | val contentValues = ContentValues().apply {
309 | put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
310 | put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
311 | put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
312 | }
313 |
314 | val contentResolver = context.contentResolver
315 | val uri = contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
316 |
317 | if (uri != null) {
318 | try {
319 | val outputStream: OutputStream? = contentResolver.openOutputStream(uri)
320 | if (outputStream != null) {
321 | pdfDocument.writeTo(outputStream)
322 | Toast.makeText(context, "PDF saved to Downloads!", Toast.LENGTH_LONG).show()
323 | }
324 | outputStream?.close()
325 | } catch (e: IOException) {
326 | e.printStackTrace()
327 | Toast.makeText(context, "Failed to save PDF!", Toast.LENGTH_LONG).show()
328 | }
329 | } else {
330 | Toast.makeText(context, "Failed to create file!", Toast.LENGTH_LONG).show()
331 | }
332 |
333 | pdfDocument.close()
334 | }
335 |
336 | // Bottom Navigation Bar Component
337 | @Composable
338 | fun BottomNavigationBar1(navController: NavController) {
339 | NavigationBar(
340 | containerColor = Color(0xFFA2B9A2),
341 | contentColor = Color.White
342 | ) {
343 | NavigationBarItem(
344 | selected = false,
345 | onClick = { navController.navigate(ROUT_PRODUCT_LIST) },
346 | icon = { Icon(Icons.Default.Home, contentDescription = "Product List") },
347 | label = { Text("Home") }
348 | )
349 | NavigationBarItem(
350 | selected = false,
351 | onClick = { navController.navigate(ROUT_ADD_PRODUCT) },
352 | icon = { Icon(Icons.Default.AddCircle, contentDescription = "Add Product") },
353 | label = { Text("Add") }
354 | )
355 | }
356 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/service/ServiceScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.service
2 |
3 | import androidx.compose.material3.ExperimentalMaterial3Api
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.tooling.preview.Preview
6 | import androidx.navigation.NavController
7 | import androidx.navigation.compose.rememberNavController
8 |
9 | @OptIn(ExperimentalMaterial3Api::class)
10 | @Composable
11 | fun ServiceScreen(navController: NavController) {
12 |
13 |
14 | }
15 | @Preview(showBackground = true)
16 | @Composable
17 | fun ServiceScreenPreview(){
18 | ServiceScreen(navController= rememberNavController())
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/splash/SplashScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.splash
2 |
3 | import android.annotation.SuppressLint
4 | import androidx.compose.foundation.Image
5 | import androidx.compose.foundation.background
6 | import androidx.compose.foundation.layout.Arrangement
7 | import androidx.compose.foundation.layout.Column
8 | import androidx.compose.foundation.layout.fillMaxSize
9 | import androidx.compose.foundation.layout.size
10 | import androidx.compose.foundation.shape.RoundedCornerShape
11 | import androidx.compose.material3.Text
12 | import androidx.compose.runtime.Composable
13 | import androidx.compose.runtime.rememberCoroutineScope
14 | import androidx.compose.ui.Alignment
15 | import androidx.compose.ui.Modifier
16 | import androidx.compose.ui.draw.clip
17 | import androidx.compose.ui.res.painterResource
18 | import androidx.compose.ui.text.font.FontWeight
19 | import androidx.compose.ui.tooling.preview.Preview
20 | import androidx.compose.ui.unit.dp
21 | import androidx.compose.ui.unit.sp
22 | import androidx.navigation.NavController
23 | import androidx.navigation.compose.rememberNavController
24 | import com.harry.sokomart.R
25 | import com.harry.sokomart.navigation.ROUT_LOGIN
26 | import com.harry.sokomart.ui.theme.mytheme
27 | import kotlinx.coroutines.delay
28 | import kotlinx.coroutines.launch
29 |
30 | @SuppressLint("CoroutineCreationDuringComposition")
31 | @Composable
32 | fun SplashScreen(navController: NavController){
33 | val coroutine = rememberCoroutineScope()
34 | coroutine.launch {
35 | delay(2000)
36 | navController.navigate(ROUT_LOGIN)
37 | }
38 | Column (
39 | modifier = Modifier
40 | .fillMaxSize()
41 | .background(mytheme)
42 | ,
43 | horizontalAlignment = Alignment.CenterHorizontally,
44 | verticalArrangement = Arrangement.Center
45 | ){
46 | Image(
47 | painter = painterResource(R.drawable.img_14),
48 | contentDescription = "",
49 | modifier = Modifier.size(300.dp).clip(shape = RoundedCornerShape(10.dp)),
50 | )
51 | Text(
52 | text = "KAI & KARO",
53 | fontSize = 35.sp,
54 | fontWeight = FontWeight.ExtraBold
55 | )
56 | }
57 | }
58 |
59 | @Preview(showBackground = true)
60 | @Composable
61 | fun SplashScreenPreview(){
62 | SplashScreen(navController= rememberNavController())
63 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/screens/start/StartScreen.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.screens.start
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.Spacer
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.foundation.layout.fillMaxWidth
9 | import androidx.compose.foundation.layout.height
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.foundation.layout.size
12 | import androidx.compose.foundation.shape.RoundedCornerShape
13 | import androidx.compose.material3.Button
14 | import androidx.compose.material3.ButtonDefaults
15 | import androidx.compose.material3.Text
16 | import androidx.compose.runtime.Composable
17 | import androidx.compose.ui.Alignment
18 | import androidx.compose.ui.Modifier
19 | import androidx.compose.ui.draw.clip
20 | import androidx.compose.ui.res.painterResource
21 | import androidx.compose.ui.text.font.FontWeight
22 | import androidx.compose.ui.text.style.TextAlign
23 | import androidx.compose.ui.tooling.preview.Preview
24 | import androidx.compose.ui.unit.dp
25 | import androidx.compose.ui.unit.sp
26 | import androidx.navigation.NavController
27 | import androidx.navigation.compose.rememberNavController
28 | import com.harry.sokomart.R
29 | import com.harry.sokomart.navigation.ROUT_ITEM
30 | import com.harry.sokomart.ui.theme.mytheme
31 |
32 | @Composable
33 | fun StartScreen(navController: NavController){
34 | Column (
35 | modifier = Modifier.fillMaxSize(),
36 | horizontalAlignment = Alignment.CenterHorizontally,
37 | verticalArrangement = Arrangement.Center
38 | ){
39 | Text(
40 | text = "Kai & Karo",
41 | fontSize = 40.sp,
42 | color = mytheme,
43 | fontWeight = FontWeight.Bold
44 | )
45 |
46 | Image(
47 | painter = painterResource(R.drawable.img),
48 | contentDescription = "home",
49 | modifier = Modifier.size(300.dp).clip(shape = RoundedCornerShape(10.dp))
50 | )
51 | Spacer(modifier = Modifier.height(20.dp))
52 | Text(
53 | text = "Buy a Car today",
54 | fontSize = 30.sp,
55 | fontWeight = FontWeight.Bold,
56 | color = mytheme
57 | )
58 | Text(
59 | text = "commercial activities including the electronic buying or selling products and services which are conducted on online platforms or over the Internet.[1] E-commerce draws on technologies such as mobile commerce, electronic funds transfer, supply chain management, Internet marketing, online transaction processing, electronic data interchange (EDI), inventory management systems, and automated data collection systems. E-commerce is the largest sector of the electronics industry and is in turn driven by the technological advances of the semiconductor industry.",
60 | fontSize = 18.sp,
61 | textAlign = TextAlign.Center
62 | )
63 | Button(
64 | onClick = {
65 | navController.navigate(ROUT_ITEM)
66 | },
67 | shape = RoundedCornerShape(10.dp),
68 | colors = ButtonDefaults.buttonColors(mytheme),
69 | modifier = Modifier.fillMaxWidth().padding(start = 20.dp, end = 20.dp)
70 | ) {
71 | Text(text = "Get started")
72 | }
73 |
74 | }
75 | }
76 |
77 | @Preview(showBackground = true)
78 | @Composable
79 | fun StartScreenPreview(){
80 | StartScreen(navController= rememberNavController())
81 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 | val Purple80 = Color(0xFFD0BCFF)
5 | val PurpleGrey80 = Color(0xFFCCC2DC)
6 | val Pink80 = Color(0xFFEFB8C8)
7 |
8 | val Purple40 = Color(0xFF6650a4)
9 | val PurpleGrey40 = Color(0xFF625b71)
10 | val Pink40 = Color(0xFF7D5260)
11 | val newcyan = Color(0xFF00BCD4)
12 | val mytheme = Color(0xFF054B6B)
13 | val white = Color(0xFFF8F8F8)
14 | val blue = Color(0xFF071388)
15 | val orange = Color(0xFFF57F17)
16 | val green = Color(0xFF098310)
17 | val gray = Color(0xFF989A98)
18 | val lightteal = Color(0xFF0D524C)
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.theme
2 |
3 | import android.os.Build
4 | import androidx.compose.foundation.isSystemInDarkTheme
5 | import androidx.compose.material3.MaterialTheme
6 | import androidx.compose.material3.darkColorScheme
7 | import androidx.compose.material3.dynamicDarkColorScheme
8 | import androidx.compose.material3.dynamicLightColorScheme
9 | import androidx.compose.material3.lightColorScheme
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.platform.LocalContext
12 |
13 | private val DarkColorScheme = darkColorScheme(
14 | primary = Purple80,
15 | secondary = PurpleGrey80,
16 | tertiary = Pink80
17 | )
18 |
19 | private val LightColorScheme = lightColorScheme(
20 | primary = Purple40,
21 | secondary = PurpleGrey40,
22 | tertiary = Pink40
23 |
24 | /* Other default colors to override
25 | background = Color(0xFFFFFBFE),
26 | surface = Color(0xFFFFFBFE),
27 | onPrimary = Color.White,
28 | onSecondary = Color.White,
29 | onTertiary = Color.White,
30 | onBackground = Color(0xFF1C1B1F),
31 | onSurface = Color(0xFF1C1B1F),
32 | */
33 | )
34 |
35 | @Composable
36 | fun SokoMartTheme(
37 | darkTheme: Boolean = isSystemInDarkTheme(),
38 | // Dynamic color is available on Android 12+
39 | dynamicColor: Boolean = true,
40 | content: @Composable () -> Unit
41 | ) {
42 | val colorScheme = when {
43 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
44 | val context = LocalContext.current
45 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
46 | }
47 |
48 | darkTheme -> DarkColorScheme
49 | else -> LightColorScheme
50 | }
51 |
52 | MaterialTheme(
53 | colorScheme = colorScheme,
54 | typography = Typography,
55 | content = content
56 | )
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/viewmodel/AuthViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.viewmodel
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.viewModelScope
5 | import com.harry.sokomart.model.User
6 | import com.harry.sokomart.repository.UserRepository
7 | import kotlinx.coroutines.launch
8 |
9 | class AuthViewModel(private val repository: UserRepository) : ViewModel() {
10 | var loggedInUser: ((User?) -> Unit)? = null
11 |
12 | fun registerUser(user: User) {
13 | viewModelScope.launch {
14 | repository.registerUser(user)
15 | }
16 | }
17 |
18 | fun loginUser(email: String, password: String) {
19 | viewModelScope.launch {
20 | val user = repository.loginUser(email, password)
21 | loggedInUser?.invoke(user)
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/harry/sokomart/viewmodel/ProductViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart.viewmodel
2 |
3 | import android.app.Application
4 | import android.net.Uri
5 | import androidx.lifecycle.AndroidViewModel
6 | import androidx.lifecycle.LiveData
7 | import androidx.lifecycle.viewModelScope
8 | import com.harry.sokomart.data.ProductDatabase
9 | import com.harry.sokomart.model.Product
10 | import kotlinx.coroutines.Dispatchers
11 | import kotlinx.coroutines.launch
12 | import java.io.File
13 | import java.io.FileOutputStream
14 | import java.io.InputStream
15 |
16 | class ProductViewModel(app: Application) : AndroidViewModel(app) {
17 |
18 | private val context = app.applicationContext
19 | private val productDao = ProductDatabase.getDatabase(app).productDao()
20 |
21 | val allProducts: LiveData> = productDao.getAllProducts()
22 |
23 | fun addProduct(name: String, price: Double, phone: String, imageUri: String) {
24 | viewModelScope.launch(Dispatchers.IO) {
25 | val savedImagePath = saveImageToInternalStorage(Uri.parse(imageUri))
26 | val newProduct = Product(
27 | name = name,
28 | price = price,
29 | phone = phone,
30 | imagePath = savedImagePath // use saved image path
31 | )
32 | productDao.insertProduct(newProduct)
33 | }
34 | }
35 |
36 | fun updateProduct(updatedProduct: Product) {
37 | viewModelScope.launch(Dispatchers.IO) {
38 | productDao.updateProduct(updatedProduct)
39 | }
40 | }
41 |
42 | fun deleteProduct(product: Product) {
43 | viewModelScope.launch(Dispatchers.IO) {
44 | // Delete image from storage
45 | deleteImageFromInternalStorage(product.imagePath)
46 | productDao.deleteProduct(product)
47 | }
48 | }
49 |
50 | // Save image permanently to internal storage
51 | private fun saveImageToInternalStorage(uri: Uri): String {
52 | val inputStream: InputStream? = context.contentResolver.openInputStream(uri)
53 | val fileName = "IMG_${System.currentTimeMillis()}.jpg"
54 | val file = File(context.filesDir, fileName)
55 |
56 | inputStream?.use { input ->
57 | FileOutputStream(file).use { output ->
58 | input.copyTo(output)
59 | }
60 | }
61 |
62 | return file.absolutePath
63 | }
64 |
65 | private fun deleteImageFromInternalStorage(path: String) {
66 | try {
67 | val file = File(path)
68 | if (file.exists()) {
69 | file.delete()
70 | }
71 | } catch (e: Exception) {
72 | e.printStackTrace()
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/download.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
11 |
12 |
14 |
16 |
18 |
20 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_1.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_10.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_11.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_12.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_13.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_14.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_15.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_16.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_3.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_4.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_5.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_6.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_7.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_8.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/drawable/img_9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/name.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/price.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/product.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/visibility.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/visibilityoff.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #999494
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SokoMart
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/test/java/com/harry/sokomart/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.harry.sokomart
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libs.plugins.android.application) apply false
4 | alias(libs.plugins.kotlin.android) apply false
5 | alias(libs.plugins.kotlin.compose) apply false
6 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. For more details, visit
12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | agp = "8.9.1"
3 | kotlin = "2.0.21"
4 | coreKtx = "1.15.0"
5 | junit = "4.13.2"
6 | junitVersion = "1.2.1"
7 | espressoCore = "3.6.1"
8 | lifecycleRuntimeKtx = "2.8.7"
9 | activityCompose = "1.10.1"
10 | composeBom = "2024.09.00"
11 | navigationRuntimeAndroid = "2.9.0-alpha09"
12 | runtimeAndroid = "1.7.8"
13 | navigationCompose = "2.8.9"
14 |
15 | [libraries]
16 | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
17 | junit = { group = "junit", name = "junit", version.ref = "junit" }
18 | androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
19 | androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
20 | androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
21 | androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
22 | androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
23 | androidx-ui = { group = "androidx.compose.ui", name = "ui" }
24 | androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
25 | androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
26 | androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
27 | androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
28 | androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
29 | androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
30 | androidx-navigation-runtime-android = { group = "androidx.navigation", name = "navigation-runtime-android", version.ref = "navigationRuntimeAndroid" }
31 | androidx-runtime-android = { group = "androidx.compose.runtime", name = "runtime-android", version.ref = "runtimeAndroid" }
32 | androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
33 |
34 | [plugins]
35 | android-application = { id = "com.android.application", version.ref = "agp" }
36 | kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
37 | kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
38 |
39 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusercloud/mypath-android/6bcddd5dc235f6ebbf4f4c98d1c08e7eede3c936/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Apr 01 08:40:44 EAT 2025
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google {
4 | content {
5 | includeGroupByRegex("com\\.android.*")
6 | includeGroupByRegex("com\\.google.*")
7 | includeGroupByRegex("androidx.*")
8 | }
9 | }
10 | mavenCentral()
11 | gradlePluginPortal()
12 | }
13 | }
14 | dependencyResolutionManagement {
15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
16 | repositories {
17 | google()
18 | mavenCentral()
19 | }
20 | }
21 |
22 | rootProject.name = "SokoMart"
23 | include(":app")
24 |
--------------------------------------------------------------------------------