Compare commits

...

No commits in common. "main" and "Lab4" have entirely different histories.
main ... Lab4

110 changed files with 4979 additions and 36 deletions

48
.gitignore vendored
View File

@ -1,35 +1,15 @@
# ---> Android
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Log/OS Files
*.log
# Android Studio generated files and folders
captures/
.externalNativeBuild/
.cxx/
*.apk
output.json
# IntelliJ
*.iml
.idea/
misc.xml
deploymentTargetDropDown.xml
render.experimental.xml
# Keystore files
*.jks
*.keystore
# Google Services (e.g. APIs or Firebase)
google-services.json
# Android Profiling
*.hprof
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

3
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

6
.idea/compiler.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

20
.idea/gradle.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View File

@ -0,0 +1,41 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
</profile>
</component>

6
.idea/kotlinc.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.10" />
</component>
</project>

9
.idea/misc.xml Normal file
View File

@ -0,0 +1,9 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,2 +0,0 @@
# Minhasapov_PMD

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

91
app/build.gradle.kts Normal file
View File

@ -0,0 +1,91 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id ("kotlin-kapt")
id("com.google.dagger.hilt.android")
}
android {
namespace = "com.example.mobileapp"
compileSdk = 34
defaultConfig {
applicationId = "com.example.mobileapp"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
kapt {
arguments {arg("room.schemaLocation", "$projectDir/schemas")}
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.3"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("androidx.activity:activity-compose:1.8.0")
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
implementation("androidx.navigation:navigation-compose:2.7.3")
implementation ("androidx.room:room-runtime:2.5.0") // Библиотека "Room"
kapt ("androidx.room:room-compiler:2.5.0") // Кодогенератор
implementation ("androidx.room:room-ktx:2.5.0")
// lab4
implementation ("com.google.dagger:hilt-android:2.44")
kapt ("com.google.dagger:hilt-android-compiler:2.44")
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
implementation ("androidx.compose.runtime:runtime-livedata:1.0.0-beta01")
implementation ("androidx.datastore:datastore-preferences:1.0.0")
implementation ("androidx.paging:paging-runtime-ktx:3.2.1")
implementation ("androidx.paging:paging-compose:3.2.1")
implementation("androidx.room:room-paging:2.5.0")
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,24 @@
package com.example.mobileapp
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.mobileapp", appContext.packageName)
}
}

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".MobileApp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MobileApp"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.MobileApp">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,65 @@
package com.example.mobileapp
import android.app.Application
import androidx.room.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import androidx.sqlite.db.SupportSQLiteDatabase
import com.example.mobileapp.db.AppDatabase
import com.example.mobileapp.repositories.CategoryRepository
import com.example.mobileapp.repositories.OrderProductRepository
import com.example.mobileapp.repositories.OrderRepository
import com.example.mobileapp.repositories.ProductRepository
import com.example.mobileapp.repositories.RoleRepository
import com.example.mobileapp.repositories.StatusRepository
import com.example.mobileapp.repositories.UserRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideDatabase(app: Application) : AppDatabase {
return Room.databaseBuilder(
app,
AppDatabase::class.java,
AppDatabase.DB_NAME
)
.fallbackToDestructiveMigration()
.build()
}
@Provides
@Singleton
fun provideCategoryRepository(db: AppDatabase) = CategoryRepository(db.categoryDao())
@Provides
@Singleton
fun provideOrderProductRepository(db: AppDatabase) = OrderProductRepository(db.orderProductDao())
@Provides
@Singleton
fun provideOrderRepository(db: AppDatabase) = OrderRepository(db.orderDao())
@Provides
@Singleton
fun provideProductRepository(db: AppDatabase) = ProductRepository(db.productDao())
@Provides
@Singleton
fun provideRoleRepository(db: AppDatabase) = RoleRepository(db.roleDao())
@Provides
@Singleton
fun provideStatusRepository(db: AppDatabase) = StatusRepository(db.statusDao())
@Provides
@Singleton
fun provideUserRepository(db: AppDatabase) = UserRepository(db.userDao())
}

View File

@ -0,0 +1,18 @@
package com.example.mobileapp
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import com.example.mobileapp.navigation.AppNavigation
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppNavigation()
}
}
}

View File

@ -0,0 +1,11 @@
package com.example.mobileapp
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class MobileApp : Application() {
override fun onCreate() {
super.onCreate()
}
}

View File

@ -0,0 +1,23 @@
package com.example.mobileapp
import android.content.Context
import android.content.SharedPreferences
class PreferencesManager(context: Context) {
private val sharedPreferences: SharedPreferences =
context.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
fun saveData(key: String, value: String) {
val editor = sharedPreferences.edit()
editor.putString(key, value)
editor.apply()
}
fun getData(key: String, defaultValue: String): String {
return sharedPreferences.getString(key, defaultValue) ?: defaultValue
}
fun deleteData(key: String) {
sharedPreferences.edit().remove(key).apply()
}
}

View File

@ -0,0 +1,24 @@
package com.example.mobileapp.dao
import androidx.room.*
import com.example.mobileapp.models.*
import kotlinx.coroutines.flow.Flow
import androidx.paging.PagingSource
@Dao
interface CategoryDao {
@Insert
suspend fun insert(category: Category)
@Update
suspend fun update(category: Category)
@Delete
suspend fun delete(category: Category)
@Query("select * from category")
fun getAll() : Flow<List<Category>>
@Query("select * from category where category.id = :id")
fun getById(id: Int) : Flow<Category>
@Query("select * from category where category.name = :name")
fun getByName(name: String) : Flow<Category>
}

View File

@ -0,0 +1,28 @@
package com.example.mobileapp.dao
import androidx.paging.PagingSource
import androidx.room.*
import com.example.mobileapp.models.*
import kotlinx.coroutines.flow.Flow
@Dao
interface OrderDao {
@Insert
suspend fun insert(order: Order) : Long
@Update
suspend fun update(order: Order)
@Delete
suspend fun delete(order: Order)
@Query("select * from `order`")
fun getAll() : Flow<List<OrderWithProducts>>
@Query("select * from `order` where `order`.`id` =:id")
fun getById(id: Int): Flow<OrderWithProducts>
@Query("select * from `order` where (`order`.`user_id` =:userId and `order`.`status_id` != 1 and `order`.`status_id` != 4)")
fun getOrdersByUserId(userId: Int): PagingSource<Int, OrderWithProducts>
@Query("select * from `order` where `order`.`user_id` =:userId and `order`.`status_id` = 1")
fun getCartByUserId(userId: Int): Flow<OrderWithProducts?>
@Query("select * from `order` where `order`.`status_id` != 1 and `order`.`status_id` != 4")
fun getOrdersToWork(): PagingSource<Int, OrderWithProducts>
}

View File

@ -0,0 +1,15 @@
package com.example.mobileapp.dao
import androidx.room.*
import com.example.mobileapp.models.OrderProduct
@Dao
interface OrderProductDao {
@Insert
suspend fun insert(orderProduct: OrderProduct)
@Update
suspend fun update(orderProduct: OrderProduct)
@Delete
suspend fun delete(orderProduct: OrderProduct)
@Query("delete from order_product WHERE order_id = :orderId AND product_id = :productId")
suspend fun deleteByProductId(orderId: Int, productId: Int);
}

View File

@ -0,0 +1,25 @@
package com.example.mobileapp.dao
import androidx.room.*
import com.example.mobileapp.models.CategoryWithProducts
import com.example.mobileapp.models.Product
import kotlinx.coroutines.flow.Flow
import androidx.paging.PagingSource
@Dao
interface ProductDao {
@Insert
suspend fun insert(product: Product)
@Update
suspend fun update(product: Product)
@Delete
suspend fun delete(product: Product)
@Query("select * from product")
fun getAll() : Flow<List<Product>>
@Query("select * from product where product.id = :id")
fun getById(id: Int): Flow<Product>
@Query("select * from product where product.category_id = :categoryId")
fun getProductsByCategoryId(categoryId: Int) : PagingSource<Int, Product>
@Query("select * from category")
fun getCategoriesWithProducts() : PagingSource<Int, CategoryWithProducts>
}

View File

@ -0,0 +1,26 @@
package com.example.mobileapp.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.mobileapp.models.*
import kotlinx.coroutines.flow.Flow
@Dao
interface RoleDao {
@Insert
suspend fun insert(role: Role)
@Update
suspend fun update(role: Role)
@Delete
suspend fun delete(role: Role)
@Query("select * from role")
fun getAll() : Flow<List<Role>>
@Query("select * from role where role.id = :id")
fun getById(id: Int) : Flow<Role>
@Query("select * from role where role.name = :name")
fun getByName(name: String) : Flow<Role>
}

View File

@ -0,0 +1,27 @@
package com.example.mobileapp.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.mobileapp.models.Role
import com.example.mobileapp.models.Status
import kotlinx.coroutines.flow.Flow
@Dao
interface StatusDao {
@Insert
suspend fun insert(status: Status)
@Update
suspend fun update(status: Status)
@Delete
suspend fun delete(status: Status)
@Query("select * from status")
fun getAll() : Flow<List<Status>>
@Query("select * from status where status.id = :id")
fun getById(id: Int) : Flow<Status>
@Query("select * from status where status.name = :name")
fun getByName(name: String) : Flow<Status>
}

View File

@ -0,0 +1,27 @@
package com.example.mobileapp.dao
import androidx.paging.PagingSource
import androidx.room.*
import com.example.mobileapp.models.Product
import com.example.mobileapp.models.User
import kotlinx.coroutines.flow.Flow
@Dao
interface UserDao {
@Insert
suspend fun insert(user: User)
@Update
suspend fun update(user: User)
@Delete
suspend fun delete(user: User)
@Query("select * from user order by login collate nocase asc")
fun getAll() : Flow<List<User>>
@Query("select * from user order by login collate nocase asc")
fun getAllPaged() : PagingSource<Int, User>
@Query("select * from user where user.id = :id")
fun getById(id: Int): Flow<User>
@Query("select * from user where user.login = :login and user.password = :password limit 1")
fun getByLoginAndPassword(login: String, password: String): Flow<User?>
}

View File

@ -0,0 +1,37 @@
package com.example.mobileapp.db
import android.content.Context
import androidx.room.*
import androidx.sqlite.db.SupportSQLiteDatabase
import com.example.mobileapp.dao.*
import com.example.mobileapp.models.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Database(
entities =
[
Role::class,
Status::class,
Category::class,
User::class,
Order::class,
Product::class,
OrderProduct::class,
],
version = 2,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract fun roleDao() : RoleDao
abstract fun statusDao() : StatusDao
abstract fun categoryDao() : CategoryDao
abstract fun userDao() : UserDao
abstract fun productDao() : ProductDao
abstract fun orderDao() : OrderDao
abstract fun orderProductDao() : OrderProductDao
companion object {
const val DB_NAME: String = "mobileapp-db"
}
}

View File

@ -0,0 +1,22 @@
package com.example.mobileapp.models
import androidx.room.*
@Entity(tableName = "category")
data class Category(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val id: Int?,
@ColumnInfo(name = "name")
val name: String
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Category
return id == other.id
}
override fun hashCode(): Int {
return id ?: -1
}
}

View File

@ -0,0 +1,9 @@
package com.example.mobileapp.models
import androidx.room.*
data class CategoryWithProducts(
@Embedded
val category: Category,
@Relation(parentColumn = "id", entityColumn = "category_id")
val products: List<Product>
)

View File

@ -0,0 +1,31 @@
package com.example.mobileapp.models
import androidx.room.*
@Entity(
tableName = "order",
foreignKeys = [
ForeignKey(User::class, ["id"], ["user_id"])
]
)
data class Order(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val id: Int?,
@ColumnInfo(name = "user_id")
val userId: Int,
@ColumnInfo(name = "price")
var price: Int,
@ColumnInfo(name = "status_id")
var statusId: Int,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Order
return id == other.id
}
override fun hashCode(): Int {
return id ?: -1
}
}

View File

@ -0,0 +1,12 @@
package com.example.mobileapp.models
import androidx.room.*
@Entity(tableName = "order_product", primaryKeys = ["order_id", "product_id"])
data class OrderProduct(
@ColumnInfo(name = "order_id", index = true)
var orderId: Int,
@ColumnInfo(name = "product_id", index = true)
var productId: Int,
@ColumnInfo(name = "amount")
var amount: Int
)

View File

@ -0,0 +1,9 @@
package com.example.mobileapp.models
import androidx.room.*
data class OrderProductWithProduct(
@Embedded
val orderProduct: OrderProduct,
@Relation(entity = Product::class, parentColumn = "product_id", entityColumn = "id")
val product: Product
)

View File

@ -0,0 +1,9 @@
package com.example.mobileapp.models
import androidx.room.*
data class OrderWithProducts(
@Embedded
val order: Order,
@Relation(entity = OrderProduct::class, parentColumn = "id", entityColumn = "order_id")
val orderWithProducts: List<OrderProductWithProduct>
)

View File

@ -0,0 +1,26 @@
package com.example.mobileapp.models
import androidx.room.*
@Entity(tableName = "product")
data class Product(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val id: Int?,
@ColumnInfo(name = "name")
val name: String,
@ColumnInfo(name = "category_id")
val categoryId: Int,
@ColumnInfo(name = "price")
val price: Int
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Product
return id == other.id
}
override fun hashCode(): Int {
return id ?: -1
}
}

View File

@ -0,0 +1,22 @@
package com.example.mobileapp.models
import androidx.room.*
@Entity(tableName = "role")
data class Role(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val id: Int?,
@ColumnInfo(name = "name")
val name: String
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Role
return id == other.id
}
override fun hashCode(): Int {
return id ?: -1
}
}

View File

@ -0,0 +1,22 @@
package com.example.mobileapp.models
import androidx.room.*
@Entity(tableName = "status")
data class Status(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val id: Int?,
@ColumnInfo(name = "name")
val name: String
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Status
return id == other.id
}
override fun hashCode(): Int {
return id ?: -1
}
}

View File

@ -0,0 +1,26 @@
package com.example.mobileapp.models
import androidx.room.*
@Entity(tableName = "user")
data class User(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
val id: Int?,
@ColumnInfo(name = "login")
val login: String,
@ColumnInfo(name = "password")
val password: String,
@ColumnInfo(name = "role_id")
var roleId: Int
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as User
return id == other.id
}
override fun hashCode(): Int {
return id ?: -1
}
}

View File

@ -0,0 +1,144 @@
package com.example.mobileapp.navigation
import android.annotation.SuppressLint
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.example.mobileapp.PreferencesManager
import com.example.mobileapp.db.AppDatabase
import com.example.mobileapp.models.User
import com.example.mobileapp.screens.AuthorizationScreen
import com.example.mobileapp.screens.CartScreen
import com.example.mobileapp.screens.CreateProductScreen
import com.example.mobileapp.screens.EditMenuScreen
import com.example.mobileapp.screens.EditProductScreen
import com.example.mobileapp.screens.ManagmentScreen
import com.example.mobileapp.screens.MenuScreen
import com.example.mobileapp.screens.OrderScreen
import com.example.mobileapp.screens.ProfileScreen
import com.example.mobileapp.screens.RegistrationScreen
import com.example.mobileapp.screens.UsersScreen
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppNavigation(){
val navController = rememberNavController()
Scaffold(
bottomBar = {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
if (currentRoute != Screens.AuthorizationScreen.name && currentRoute != Screens.RegistrationScreen.name) {
CustomNavigationBar(navController)
}
}
){
paddingValues ->
NavHost(
navController = navController,
startDestination = Screens.AuthorizationScreen.name,
modifier = Modifier.padding(paddingValues)
){
composable(route = Screens.MenuScreen.name){ MenuScreen() }
composable(route = Screens.ProfileScreen.name){ ProfileScreen(navController) }
composable(route = Screens.CartScreen.name){ CartScreen() }
composable(route = Screens.ManagmentScreen.name){ ManagmentScreen(navController) }
composable(route = Screens.OrderScreen.name){ OrderScreen() }
composable(route = Screens.AuthorizationScreen.name){ AuthorizationScreen(navController) }
composable(route = Screens.CreateProductScreen.name){ CreateProductScreen(navController) }
composable(route = Screens.EditMenuScreen.name){ EditMenuScreen(navController) }
composable(route = Screens.RegistrationScreen.name){ RegistrationScreen(navController) }
composable(route = Screens.UsersScreen.name){ UsersScreen() }
composable(route = Screens.EditProductScreen.name + "/{productId}"){ backStackEntry ->
val productId = backStackEntry.arguments?.getString("productId")
EditProductScreen(navController, productId.toString().toInt())
}
}
}
}
@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun CustomNavigationBar(navController: NavController) {
val sharedPref = PreferencesManager(LocalContext.current)
val user_role = sharedPref.getData("userRole", "user")
val listOfNavItems = remember { mutableStateListOf<NavItem>() }
when (user_role) {
"admin" -> {
listOfNavItems.clear()
listOfNavItems.addAll(listOfAdminNavItems)
}
"worker" -> {
listOfNavItems.clear()
listOfNavItems.addAll(listOfWorkerNavItems)
}
"user" -> {
listOfNavItems.clear()
listOfNavItems.addAll(listOfUserNavItems)
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.1f)
.background(Color(212, 206, 203))
) {
listOfNavItems.forEachIndexed { index, navItem ->
OutlinedButton(
modifier = Modifier
.fillMaxHeight()
.weight(1f),
onClick = {
navController.navigate(navItem.route) {
popUpTo(navController.graph.startDestinationId)
launchSingleTop = true
}
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(
topStart = if (index == 0) 30.dp else 0.dp,
topEnd = if (index == listOfNavItems.size - 1) 30.dp else 0.dp
),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
){
Icon(
painterResource(id = navItem.icon),
contentDescription = null,
tint = Color.Black
)
}
}
}
}

View File

@ -0,0 +1,78 @@
package com.example.mobileapp.navigation
import com.example.mobileapp.R
data class NavItem(
val label: String,
val icon: Int,
val route: String
)
val listOfAdminNavItems = listOf(
NavItem(
label = "Order",
icon = R.drawable.icon_order,
route = Screens.OrderScreen.name
),
NavItem(
label = "Profile",
icon = R.drawable.icon_profile,
route = Screens.ProfileScreen.name
),
NavItem(
label = "Menu",
icon = R.drawable.icon_menu,
route = Screens.MenuScreen.name
),
NavItem(
label = "Cart",
icon = R.drawable.icon_cart,
route = Screens.CartScreen.name
),
NavItem(
label = "Managment",
icon = R.drawable.icon_managment,
route = Screens.ManagmentScreen.name
),
)
val listOfUserNavItems = listOf(
NavItem(
label = "Profile",
icon = R.drawable.icon_profile,
route = Screens.ProfileScreen.name
),
NavItem(
label = "Menu",
icon = R.drawable.icon_menu,
route = Screens.MenuScreen.name
),
NavItem(
label = "Cart",
icon = R.drawable.icon_cart,
route = Screens.CartScreen.name
),
)
val listOfWorkerNavItems = listOf(
NavItem(
label = "Order",
icon = R.drawable.icon_order,
route = Screens.OrderScreen.name
),
NavItem(
label = "Profile",
icon = R.drawable.icon_profile,
route = Screens.ProfileScreen.name
),
NavItem(
label = "Menu",
icon = R.drawable.icon_menu,
route = Screens.MenuScreen.name
),
NavItem(
label = "Cart",
icon = R.drawable.icon_cart,
route = Screens.CartScreen.name
),
)

View File

@ -0,0 +1,15 @@
package com.example.mobileapp.navigation
enum class Screens {
CartScreen,
ManagmentScreen,
MenuScreen,
OrderScreen,
ProfileScreen,
AuthorizationScreen,
CreateProductScreen,
EditMenuScreen,
EditProductScreen,
RegistrationScreen,
UsersScreen,
}

View File

@ -0,0 +1,18 @@
package com.example.mobileapp.repositories
import com.example.mobileapp.dao.CategoryDao
import com.example.mobileapp.models.Category
import javax.inject.Inject
import androidx.paging.*
import kotlinx.coroutines.flow.Flow
class CategoryRepository @Inject constructor(
private val categoryDao: CategoryDao
){
suspend fun insert(category: Category) = categoryDao.insert(category)
suspend fun update(category: Category) = categoryDao.update(category)
suspend fun delete(category: Category) = categoryDao.delete(category)
fun getAll() = categoryDao.getAll()
fun getById(id: Int) = categoryDao.getById(id)
fun getByName(name: String) = categoryDao.getByName(name)
}

View File

@ -0,0 +1,15 @@
package com.example.mobileapp.repositories
import com.example.mobileapp.dao.OrderProductDao
import com.example.mobileapp.dao.ProductDao
import com.example.mobileapp.models.OrderProduct
import com.example.mobileapp.models.Product
import javax.inject.Inject
class OrderProductRepository @Inject constructor(
private val orderProductDao: OrderProductDao
){
suspend fun insert(orderProduct: OrderProduct) = orderProductDao.insert(orderProduct)
suspend fun update(orderProduct: OrderProduct) = orderProductDao.update(orderProduct)
suspend fun delete(orderProduct: OrderProduct) = orderProductDao.delete(orderProduct)
}

View File

@ -0,0 +1,39 @@
package com.example.mobileapp.repositories
import com.example.mobileapp.dao.OrderDao
import androidx.paging.*
import com.example.mobileapp.dao.OrderProductDao
import com.example.mobileapp.models.Order
import com.example.mobileapp.models.OrderProduct
import com.example.mobileapp.models.OrderWithProducts
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class OrderRepository @Inject constructor(
private val orderDao: OrderDao
){
suspend fun insert(order: Order):Long {
return orderDao.insert(order)
}
suspend fun update(order: Order) = orderDao.update(order)
suspend fun delete(order: Order) = orderDao.delete(order)
fun getAll() = orderDao.getAll()
fun getById(id: Int) = orderDao.getById(id)
fun getOrdersByUserId(userId: Int) = Pager(
PagingConfig(
pageSize = 3,
enablePlaceholders = false
),
pagingSourceFactory = { orderDao.getOrdersByUserId(userId) }
).flow
fun getCartByUserId(userId: Int) : Flow<OrderWithProducts?> {
return orderDao.getCartByUserId(userId)
}
fun getOrdersToWork() = Pager(
PagingConfig(
pageSize = 3,
enablePlaceholders = false
),
pagingSourceFactory = { orderDao.getOrdersToWork() }
).flow
}

View File

@ -0,0 +1,37 @@
package com.example.mobileapp.repositories
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingSource
import com.example.mobileapp.dao.ProductDao
import com.example.mobileapp.dao.RoleDao
import com.example.mobileapp.models.OrderWithProducts
import com.example.mobileapp.models.Product
import com.example.mobileapp.models.Role
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class ProductRepository @Inject constructor(
private val productDao: ProductDao
){
suspend fun insert(product: Product) = productDao.insert(product)
suspend fun update(product: Product) = productDao.update(product)
suspend fun delete(product: Product) = productDao.delete(product)
fun getAll() = productDao.getAll()
fun getById(id: Int) = productDao.getById(id)
fun getProductsByCategoryId(categoryId: Int) = Pager(
PagingConfig(
pageSize = 3,
enablePlaceholders = false
),
pagingSourceFactory = { productDao.getProductsByCategoryId(categoryId) }
).flow
fun getCategoriesWithProducts() = Pager(
PagingConfig(
pageSize = 3,
enablePlaceholders = false
),
pagingSourceFactory = { productDao.getCategoriesWithProducts() }
).flow
}

View File

@ -0,0 +1,16 @@
package com.example.mobileapp.repositories
import com.example.mobileapp.dao.RoleDao
import com.example.mobileapp.models.Role
import javax.inject.Inject
class RoleRepository @Inject constructor(
private val roleDao: RoleDao
){
suspend fun insert(role: Role) = roleDao.insert(role)
suspend fun update(role:Role) = roleDao.update(role)
suspend fun delete(role:Role) = roleDao.delete(role)
fun getAll() = roleDao.getAll()
fun getById(id: Int) = roleDao.getById(id)
fun getByName(name: String) = roleDao.getByName(name)
}

View File

@ -0,0 +1,17 @@
package com.example.mobileapp.repositories
import com.example.mobileapp.dao.StatusDao
import com.example.mobileapp.models.Status
import javax.inject.Inject
class StatusRepository @Inject constructor(
private val statusDao: StatusDao
){
suspend fun insert(status: Status) = statusDao.insert(status)
suspend fun update(status: Status) = statusDao.update(status)
suspend fun delete(status: Status) = statusDao.delete(status)
fun getAll() = statusDao.getAll()
fun getById(id: Int) = statusDao.getById(id)
fun getByName(name: String) = statusDao.getByName(name)
}

View File

@ -0,0 +1,27 @@
package com.example.mobileapp.repositories
import androidx.paging.Pager
import androidx.paging.PagingConfig
import com.example.mobileapp.dao.RoleDao
import com.example.mobileapp.dao.UserDao
import com.example.mobileapp.models.Role
import com.example.mobileapp.models.User
import javax.inject.Inject
class UserRepository @Inject constructor(
private val userDao: UserDao
){
suspend fun insert(user: User) = userDao.insert(user)
suspend fun update(user: User) = userDao.update(user)
suspend fun delete(user: User) = userDao.delete(user)
fun getAll() = userDao.getAll()
fun getAllPaged() = Pager(
PagingConfig(
pageSize = 6,
enablePlaceholders = false
),
pagingSourceFactory = { userDao.getAllPaged() }
).flow
fun getById(id: Int) = userDao.getById(id)
fun getByLoginAndPassword(login: String, password: String) = userDao.getByLoginAndPassword(login, password)
}

View File

@ -0,0 +1,214 @@
package com.example.mobileapp.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import com.example.mobileapp.PreferencesManager
import com.example.mobileapp.R
import com.example.mobileapp.navigation.Screens
import com.example.mobileapp.viewmodels.AuthorizationViewModel
import com.example.mobileapp.widgets.Header
@Composable
fun AuthorizationScreen(navController: NavController) {
Column (
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Header(Color.White)
AuthorizationCard(navController)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AuthorizationCard(navController: NavController){
val sharedPref = PreferencesManager(LocalContext.current)
val login = remember { mutableStateOf(TextFieldValue("")) }
val password = remember { mutableStateOf(TextFieldValue("")) }
val viewModel = hiltViewModel<AuthorizationViewModel>()
val success = viewModel.successState.observeAsState().value
if (success == true) {
navController.navigate(Screens.MenuScreen.name)
}
if (success == false) {
AlertDialog(
title = {
Text(
text = "Ошибка авторизации",
fontWeight = FontWeight.Bold,
fontSize = 20.sp
)
},
text = {
Text(
text = "Неверный логин или пароль",
fontWeight = FontWeight.Normal,
fontSize = 16.sp
)
},
onDismissRequest = {
viewModel.calmSuccessState()
},
confirmButton = {
TextButton(
onClick = {
viewModel.calmSuccessState()
}
) {
Text("ОК")
}
}
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Card(
shape = RoundedCornerShape(30.dp),
modifier = Modifier.padding(top = 30.dp)
) {
Column (
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.background(Color(212, 206, 203))
.fillMaxWidth(0.9f)
) {
Card(
shape = RoundedCornerShape(90.dp, 90.dp, 30.dp, 30.dp),
border = BorderStroke(4.dp, Color(222,161,69)),
modifier = Modifier
.fillMaxWidth()
.padding(top = 30.dp, start = 20.dp, end = 20.dp)
){
Text(
text = "Авторизация",
color = Color.White,
fontFamily = FontFamily(Font(R.font.nunito_bold)),
fontSize = 30.sp,
modifier = Modifier
.fillMaxWidth()
.background(Color(255, 186, 83))
.padding(top = 20.dp, bottom = 20.dp),
textAlign = TextAlign.Center,
)
}
OutlinedTextField(
value = login.value,
onValueChange = {login.value = it},
placeholder = { Text(text = "Логин", fontSize = 20.sp, color = Color.LightGray) },
singleLine = true,
shape = RoundedCornerShape(30.dp, 30.dp, 30.dp, 30.dp),
textStyle = TextStyle(fontSize = 20.sp),
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.Black,
disabledTextColor = Color.Transparent,
containerColor = Color.White,
focusedBorderColor = Color.Yellow,
unfocusedBorderColor = Color.Yellow,
),
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp, start = 20.dp, end = 20.dp),
)
OutlinedTextField(
value = password.value,
onValueChange = {password.value = it},
placeholder = { Text(text = "Пароль", fontSize = 20.sp, color = Color.LightGray) },
singleLine = true,
shape = RoundedCornerShape(30.dp, 30.dp, 30.dp, 30.dp),
textStyle = TextStyle(fontSize = 20.sp),
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.Black,
disabledTextColor = Color.Transparent,
containerColor = Color.White,
focusedBorderColor = Color(183,97,48),
unfocusedBorderColor = Color(183,97,48),
),
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp, start = 20.dp, end = 20.dp),
)
Button(
onClick = {
viewModel.login(sharedPref, login.value.text, password.value.text)
},
colors = ButtonDefaults.buttonColors(Color(255,186,83), Color.White),
shape = RoundedCornerShape(30.dp, 30.dp, 30.dp, 30.dp),
border = BorderStroke(4.dp, Color(222,161,69)),
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp, start = 20.dp, end = 20.dp)
) {
Text(
text = "Войти",
color = Color.White,
fontFamily = FontFamily(Font(R.font.nunito_bold)),
fontSize = 30.sp,
textAlign = TextAlign.Center,
)
}
Button(
onClick = {
navController.navigate(Screens.RegistrationScreen.name)
},
colors = ButtonDefaults.buttonColors(Color(212,206,203)),
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp, start = 20.dp, end = 20.dp)
) {
Text(
text = "Создать аккаунт",
color = Color.White,
fontFamily = FontFamily(Font(R.font.nunito_bold)),
fontSize = 20.sp,
textAlign = TextAlign.Center,
)
}
}
}
}
}

View File

@ -0,0 +1,269 @@
package com.example.mobileapp.screens
import android.annotation.SuppressLint
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import com.example.mobileapp.PreferencesManager
import com.example.mobileapp.R
import com.example.mobileapp.models.OrderProductWithProduct
import com.example.mobileapp.models.OrderWithProducts
import com.example.mobileapp.viewmodels.CartViewModel
import com.example.mobileapp.widgets.Header
@Composable
fun CartScreen(){
Column (
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Header(Color(212, 206, 203))
CartCard()
}
}
@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun CartCard(){
val sharedPref = PreferencesManager(LocalContext.current)
val cartViewModel: CartViewModel = hiltViewModel<CartViewModel>()
cartViewModel.retrieveUser(sharedPref)
val cart: OrderWithProducts? = cartViewModel.cart.observeAsState().value
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(212, 206, 203)),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(0.9f)
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Корзина",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
textAlign = TextAlign.Center,
)
if (cart != null && cart.orderWithProducts.size != 0) {
LazyColumn(
modifier = Modifier
.fillMaxWidth()
){
items(cart.orderWithProducts.size) { index ->
CartProductCard(cartViewModel, cart.orderWithProducts[index])
}
}
Card(
shape = RoundedCornerShape(20.dp),
border = BorderStroke(3.dp, Color(222,161,69)),
modifier = Modifier
.padding(top = 15.dp, start = 5.dp, end = 5.dp)
){
Row(
modifier = Modifier
.fillMaxWidth()
.background(Color.White),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
){
Text(
text = "Итого: " + cart.order.price.toString() + " р.",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 20.sp,
textAlign = TextAlign.Center,
modifier = Modifier.padding(start = 5.dp),
)
OutlinedButton(
onClick = {
cartViewModel.makeOrder()
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
modifier = Modifier.padding(5.dp)
) {
Text(text = "Заказать", fontSize = 20.sp, color = Color.White)
}
}
}
}
}
}
}
@Composable
fun CartProductCard(cartViewModel: CartViewModel, product: OrderProductWithProduct){
Card(
shape = RoundedCornerShape(20.dp),
border = BorderStroke(3.dp, Color(222,161,69)),
modifier = Modifier
.fillMaxSize()
.padding(top = 15.dp, start = 5.dp, end = 5.dp)
){
Row(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly,
){
CartProductImageCard(product.product.categoryId)
Column (
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceAround
) {
Text(
text = product.product.name,
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 17.sp,
textAlign = TextAlign.Center,
)
Text(
text = product.product.price.toString() + " р.",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 17.sp,
textAlign = TextAlign.Center,
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth(0.8f)
){
OutlinedButton(
modifier = Modifier
.fillMaxHeight(),
onClick = {
cartViewModel.removeProductFromOrder(product.product.id!!)
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(topStart = 30.dp, bottomStart = 30.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
) {
Text(text = "-", fontSize = 20.sp, color = Color.Black)
}
Card(
modifier = Modifier
.fillMaxHeight(),
border = BorderStroke(3.dp, Color(222,161,69)),
shape = RoundedCornerShape(0.dp),
){
Text(
modifier = Modifier
.background(Color.White)
.fillMaxHeight()
.padding(
start = 20.dp,
end = 20.dp,
top = 7.dp,
bottom = 7.dp
),
text = product.orderProduct.amount.toString(),
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_bold)),
fontSize = 20.sp,
textAlign = TextAlign.Center,
)
}
OutlinedButton(
modifier = Modifier
.fillMaxHeight(),
onClick = {
cartViewModel.addProductToOrder(product.product.id!!)
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
) {
Text(text = "+", fontSize = 20.sp, color = Color.Black)
}
}
}
}
}
}
@Composable
fun CartProductImageCard(categoryId : Int){
if (categoryId == 1){
Image(
painterResource(
id = R.drawable.burger
),
contentDescription = null,
modifier = Modifier
.size(100.dp, 100.dp)
.padding(5.dp),
)
}
if (categoryId == 2){
Image(
painterResource(
id = R.drawable.potato
),
contentDescription = null,
modifier = Modifier
.size(100.dp, 100.dp)
.padding(5.dp),
)
}
if (categoryId == 3){
Image(
painterResource(
id = R.drawable.pepsi
),
contentDescription = null,
modifier = Modifier
.size(100.dp, 100.dp)
.padding(5.dp),
)
}
}

View File

@ -0,0 +1,214 @@
package com.example.mobileapp.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import com.example.mobileapp.R
import com.example.mobileapp.navigation.Screens
import com.example.mobileapp.viewmodels.ProductViewModel
import com.example.mobileapp.widgets.Header
@Composable
fun CreateProductScreen(navController: NavController){
Column (
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Header(Color(212, 206, 203))
CreateProductCard(navController)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CreateProductCard(navController: NavController){
val productViewModel: ProductViewModel = hiltViewModel<ProductViewModel>()
val title = remember { mutableStateOf(TextFieldValue("")) }
val price = remember { mutableStateOf(TextFieldValue("")) }
val categoryList = remember { mutableStateListOf(productViewModel.categories) }
var selectedCategory by remember { mutableStateOf("") }
var expanded by remember { mutableStateOf(false) }
val success = productViewModel.successState.observeAsState().value
if (success == true) {
navController.navigate(Screens.ManagmentScreen.name)
}
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(212, 206, 203)),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(0.9f)
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Создание блюда",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
textAlign = TextAlign.Center,
)
Card(
shape = RoundedCornerShape(20.dp),
border = BorderStroke(3.dp, Color(222,161,69)),
modifier = Modifier
.padding(top = 15.dp, start = 5.dp, end = 5.dp)
){
Column (
modifier = Modifier.background(Color.White)
) {
OutlinedTextField(
value = title.value,
onValueChange = {title.value = it},
placeholder = { Text(text = "Название", fontSize = 20.sp, color = Color.LightGray) },
singleLine = true,
shape = RoundedCornerShape(20.dp),
textStyle = TextStyle(fontSize = 20.sp),
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.Black,
disabledTextColor = Color.Transparent,
containerColor = Color.White,
focusedBorderColor = Color(222,161,69),
unfocusedBorderColor = Color(222,161,69),
),
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp, start = 20.dp, end = 20.dp),
)
Card(
shape = RoundedCornerShape(20.dp),
border = BorderStroke(1.dp, Color(222,161,69)),
modifier = Modifier.fillMaxWidth().padding(top = 20.dp, start = 20.dp, end = 20.dp),
){
MaterialTheme(
colorScheme = lightColorScheme(
surfaceVariant= Color.White,
surface = Color.White,
onSurface = Color.Gray
)
){
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = {
expanded = !expanded
},
) {
TextField(
modifier = Modifier
.menuAnchor().fillMaxWidth(),
readOnly = true,
value = selectedCategory,
onValueChange = {},
label = { Text("Категория") },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = {
expanded = false
},
) {
categoryList.first().forEach { selectionOption ->
DropdownMenuItem(
text = { Text(selectionOption) },
onClick = {
selectedCategory = selectionOption
expanded = false
},
contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
)
}
}
}
}
}
OutlinedTextField(
value = price.value,
onValueChange = {price.value = it},
placeholder = { Text(text = "Цена", fontSize = 20.sp, color = Color.LightGray) },
singleLine = true,
shape = RoundedCornerShape(20.dp),
textStyle = TextStyle(fontSize = 20.sp),
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.Black,
disabledTextColor = Color.Transparent,
containerColor = Color.White,
focusedBorderColor = Color(222,161,69),
unfocusedBorderColor = Color(222,161,69),
),
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp, start = 20.dp, end = 20.dp),
)
OutlinedButton(
onClick = {
productViewModel.addProduct(title.value.text, selectedCategory, price.value.text)
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
modifier = Modifier
.fillMaxWidth()
.padding(20.dp),
) {
Text(
text = "Сохранить",
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 30.sp,
color = Color.White
)
}
}
}
}
}
}

View File

@ -0,0 +1,230 @@
package com.example.mobileapp.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.mobileapp.PreferencesManager
import com.example.mobileapp.R
import com.example.mobileapp.models.Category
import com.example.mobileapp.models.CategoryWithProducts
import com.example.mobileapp.models.Product
import com.example.mobileapp.navigation.Screens
import com.example.mobileapp.viewmodels.MenuViewModel
import com.example.mobileapp.widgets.Header
@Composable
fun EditMenuScreen(navController: NavController){
Column (
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Header(Color(212, 206, 203))
EditMenuCard(navController)
}
}
@Composable
fun EditMenuCard(navController: NavController){
val sharedPref = PreferencesManager(LocalContext.current)
val menuViewModel: MenuViewModel = hiltViewModel<MenuViewModel>()
menuViewModel.retrieveUser(sharedPref)
val categoriesWithProducts = menuViewModel.categoiesWithProductsListUiState.collectAsLazyPagingItems()
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(212, 206, 203)),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(0.9f)
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
) {
items(
count = categoriesWithProducts.itemCount,
key = categoriesWithProducts.itemKey()
){ index ->
EditMenuCategoryProductsCard(navController, menuViewModel, categoriesWithProducts[index]!!)
}
}
}
}
}
@Composable
fun EditMenuCategoryProductsCard(navController: NavController, menuViewModel : MenuViewModel, categoryWithProducts: CategoryWithProducts){
if (categoryWithProducts.products.size == 0){
return
}
Text(
text = categoryWithProducts.category.name,
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
textAlign = TextAlign.Center,
)
categoryWithProducts.products.forEach{ product ->
EditMenuProductsCard(navController, menuViewModel, product, categoryWithProducts.category)
}
}
@Composable
fun EditMenuProductsCard(navController: NavController, menuViewModel : MenuViewModel, product : Product, category: Category){
Card(
shape = RoundedCornerShape(20.dp),
border = BorderStroke(3.dp, Color(222,161,69)),
modifier = Modifier
.fillMaxSize()
.padding(top = 15.dp, start = 5.dp, end = 5.dp)
){
Row(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly,
){
EditMenuProductImageCard(category.name)
Column (
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceAround
) {
Text(
text = product.name,
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 17.sp,
textAlign = TextAlign.Center,
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp),
){
Text(
text = product.price.toString() + " р.",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 17.sp,
textAlign = TextAlign.Center,
)
OutlinedButton(
onClick = {
navController.navigate(Screens.EditProductScreen.name + "/${product.id}")
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(10.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
){
Icon(
painterResource(id = R.drawable.icon_settings),
contentDescription = null,
Modifier.size(30.dp),
tint = Color.Black
)
}
OutlinedButton(
onClick = {
menuViewModel.deleteProduct(product)
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(10.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
){
Icon(
painterResource(id = R.drawable.icon_trash),
contentDescription = null,
Modifier.size(30.dp),
tint = Color.Black
)
}
}
}
}
}
}
@Composable
fun EditMenuProductImageCard(categoryName : String){
if (categoryName == "Бургеры"){
Image(
painterResource(
id = R.drawable.burger
),
contentDescription = null,
modifier = Modifier
.size(100.dp, 100.dp)
.padding(5.dp),
)
}
if (categoryName == "Картошка"){
Image(
painterResource(
id = R.drawable.potato
),
contentDescription = null,
modifier = Modifier
.size(100.dp, 100.dp)
.padding(5.dp),
)
}
if (categoryName == "Напитки"){
Image(
painterResource(
id = R.drawable.pepsi
),
contentDescription = null,
modifier = Modifier
.size(100.dp, 100.dp)
.padding(5.dp),
)
}
}

View File

@ -0,0 +1,239 @@
package com.example.mobileapp.screens
import android.annotation.SuppressLint
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import com.example.mobileapp.R
import com.example.mobileapp.models.Product
import com.example.mobileapp.navigation.Screens
import com.example.mobileapp.viewmodels.ProductViewModel
import com.example.mobileapp.widgets.Header
import kotlinx.coroutines.launch
@Composable
fun EditProductScreen(navController: NavController, id: Int){
Column (
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Header(Color(212, 206, 203))
EditProductCard(navController, id)
}
}
@SuppressLint("CoroutineCreationDuringComposition")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditProductCard(navController: NavController, productId : Int){
val scope = rememberCoroutineScope()
val productViewModel: ProductViewModel = hiltViewModel<ProductViewModel>()
productViewModel.getProduct(productId)
val currentProduct = remember { mutableStateOf<Product?>(null) }
val title = remember { mutableStateOf(TextFieldValue("")) }
val price = remember { mutableStateOf(TextFieldValue("")) }
val categoryList = remember { mutableStateListOf(productViewModel.categories) }
var selectedCategory by remember { mutableStateOf("") }
var expanded by remember { mutableStateOf(false) }
val success = productViewModel.successState.observeAsState().value
if (productViewModel.product.value != null){
currentProduct.value = productViewModel.product.value!!
title.value = TextFieldValue(productViewModel.product.value!!.name)
price.value = TextFieldValue(productViewModel.product.value!!.price.toString())
scope.launch {
selectedCategory = productViewModel.getCategory(productViewModel.product.value!!.categoryId)
}
}
if (success == true) {
navController.navigate(Screens.EditMenuScreen.name)
}
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(212, 206, 203)),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(0.9f)
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Редактирование блюда",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
textAlign = TextAlign.Center,
)
Card(
shape = RoundedCornerShape(20.dp),
border = BorderStroke(3.dp, Color(222,161,69)),
modifier = Modifier
.padding(top = 15.dp, start = 5.dp, end = 5.dp)
){
Column (
modifier = Modifier.background(Color.White)
) {
OutlinedTextField(
value = title.value,
onValueChange = {title.value = it},
placeholder = { Text(text = "Название", fontSize = 20.sp, color = Color.LightGray) },
singleLine = true,
shape = RoundedCornerShape(20.dp),
textStyle = TextStyle(fontSize = 20.sp),
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.Black,
disabledTextColor = Color.Transparent,
containerColor = Color.White,
focusedBorderColor = Color(222,161,69),
unfocusedBorderColor = Color(222,161,69),
),
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp, start = 20.dp, end = 20.dp),
)
Card(
shape = RoundedCornerShape(20.dp),
border = BorderStroke(1.dp, Color(222,161,69)),
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp, start = 20.dp, end = 20.dp),
){
MaterialTheme(
colorScheme = lightColorScheme(
surfaceVariant= Color.White,
surface = Color.White,
onSurface = Color.Gray
)
){
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = {
expanded = !expanded
},
) {
TextField(
modifier = Modifier
.menuAnchor()
.fillMaxWidth(),
readOnly = true,
value = selectedCategory,
onValueChange = {},
label = { Text("Категория") },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = {
expanded = false
},
) {
categoryList.first().forEach { selectionOption ->
DropdownMenuItem(
text = { Text(selectionOption) },
onClick = {
selectedCategory = selectionOption
expanded = false
},
contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
)
}
}
}
}
}
OutlinedTextField(
value = price.value,
onValueChange = {price.value = it},
placeholder = { Text(text = "Цена", fontSize = 20.sp, color = Color.LightGray) },
singleLine = true,
shape = RoundedCornerShape(20.dp),
textStyle = TextStyle(fontSize = 20.sp),
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.Black,
disabledTextColor = Color.Transparent,
containerColor = Color.White,
focusedBorderColor = Color(222,161,69),
unfocusedBorderColor = Color(222,161,69),
),
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp, start = 20.dp, end = 20.dp),
)
OutlinedButton(
onClick = {
productViewModel.updateProduct(currentProduct.value?.id!!, title.value.text, selectedCategory, price.value.text)
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
modifier = Modifier
.fillMaxWidth()
.padding(20.dp),
) {
Text(
text = "Сохранить",
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 30.sp,
color = Color.White
)
}
}
}
}
}
}

View File

@ -0,0 +1,149 @@
package com.example.mobileapp.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.example.mobileapp.R
import com.example.mobileapp.navigation.Screens
import com.example.mobileapp.widgets.Header
@Composable
fun ManagmentScreen(navController: NavController){
Column (
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Header(Color(212, 206, 203))
ManagmentCard(navController)
}
}
@Composable
fun ManagmentCard(navController: NavController){
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(212, 206, 203)),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(0.9f)
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Блюда",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
textAlign = TextAlign.Center,
)
Row(
horizontalArrangement = Arrangement.spacedBy(5.dp),
modifier = Modifier.padding(top = 10.dp),
){
OutlinedButton(
onClick = {
navController.navigate(Screens.CreateProductScreen.name)
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
){
Icon(
painterResource(id = R.drawable.icon_add),
contentDescription = null,
Modifier.size(50.dp),
tint = Color.Black
)
}
OutlinedButton(
onClick = {
navController.navigate(Screens.EditMenuScreen.name)
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
){
Icon(
painterResource(id = R.drawable.icon_settings),
contentDescription = null,
Modifier.size(50.dp),
tint = Color.Black
)
}
}
Text(
text = "Пользователи",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
textAlign = TextAlign.Center,
)
OutlinedButton(
onClick = {
navController.navigate(Screens.UsersScreen.name)
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
modifier = Modifier.padding(top = 10.dp)
){
Icon(
painterResource(id = R.drawable.icon_profile),
contentDescription = null,
Modifier.size(50.dp),
tint = Color.Black
)
Icon(
painterResource(id = R.drawable.icon_order),
contentDescription = null,
Modifier.size(50.dp),
tint = Color.Black
)
Icon(
painterResource(id = R.drawable.icon_managment),
contentDescription = null,
Modifier.size(50.dp),
tint = Color.Black
)
}
}
}
}

View File

@ -0,0 +1,203 @@
package com.example.mobileapp.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.mobileapp.PreferencesManager
import com.example.mobileapp.R
import com.example.mobileapp.models.Category
import com.example.mobileapp.models.CategoryWithProducts
import com.example.mobileapp.models.Product
import com.example.mobileapp.viewmodels.MenuViewModel
import com.example.mobileapp.widgets.Header
@Composable
fun MenuScreen(){
Column (
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Header(Color(212, 206, 203))
MenuCard()
}
}
@Composable
fun MenuCard(){
val sharedPref = PreferencesManager(LocalContext.current)
val menuViewModel: MenuViewModel = hiltViewModel<MenuViewModel>()
menuViewModel.retrieveUser(sharedPref)
val categoriesWithProducts = menuViewModel.categoiesWithProductsListUiState.collectAsLazyPagingItems()
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(212, 206, 203)),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(0.9f)
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
) {
items(
count = categoriesWithProducts.itemCount,
key = categoriesWithProducts.itemKey()
){ index ->
MenuCategoryProductsCard(menuViewModel, categoriesWithProducts[index]!!)
}
}
}
}
}
@Composable
fun MenuCategoryProductsCard(menuViewModel : MenuViewModel, categoryWithProducts: CategoryWithProducts){
if (categoryWithProducts.products.size == 0){
return
}
Text(
text = categoryWithProducts.category.name,
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
textAlign = TextAlign.Center,
)
categoryWithProducts.products.forEach{ product ->
MenuProductsCard(menuViewModel, product, categoryWithProducts.category)
}
}
@Composable
fun MenuProductsCard(menuViewModel : MenuViewModel, product : Product, category: Category){
Card(
shape = RoundedCornerShape(20.dp),
border = BorderStroke(3.dp, Color(222,161,69)),
modifier = Modifier
.fillMaxSize()
.padding(top = 15.dp, start = 5.dp, end = 5.dp)
){
Row(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly,
){
MenuProductImageCard(category.name)
Column (
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceAround
) {
Text(
text = product.name,
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 17.sp,
textAlign = TextAlign.Center,
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp),
){
Text(
text = product.price.toString() + " р.",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 17.sp,
textAlign = TextAlign.Center,
)
OutlinedButton(
onClick = {
menuViewModel.addProductToCart(product.id!!)
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(30.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
) {
Text(text = "+", fontSize = 20.sp, color = Color.Black)
}
}
}
}
}
}
@Composable
fun MenuProductImageCard(categoryName : String){
if (categoryName == "Бургеры"){
Image(
painterResource(
id = R.drawable.burger
),
contentDescription = null,
modifier = Modifier
.size(100.dp, 100.dp)
.padding(5.dp),
)
}
if (categoryName == "Картошка"){
Image(
painterResource(
id = R.drawable.potato
),
contentDescription = null,
modifier = Modifier
.size(100.dp, 100.dp)
.padding(5.dp),
)
}
if (categoryName == "Напитки"){
Image(
painterResource(
id = R.drawable.pepsi
),
contentDescription = null,
modifier = Modifier
.size(100.dp, 100.dp)
.padding(5.dp),
)
}
}

View File

@ -0,0 +1,196 @@
package com.example.mobileapp.screens
import android.annotation.SuppressLint
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.mobileapp.R
import com.example.mobileapp.models.OrderWithProducts
import com.example.mobileapp.viewmodels.OrderViewModel
import com.example.mobileapp.widgets.Header
import kotlinx.coroutines.launch
@Composable
fun OrderScreen(){
Column (
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Header(Color(212, 206, 203))
OrdersCard()
}
}
@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun OrdersCard(){
val orderViewModel: OrderViewModel = hiltViewModel<OrderViewModel>()
val orderListUiState = orderViewModel.orderList.observeAsState().value?.collectAsLazyPagingItems()
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(212, 206, 203)),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(0.9f)
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Заказы",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
textAlign = TextAlign.Center,
)
if (orderListUiState != null){
LazyColumn(
modifier = Modifier
.fillMaxWidth()
) {
items(
count = orderListUiState.itemCount,
key = orderListUiState.itemKey()
){ index ->
if (orderListUiState[index] != null){
OrderCard(orderListUiState[index]!!, orderViewModel)
}
}
}
}
}
}
}
@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun OrderCard(order: OrderWithProducts, orderViewModel: OrderViewModel){
val scope = rememberCoroutineScope()
var status by remember { mutableStateOf("")}
scope.launch {
status = orderViewModel.getOrderStatus(order.order.statusId)
}
Card(
shape = RoundedCornerShape(20.dp),
border = BorderStroke(3.dp, Color(222,161,69)),
modifier = Modifier
.padding(top = 15.dp, start = 5.dp, end = 5.dp)
){
Row(
modifier = Modifier
.fillMaxWidth()
.background(Color.White),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly,
){
Text(
text = "" + order.order.id,
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
textAlign = TextAlign.Center,
)
Text(
text = "Статус: " + status,
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 20.sp,
textAlign = TextAlign.Center,
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.background(Color.White),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceAround,
){
Column {
if (order.orderWithProducts.size != 0){
order.orderWithProducts.forEach{ orderProduct ->
Row(
modifier = Modifier
.background(Color.White),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
){
Text(
text = orderProduct.product.name + " x" + orderProduct.orderProduct.amount.toString(),
color = Color.Gray,
fontFamily = FontFamily(Font(R.font.nunito_bold)),
fontSize = 20.sp,
textAlign = TextAlign.Center,
)
}
}
}
}
OutlinedButton(
onClick = {
orderViewModel.changeOrderStatus(order.order.id!!)
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
modifier = Modifier.padding(bottom = 10.dp)
) {
if (order.order.statusId == 2){
Text(
text = "Готов",
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 30.sp,
color = Color.White
)
}
if (order.order.statusId == 3){
Text(
text = "Выдать",
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 30.sp,
color = Color.White
)
}
}
}
}
}

View File

@ -0,0 +1,195 @@
package com.example.mobileapp.screens
import android.annotation.SuppressLint
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.mobileapp.PreferencesManager
import com.example.mobileapp.R
import com.example.mobileapp.models.OrderWithProducts
import com.example.mobileapp.navigation.Screens
import com.example.mobileapp.viewmodels.ProfileViewModel
import com.example.mobileapp.widgets.Header
import kotlinx.coroutines.launch
@Composable
fun ProfileScreen(navController: NavController){
Column (
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Header(Color(212, 206, 203))
ProfileCard(navController)
}
}
@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun ProfileCard(navController: NavController){
val sharedPref = PreferencesManager(LocalContext.current)
val profileViewModel: ProfileViewModel = hiltViewModel<ProfileViewModel>()
val activeOrdersListUiState = profileViewModel.getActiveOrders(sharedPref.getData("userId", "-1").toInt())?.collectAsLazyPagingItems()
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(212, 206, 203)),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(0.9f)
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
) {
OutlinedButton(
modifier = Modifier
.fillMaxWidth()
.padding(top = 15.dp, start = 5.dp, end = 5.dp),
onClick = {
profileViewModel.logout(sharedPref)
navController.navigate(Screens.AuthorizationScreen.name)
},
border = BorderStroke(4.dp, Color(255,86,86)),
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,86,86)),
) {
Text(
text = "Выйти из аккаунта",
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 30.sp,
color = Color.White
)
}
Text(
text = "Активные заказы",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
textAlign = TextAlign.Center,
)
if (activeOrdersListUiState != null){
LazyColumn(
modifier = Modifier
.fillMaxWidth()
) {
items(
count = activeOrdersListUiState.itemCount,
key = activeOrdersListUiState.itemKey()
){ index ->
ActiveOrderCard(activeOrdersListUiState[index]!!, profileViewModel)
}
}
}
}
}
}
@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun ActiveOrderCard(order: OrderWithProducts, profileViewModel: ProfileViewModel) {
val scope = rememberCoroutineScope()
val status = remember { mutableStateOf("")}
scope.launch {
status.value = profileViewModel.getOrderStatus(order.order.statusId)
}
Card(
shape = RoundedCornerShape(20.dp),
border = BorderStroke(3.dp, Color(222,161,69)),
modifier = Modifier
.padding(top = 15.dp, start = 5.dp, end = 5.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(Color.White),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly,
) {
Text(
text = "" + order.order.id.toString(),
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
textAlign = TextAlign.Center,
)
Text(
text = "Статус: " + status.value,
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 20.sp,
textAlign = TextAlign.Center,
)
}
order.orderWithProducts.forEach { orderProduct ->
Row(
modifier = Modifier
.fillMaxWidth()
.background(Color.White),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
Text(
text = orderProduct.product.name + " " + orderProduct.product.price.toString() + "р. x" + orderProduct.orderProduct.amount.toString(),
color = Color.Gray,
fontFamily = FontFamily(Font(R.font.nunito_bold)),
fontSize = 20.sp,
textAlign = TextAlign.Center,
)
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.background(Color.White),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start,
) {
Text(
text = "К оплате: " + order.order.price.toString() + " р.",
modifier = Modifier.padding(start = 20.dp),
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 20.sp,
textAlign = TextAlign.Center,
)
}
}
}

View File

@ -0,0 +1,231 @@
package com.example.mobileapp.screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import com.example.mobileapp.R
import com.example.mobileapp.navigation.Screens
import com.example.mobileapp.viewmodels.RegistrationViewModel
import com.example.mobileapp.widgets.Header
@Composable
fun RegistrationScreen(navController: NavController) {
Column (
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Header(Color.White)
RegistrationCard(navController)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RegistrationCard(navController: NavController){
val login = remember { mutableStateOf(TextFieldValue("")) }
val password = remember { mutableStateOf(TextFieldValue("")) }
val passwordRepeat = remember { mutableStateOf(TextFieldValue("")) }
val viewModel = hiltViewModel<RegistrationViewModel>()
val success = viewModel.successState.observeAsState().value
if (success == true) {
navController.navigate(Screens.AuthorizationScreen.name)
}
if (success == false) {
AlertDialog(
title = {
Text(
text = "Ошибка регистрации",
fontWeight = FontWeight.Bold,
fontSize = 20.sp
)
},
text = {
Text(
text = "Проверьте корректность введенных данных",
fontWeight = FontWeight.Normal,
fontSize = 16.sp
)
},
onDismissRequest = {
viewModel.calmSuccessState()
},
confirmButton = {
TextButton(
onClick = {
viewModel.calmSuccessState()
}
) {
Text("ОК")
}
}
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Card(
shape = RoundedCornerShape(30.dp),
modifier = Modifier.padding(top = 30.dp)
) {
Column (
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.background(Color(212, 206, 203))
.fillMaxWidth(0.9f)
) {
Card(
shape = RoundedCornerShape(90.dp, 90.dp, 30.dp, 30.dp),
border = BorderStroke(4.dp, Color(222,161,69)),
modifier = Modifier
.fillMaxWidth()
.padding(top = 30.dp, start = 20.dp, end = 20.dp)
){
Text(
text = "Регистрация",
color = Color.White,
fontFamily = FontFamily(Font(R.font.nunito_bold)),
fontSize = 30.sp,
modifier = Modifier
.fillMaxWidth()
.background(Color(255, 186, 83))
.padding(top = 20.dp, bottom = 20.dp),
textAlign = TextAlign.Center,
)
}
OutlinedTextField(
value = login.value,
onValueChange = {login.value = it},
placeholder = { Text(text = "Логин", fontSize = 20.sp, color = Color.LightGray) },
singleLine = true,
shape = RoundedCornerShape(30.dp, 30.dp, 30.dp, 30.dp),
textStyle = TextStyle(fontSize = 20.sp),
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.Black,
disabledTextColor = Color.Transparent,
containerColor = Color.White,
focusedBorderColor = Color.Yellow,
unfocusedBorderColor = Color.Yellow,
),
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp, start = 20.dp, end = 20.dp),
)
OutlinedTextField(
value = password.value,
onValueChange = {password.value = it},
placeholder = { Text(text = "Пароль", fontSize = 20.sp, color = Color.LightGray) },
singleLine = true,
shape = RoundedCornerShape(30.dp, 30.dp, 30.dp, 30.dp),
textStyle = TextStyle(fontSize = 20.sp),
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.Black,
disabledTextColor = Color.Transparent,
containerColor = Color.White,
focusedBorderColor = Color(183,97,48),
unfocusedBorderColor = Color(183,97,48),
),
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp, start = 20.dp, end = 20.dp),
)
OutlinedTextField(
value = passwordRepeat.value,
onValueChange = {passwordRepeat.value = it},
placeholder = { Text(text = "Логин повторно", fontSize = 20.sp, color = Color.LightGray) },
singleLine = true,
shape = RoundedCornerShape(30.dp, 30.dp, 30.dp, 30.dp),
textStyle = TextStyle(fontSize = 20.sp),
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.Black,
disabledTextColor = Color.Transparent,
containerColor = Color.White,
focusedBorderColor = Color.Red,
unfocusedBorderColor = Color.Red,
),
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp, start = 20.dp, end = 20.dp),
)
Button(
onClick = {
viewModel.registration(login.value.text, password.value.text, passwordRepeat.value.text)
},
colors = ButtonDefaults.buttonColors(Color(255,186,83), Color.White),
shape = RoundedCornerShape(30.dp, 30.dp, 30.dp, 30.dp),
border = BorderStroke(4.dp, Color(222,161,69)),
modifier = Modifier
.fillMaxWidth()
.padding(top = 20.dp, start = 20.dp, end = 20.dp)
) {
Text(
text = "Продолжить",
color = Color.White,
fontFamily = FontFamily(Font(R.font.nunito_bold)),
fontSize = 30.sp,
textAlign = TextAlign.Center,
)
}
Button(
onClick = {
navController.navigate(Screens.AuthorizationScreen.name)
},
colors = ButtonDefaults.buttonColors(Color(212,206,203)),
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp, start = 20.dp, end = 20.dp)
) {
Text(
text = "Есть аккаунт",
color = Color.White,
fontFamily = FontFamily(Font(R.font.nunito_bold)),
fontSize = 20.sp,
textAlign = TextAlign.Center,
)
}
}
}
}
}

View File

@ -0,0 +1,259 @@
package com.example.mobileapp.screens
import android.annotation.SuppressLint
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.map
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.mobileapp.PreferencesManager
import com.example.mobileapp.R
import com.example.mobileapp.db.AppDatabase
import com.example.mobileapp.models.Role
import com.example.mobileapp.models.User
import com.example.mobileapp.viewmodels.UsersViewModel
import com.example.mobileapp.widgets.Header
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@Composable
fun UsersScreen() {
Column (
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Header(Color(212, 206, 203))
UsersCard()
}
}
@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun UsersCard(){
val sharedPref = PreferencesManager(LocalContext.current)
val usersViewModel: UsersViewModel = hiltViewModel<UsersViewModel>()
val userListUiState = usersViewModel.getUsersList().collectAsLazyPagingItems()
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(212, 206, 203)),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(0.9f)
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Редактирование пользователей",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
textAlign = TextAlign.Center,
)
if (userListUiState != null){
LazyColumn(
modifier = Modifier
.fillMaxSize()
) {
items(
count = userListUiState.itemCount,
key = userListUiState.itemKey()
){ index ->
if (userListUiState[index]!!.id != sharedPref.getData("userId", "-1").toInt()){
UserCard(userListUiState[index]!!, usersViewModel)
}
}
}
}
}
}
}
@SuppressLint("CoroutineCreationDuringComposition")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun UserCard (user: User, usersViewModel: UsersViewModel){
val scope = rememberCoroutineScope()
val roleList = remember { mutableStateListOf(usersViewModel.roles)}
var selectedRole by remember { mutableStateOf("")}
scope.launch {
selectedRole = usersViewModel.getUserRole(user)
}
var expanded by remember { mutableStateOf(false) }
Card(
shape = RoundedCornerShape(20.dp),
border = BorderStroke(3.dp, Color(222,161,69)),
modifier = Modifier
.fillMaxSize()
.padding(top = 15.dp, start = 5.dp, end = 5.dp)
){
Row(
modifier = Modifier
.fillMaxSize()
.background(Color.White),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly,
){
Column (
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceAround,
modifier = Modifier.fillMaxWidth(0.4f)
) {
Text(
text = "Логин: " + user.login,
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 17.sp,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 5.dp)
)
Card(
shape = RoundedCornerShape(20.dp),
border = BorderStroke(1.dp, Color(222,161,69)),
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 10.dp),
){
MaterialTheme(
colorScheme = lightColorScheme(
surfaceVariant= Color.White,
surface = Color.White,
onSurface = Color.Gray
)
){
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = {
expanded = !expanded
},
) {
TextField(
modifier = Modifier
.menuAnchor()
.fillMaxWidth(),
readOnly = true,
value = selectedRole,
onValueChange = {},
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = {
expanded = false
},
) {
roleList.first().forEach {selectionOption ->
DropdownMenuItem(
text = { Text(selectionOption) },
onClick = {
selectedRole = selectionOption
expanded = false
},
contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
)
}
}
}
}
}
}
OutlinedButton(
onClick = {
usersViewModel.changeUserRole(user.id!!, selectedRole)
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(10.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
){
Icon(
painterResource(id = R.drawable.icon_settings),
contentDescription = null,
Modifier.size(30.dp),
tint = Color.Black
)
}
OutlinedButton(
onClick = {
usersViewModel.deleteUser(user.id!!)
},
border = BorderStroke(4.dp, Color(222,161,69)),
shape = RoundedCornerShape(10.dp),
colors = ButtonDefaults.outlinedButtonColors(Color(255,186,83)),
){
Icon(
painterResource(id = R.drawable.icon_trash),
contentDescription = null,
Modifier.size(30.dp),
tint = Color.Black
)
}
}
}
}

View File

@ -0,0 +1,11 @@
package com.example.mobileapp.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

View File

@ -0,0 +1,70 @@
package com.example.mobileapp.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun MobileAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

View File

@ -0,0 +1,34 @@
package com.example.mobileapp.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)

View File

@ -0,0 +1,73 @@
package com.example.mobileapp.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.mobileapp.PreferencesManager
import com.example.mobileapp.models.Category
import com.example.mobileapp.models.Role
import com.example.mobileapp.models.Status
import com.example.mobileapp.models.User
import com.example.mobileapp.repositories.CategoryRepository
import com.example.mobileapp.repositories.RoleRepository
import com.example.mobileapp.repositories.StatusRepository
import com.example.mobileapp.repositories.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class AuthorizationViewModel @Inject constructor(
private val userRepository: UserRepository,
private val categoryRepository: CategoryRepository,
private val roleRepository: RoleRepository,
private val statusRepository: StatusRepository,
) : ViewModel() {
private val _successState = MutableLiveData<Boolean?>()
val successState: LiveData<Boolean?>
get() = _successState
init {
addAdmin()
}
fun calmSuccessState() {
_successState.postValue(null)
}
fun login(sharedPref: PreferencesManager, login: String, password: String) {
viewModelScope.launch {
userRepository.getByLoginAndPassword(login, password).collect {
if (it == null) {
_successState.postValue(false)
} else {
sharedPref.saveData("userId", it.id.toString())
sharedPref.saveData("userRole", roleRepository.getById(it.roleId).first().name)
_successState.postValue(true)
}
}
}
}
private fun addAdmin() {
viewModelScope.launch {
userRepository.getAll().collect {
if (it.size == 0) {
roleRepository.insert(Role(1, "admin"))
roleRepository.insert(Role(2, "worker"))
roleRepository.insert(Role(3, "user"))
statusRepository.insert(Status(1, "Корзина"))
statusRepository.insert(Status(2, "Принят"))
statusRepository.insert(Status(3, "Готов"))
statusRepository.insert(Status(4, "Выдан"))
categoryRepository.insert(Category(1, "Бургеры"))
categoryRepository.insert(Category(2, "Картошка"))
categoryRepository.insert(Category(3, "Напитки"))
userRepository.insert(User(1, "admin", "admin", 1))
}
}
}
}
}

View File

@ -0,0 +1,102 @@
package com.example.mobileapp.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.mobileapp.PreferencesManager
import com.example.mobileapp.models.OrderProduct
import com.example.mobileapp.models.OrderWithProducts
import com.example.mobileapp.models.User
import com.example.mobileapp.repositories.OrderProductRepository
import com.example.mobileapp.repositories.OrderRepository
import com.example.mobileapp.repositories.StatusRepository
import com.example.mobileapp.repositories.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class CartViewModel @Inject constructor(
private val orderRepository: OrderRepository,
private val orderProductRepository: OrderProductRepository,
private val statusRepository: StatusRepository,
private val userRepository: UserRepository
) : ViewModel() {
private val _user = MutableLiveData<User>()
val user: LiveData<User>
get() = _user
private val _cart = MutableLiveData<OrderWithProducts?>()
val cart: LiveData<OrderWithProducts?>
get() = _cart
fun retrieveUser(sharedPref: PreferencesManager) {
val userId = sharedPref.getData("userId", "-1").toInt()
if (userId == -1) return
viewModelScope.launch {
userRepository.getById(userId).collect {
_user.postValue(it)
}
}
viewModelScope.launch {
orderRepository.getCartByUserId(userId).collect{
_cart.postValue(it)
}
}
}
fun makeOrder() {
viewModelScope.launch {
val model = cart.value!!.order
model.statusId = statusRepository.getByName("Принят").first().id!!
orderRepository.update(model)
}
}
fun removeProductFromOrder(productId: Int) {
val orderModel = cart.value!!.order
var productModel : OrderProduct? = null
cart.value!!.orderWithProducts.map {
if (it.orderProduct.productId == productId){
productModel = it.orderProduct
orderModel.price -= it.product.price
}
}
if(productModel!!.amount == 1) {
// delete
viewModelScope.launch {
orderRepository.update(orderModel)
orderProductRepository.delete(productModel!!)
}
}
else{
// update
productModel!!.amount -= 1
viewModelScope.launch {
orderRepository.update(orderModel)
orderProductRepository.update(productModel!!)
}
}
}
fun addProductToOrder(productId: Int) {
val orderModel = cart.value!!.order
var productModel : OrderProduct? = null
cart.value!!.orderWithProducts.map {
if (it.orderProduct.productId == productId){
productModel = it.orderProduct
orderModel.price += it.product.price
}
}
// update
productModel!!.amount += 1
viewModelScope.launch {
orderRepository.update(orderModel)
orderProductRepository.update(productModel!!)
}
}
}

View File

@ -0,0 +1,87 @@
package com.example.mobileapp.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import com.example.mobileapp.PreferencesManager
import com.example.mobileapp.models.CategoryWithProducts
import com.example.mobileapp.models.Order
import com.example.mobileapp.models.OrderProduct
import com.example.mobileapp.models.Product
import com.example.mobileapp.models.User
import com.example.mobileapp.repositories.CategoryRepository
import com.example.mobileapp.repositories.OrderProductRepository
import com.example.mobileapp.repositories.OrderRepository
import com.example.mobileapp.repositories.ProductRepository
import com.example.mobileapp.repositories.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import javax.inject.Inject
@HiltViewModel
class MenuViewModel @Inject constructor(
private val productRepository: ProductRepository,
private val orderRepository: OrderRepository,
private val orderProductRepository: OrderProductRepository,
private val userRepository: UserRepository
) : ViewModel() {
private val _user = MutableLiveData<User>()
val user: LiveData<User>
get() = _user
fun retrieveUser(sharedPref: PreferencesManager) {
val userId = sharedPref.getData("userId", "-1").toInt()
if (userId == -1) return
viewModelScope.launch {
userRepository.getById(userId).collect {
_user.postValue(it)
}
}
}
val categoiesWithProductsListUiState: Flow<PagingData<CategoryWithProducts>> = productRepository.getCategoriesWithProducts()
fun deleteProduct(product : Product) {
viewModelScope.launch {
productRepository.delete(product)
}
}
fun addProductToCart(productId: Int) {
viewModelScope.launch {
val product = productRepository.getById(productId).first()
val order = orderRepository.getCartByUserId(user.value?.id!!).first()
if (order == null) {
val newOrderId = orderRepository.insert(Order(null, user.value?.id!!, product.price, 1 ))
orderProductRepository.insert(OrderProduct(newOrderId.toInt(), productId, 1))
}
else {
var isAlreadyAdded = false
for (prod in order.orderWithProducts) {
if (prod.product.id == productId) {
val newOrderProduct = prod.orderProduct
newOrderProduct.amount += 1
val newOrder = order.order
newOrder.price += prod.product.price
orderProductRepository.update(newOrderProduct)
orderRepository.update(newOrder)
isAlreadyAdded = true
}
}
if (!isAlreadyAdded) {
orderProductRepository.insert(OrderProduct(order.order.id!!, productId, 1))
val newOrder = order.order
newOrder.price += product.price
orderRepository.update(newOrder)
}
}
}
}
}

View File

@ -0,0 +1,60 @@
package com.example.mobileapp.viewmodels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import com.example.mobileapp.models.OrderWithProducts
import com.example.mobileapp.repositories.OrderRepository
import com.example.mobileapp.repositories.StatusRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class OrderViewModel @Inject constructor(
private val statusRepository: StatusRepository,
private val orderRepository: OrderRepository
) : ViewModel() {
val orderList = MutableLiveData<Flow<PagingData<OrderWithProducts>>>()
init {
updateOrderList()
}
private fun updateOrderList(){
orderList.postValue(orderRepository.getOrdersToWork())
}
suspend fun getOrderStatus(statusId: Int) : String{
return statusRepository.getById(statusId).first().name
}
fun changeOrderStatus(orderId: Int) {
viewModelScope.launch {
val order = orderRepository.getById(orderId).first()
when(statusRepository.getById(order.order.statusId).first().name){
"Принят" -> {
val newOrder = order.order
newOrder.statusId = statusRepository.getByName("Готов").first().id!!
orderRepository.update(newOrder)
}
"Готов" -> {
val newOrder = order.order
newOrder.statusId = statusRepository.getByName("Выдан").first().id!!
orderRepository.update(newOrder)
}
}
updateOrderList()
// для удобства
if (orderRepository.getById(orderId).first().order.statusId == 4){
orderRepository.delete(orderRepository.getById(orderId).first().order)
}
}
}
}

View File

@ -0,0 +1,93 @@
package com.example.mobileapp.viewmodels
import androidx.compose.runtime.mutableStateListOf
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.mobileapp.models.OrderWithProducts
import com.example.mobileapp.models.Product
import com.example.mobileapp.models.User
import com.example.mobileapp.repositories.CategoryRepository
import com.example.mobileapp.repositories.ProductRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class ProductViewModel @Inject constructor(
private val productRepository: ProductRepository,
private val categoryRepository: CategoryRepository
) : ViewModel() {
private val _successState = MutableLiveData<Boolean?>()
private val _categories = mutableStateListOf<String>()
val categories: List<String>
get() = _categories
val successState: LiveData<Boolean?>
get() = _successState
private val _product = MutableLiveData<Product?>()
val product: LiveData<Product?>
get() = _product
fun getProduct(productId: Int) {
viewModelScope.launch {
_product.postValue(productRepository.getById(productId).first())
}
}
init {
viewModelScope.launch {
categoryRepository.getAll().collect{data ->
_categories.addAll(data.map { category -> category.name })
}
}
}
suspend fun getCategory(categoryId : Int) : String{
return categoryRepository.getById(categoryId).first().name
}
fun calmSuccessState() {
_successState.postValue(null)
}
fun addProduct(name: String, category: String, price: String) {
if (name.isEmpty() || name.length > 10 || name.length < 3){
_successState.postValue(false)
return
}
if (category.isEmpty()){
_successState.postValue(false)
return
}
if (price.isEmpty() || price.toIntOrNull() == null){
_successState.postValue(false)
return
}
viewModelScope.launch {
productRepository.insert(Product(null, name, categoryRepository.getByName(category).first().id!!, price.toInt()))
_successState.postValue(true)
}
}
fun updateProduct(id: Int, name: String, category: String, price: String) {
if (name.isEmpty() || name.length > 10 || name.length < 3){
_successState.postValue(false)
return
}
if (category.isEmpty()){
_successState.postValue(false)
return
}
if (price.isEmpty() || price.toIntOrNull() == null){
_successState.postValue(false)
return
}
viewModelScope.launch {
productRepository.update(Product(id, name, categoryRepository.getByName(category).first().id!!, price.toInt()))
_successState.postValue(true)
}
}
}

View File

@ -0,0 +1,42 @@
package com.example.mobileapp.viewmodels
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingData
import com.example.mobileapp.PreferencesManager
import com.example.mobileapp.models.CategoryWithProducts
import com.example.mobileapp.models.OrderWithProducts
import com.example.mobileapp.models.User
import com.example.mobileapp.repositories.OrderRepository
import com.example.mobileapp.repositories.StatusRepository
import com.example.mobileapp.repositories.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class ProfileViewModel @Inject constructor(
private val orderRepository: OrderRepository,
private val statusRepository: StatusRepository
) : ViewModel() {
fun getActiveOrders(userId : Int) : Flow<PagingData<OrderWithProducts>>? {
if (userId == -1) return null
return orderRepository.getOrdersByUserId(userId)
}
suspend fun getOrderStatus(statusId: Int) : String{
return statusRepository.getById(statusId).first().name
}
fun logout(sharedPref: PreferencesManager) {
sharedPref.deleteData("userId")
sharedPref.deleteData("userRole")
}
}

View File

@ -0,0 +1,52 @@
package com.example.mobileapp.viewmodels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.mobileapp.models.User
import com.example.mobileapp.repositories.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class RegistrationViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
private val _successState = MutableLiveData<Boolean?>()
val successState: LiveData<Boolean?>
get() = _successState
fun calmSuccessState() {
_successState.postValue(null)
}
fun registration(login: String, password: String, confirmPassword: String) {
if (login.length < 3 || login.length > 20) {
_successState.postValue(false)
return
}
if (password.length < 3 || password.length > 20) {
_successState.postValue(false)
return
}
if (password != confirmPassword) {
_successState.postValue(false)
return
}
viewModelScope.launch {
userRepository.getByLoginAndPassword(login, password).collect {
if (it != null) {
_successState.postValue(false)
} else {
userRepository.insert(User(null, login, password, 3))
_successState.postValue(true)
}
}
}
}
}

View File

@ -0,0 +1,83 @@
package com.example.mobileapp.viewmodels
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import com.example.mobileapp.PreferencesManager
import com.example.mobileapp.models.OrderWithProducts
import com.example.mobileapp.models.Role
import com.example.mobileapp.models.User
import com.example.mobileapp.repositories.OrderRepository
import com.example.mobileapp.repositories.RoleRepository
import com.example.mobileapp.repositories.StatusRepository
import com.example.mobileapp.repositories.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.cancellable
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class UsersViewModel @Inject constructor(
private val roleRepository: RoleRepository,
private val userRepository: UserRepository
) : ViewModel() {
private val _roles = mutableStateListOf<String>()
val roles: List<String>
get() = _roles
init {
viewModelScope.launch {
roleRepository.getAll().collect{data ->
_roles.addAll(data.map { role -> role.name })
}
}
}
fun getUsersList() : Flow<PagingData<User>> {
return userRepository.getAllPaged()
}
suspend fun getUserRole(user: User) : String {
return roleRepository.getById(user.roleId).first().name
}
fun changeUserRole(userId: Int, roleName: String) {
viewModelScope.launch {
val user = userRepository.getById(userId).first()
when(roleName){
"user" -> {
val newUser = user
user.roleId = roleRepository.getByName("user").first().id!!
userRepository.update(newUser)
}
"worker" -> {
val newUser = user
user.roleId = roleRepository.getByName("worker").first().id!!
userRepository.update(newUser)
}
"admin" -> {
val newUser = user
user.roleId = roleRepository.getByName("admin").first().id!!
userRepository.update(newUser)
}
}
}
}
fun deleteUser(userId: Int){
viewModelScope.launch{
userRepository.delete(userRepository.getById(userId).first())
}
}
}

View File

@ -0,0 +1,77 @@
package com.example.mobileapp.widgets
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.mobileapp.R
@Composable
fun Header(backColor: Color){
Column {
Card(
shape = RoundedCornerShape(bottomStart = 30.dp, bottomEnd = 30.dp),
border = BorderStroke(4.dp, Color(222,161,69)),
modifier = Modifier
.fillMaxWidth()
.background(backColor),
colors = CardDefaults.cardColors(
containerColor = Color(255, 186, 83)
)
) {
Row (
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
Text(
text = "Вкусно",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
modifier = Modifier
.padding(top = 20.dp, bottom = 20.dp),
textAlign = TextAlign.Center,
)
Image(
painterResource(
id = R.drawable.icon
),
contentDescription = "Logo",
modifier = Modifier
.size(60.dp, 60.dp),
)
Text(
text = "ладно",
color = Color.Black,
fontFamily = FontFamily(Font(R.font.nunito_extrabold_italic)),
fontSize = 34.sp,
modifier = Modifier
.padding(top = 20.dp, bottom = 20.dp),
textAlign = TextAlign.Center,
)
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">MobileApp</string>
</resources>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.MobileApp" parent="android:Theme.Material.Light.NoActionBar" />
</resources>

Some files were not shown because too many files have changed in this diff Show More