Compare commits

...

5 Commits

59 changed files with 1744 additions and 826 deletions

View File

@ -9,42 +9,54 @@
<option name="autoReloadType" value="NONE" />
</component>
<component name="ChangeListManager">
<list default="true" id="7c94e195-a540-483e-9a1c-11797aeb1741" name="Changes" comment="empty activity test">
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Basket/BasketItem.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/List_of_Services/AddService.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/List_of_Services/Service.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Orders/OrderItem.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Orders/Orders.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Profile/Login.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Profile/ProfileChange.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Profile/ProfileNotAuth.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Profile/Registration.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/TestServiceItem.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/UIComponents/MyTextField.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/res/drawable/icon_calendar.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/res/drawable/image_service.png" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/res/drawable/upload.xml" afterDir="false" />
<list default="true" id="7c94e195-a540-483e-9a1c-11797aeb1741" name="Changes" comment="feature&amp;fix: lab4 add viewmodel, fix repos">
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/AppContainer.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/AppDataContainer.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/MainActivity.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/viewmodel/AppViewModelProvider.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/app/build.gradle.kts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/AndroidManifest.xml" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/AndroidManifest.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/App.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/App.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Basket/Basket.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Basket/Basket.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/List_of_Services/ListOfServices.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/List_of_Services/ListOfServices.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Navbar/NavBar.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Navbar/NavBar.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Navbar/NavController.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Navbar/NavController.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Navbar/NavItem.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Navbar/NavItem.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Profile/Profile.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/Profile/Profile.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/UIComponents/Button.kt" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/UIComponents/SearchBar.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/UIComponents/SearchBar.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/ui/theme/Color.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/ui/theme/Color.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/ui/theme/Theme.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/ui/theme/Theme.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/ui/theme/Type.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/ui/theme/Type.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Basket/Basket.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Basket/Basket.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Basket/BasketItemUI.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Basket/BasketItemUI.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/AddService.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/AddService.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/ListOfServices.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/ListOfServices.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/Service.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/Service.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavController.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavController.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavItem.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavItem.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Orders/Orders.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Orders/Orders.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Login.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Login.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Profile.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Profile.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/ProfileChange.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/ProfileChange.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/ProfileNotAuth.kt" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Registration.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Registration.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/UIComponents/MyTextField.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/UIComponents/MyTextField.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/dao/BasketDao.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/dao/BasketDao.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/dao/OrderDao.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/dao/OrderDao.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/dao/ServiceDao.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/dao/ServiceDao.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/dao/UserDao.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/dao/UserDao.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/database/AppDatabase.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/database/AppDatabase.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/Basket.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/Basket.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/BasketItem.kt" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/BasketService.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/BasketService.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/OrderService.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/OrderService.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/Service.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/Service.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/User.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/User.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/repository/BasketRepository.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/repository/BasketRepository.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/repository/OrderRepository.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/repository/OrderRepository.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/repository/ServiceRepository.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/repository/ServiceRepository.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/viewmodel/BasketViewModel.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/viewmodel/BasketViewModel.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/viewmodel/OrderViewModel.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/viewmodel/OrderViewModel.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/viewmodel/ServiceViewModel.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/viewmodel/ServiceViewModel.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/viewmodel/UserViewModel.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/viewmodel/UserViewModel.kt" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ExecutionTargetManager" SELECTED_TARGET="device_and_snapshot_combo_box_target[C:\Users\Danil\.android\avd\Pixel_3a_API_34_extension_level_7_x86_64.avd]" />
<component name="ExecutionTargetManager" SELECTED_TARGET="device_and_snapshot_combo_box_target[adb-6681ed71-4kOMN8._adb-tls-connect._tcp]" />
<component name="ExternalProjectsData">
<projectState path="$PROJECT_DIR$">
<ProjectState />
@ -53,8 +65,8 @@
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Class" />
<option value="resourceFile" />
<option value="Class" />
<option value="Kotlin Class" />
</list>
</option>
@ -67,11 +79,14 @@
<component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY">
<map>
<entry key="$PROJECT_DIR$" value="main" />
<entry key="$PROJECT_DIR$" value="LabWork03" />
</map>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="KotlinCodeInsightWorkspaceSettings">
<option name="optimizeImportsOnTheFly" value="true" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
@ -80,28 +95,37 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;ApkExportedModule&quot;: &quot;My_Application.app&quot;,
&quot;ExportApk.ApkPathForMy_Application.app&quot;: &quot;C:\\Users\\Danil\\Desktop\\MDP\\labs\\app&quot;,
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;com.android.tools.idea.devicemanager.tab&quot;: &quot;Physical&quot;,
&quot;last_opened_file_path&quot;: &quot;C:/Users/Danil/Downloads/test&quot;,
&quot;project.structure.last.edited&quot;: &quot;Build Variants&quot;,
&quot;project.structure.proportion&quot;: &quot;0.17&quot;,
&quot;project.structure.side.proportion&quot;: &quot;0.2&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;trusted.hosts&quot;
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ApkExportedModule": "My_Application.app",
"ExportApk.ApkPathForMy_Application.app": "C:\\Users\\Danil\\Desktop\\MDP\\labs\\app",
"PROJECT_TRUSTED_KEY": "true",
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.cidr.known.project.marker": "true",
"cidr.known.project.marker": "true",
"com.android.tools.idea.devicemanager.tab": "Physical",
"last_opened_file_path": "C:/Users/Danil/Downloads/test",
"project.structure.last.edited": "Modules",
"project.structure.proportion": "0.17",
"project.structure.side.proportion": "0.2",
"settings.editor.selected.configurable": "emulator"
},
&quot;keyToStringList&quot;: {
&quot;ExportApk.BuildVariants&quot;: [
&quot;release&quot;
"keyToStringList": {
"ExportApk.BuildVariants": [
"release"
],
"com.android.tools.idea.sqlite.queryhistory": [
"insert into tbl_user values (1, \"Danil\", \"Markov\", \"danil@mail.ru\", \"123\", \"ADMIN\", null)",
"delete from tbl_user where userId = 6",
"delete from tbl_user where userId = 4",
"delete from tbl_user where userId = 2"
]
}
}</component>
}]]></component>
<component name="PsdUISettings">
<option name="MODULE_TAB" value="Signing Configs" />
<option name="LAST_EDITED_SIGNING_CONFIG" value="debug" />
<option name="LAST_EDITED_BUILD_TYPE" value="release" />
</component>
<component name="RecentsManager">
@ -113,7 +137,7 @@
<recent name="C:\Users\Danil\Desktop\MDP\labs\app\src\main\java\com\example\myapplication\UIComponents" />
</key>
</component>
<component name="RunManager" selected="Android App.App">
<component name="RunManager" selected="Android App.app">
<configuration name="App" type="AndroidRunConfigurationType" factoryName="Android App" temporary="true">
<module name="My_Application.app.main" />
<option name="DEPLOY" value="true" />
@ -246,8 +270,16 @@
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="build.gradle.kts" type="KotlinStandaloneScriptRunConfigurationType" temporary="true" nameIsGenerated="true">
<module name="My_Application" />
<option name="filePath" value="$PROJECT_DIR$/app/build.gradle.kts" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<recent_temporary>
<list>
<item itemvalue="Kotlin script (Beta).build.gradle.kts" />
<item itemvalue="Android App.App" />
</list>
</recent_temporary>
@ -275,7 +307,42 @@
<option name="project" value="LOCAL" />
<updated>1696755925297</updated>
</task>
<option name="localTasksCounter" value="3" />
<task id="LOCAL-00003" summary="feature: UI, lab2 maybe done">
<created>1698677204522</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1698677204522</updated>
</task>
<task id="LOCAL-00004" summary="feature: lab3 is done">
<created>1699943645305</created>
<option name="number" value="00004" />
<option name="presentableId" value="LOCAL-00004" />
<option name="project" value="LOCAL" />
<updated>1699943645305</updated>
</task>
<task id="LOCAL-00005" summary="feature: lab3 is done">
<created>1699948377847</created>
<option name="number" value="00005" />
<option name="presentableId" value="LOCAL-00005" />
<option name="project" value="LOCAL" />
<updated>1699948377847</updated>
</task>
<task id="LOCAL-00006" summary="feature: lab4 add repos">
<created>1700908930838</created>
<option name="number" value="00006" />
<option name="presentableId" value="LOCAL-00006" />
<option name="project" value="LOCAL" />
<updated>1700908930838</updated>
</task>
<task id="LOCAL-00007" summary="feature&amp;fix: lab4 add viewmodel, fix repos">
<created>1700915139985</created>
<option name="number" value="00007" />
<option name="presentableId" value="LOCAL-00007" />
<option name="project" value="LOCAL" />
<updated>1700915139985</updated>
</task>
<option name="localTasksCounter" value="8" />
<servers />
</component>
<component name="Vcs.Log.Tabs.Properties">
@ -291,6 +358,36 @@
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="empty activity test" />
<option name="LAST_COMMIT_MESSAGE" value="empty activity test" />
<MESSAGE value="feature: UI, lab2 maybe done" />
<MESSAGE value="feature: lab3 is done" />
<MESSAGE value="feature: lab4 add repos" />
<MESSAGE value="feature&amp;fix: lab4 add viewmodel, fix repos" />
<option name="LAST_COMMIT_MESSAGE" value="feature&amp;fix: lab4 add viewmodel, fix repos" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="kotlin-line">
<url>file://$PROJECT_DIR$/app/src/main/java/com/example/myapplication/viewmodel/OrderViewModel.kt</url>
<line>38</line>
<option name="timeStamp" value="18" />
</line-breakpoint>
<line-breakpoint enabled="true" type="kotlin-line">
<url>file://$PROJECT_DIR$/app/src/main/java/com/example/myapplication/viewmodel/OrderViewModel.kt</url>
<line>36</line>
<option name="timeStamp" value="19" />
</line-breakpoint>
<line-breakpoint enabled="true" type="kotlin-line">
<url>file://$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavController.kt</url>
<line>63</line>
<option name="timeStamp" value="23" />
</line-breakpoint>
<line-breakpoint enabled="true" type="kotlin-line">
<url>file://$PROJECT_DIR$/app/src/main/java/com/example/myapplication/viewmodel/ServiceViewModel.kt</url>
<line>30</line>
<option name="timeStamp" value="25" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
</project>

View File

@ -1,6 +1,7 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id ("kotlin-kapt")
}
android {
@ -9,7 +10,7 @@ android {
defaultConfig {
applicationId = "com.example.myapplication"
minSdk = 29
minSdk = 26
targetSdk = 34
versionCode = 1
versionName = "1.0"
@ -30,11 +31,11 @@ android {
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "17"
}
buildFeatures {
compose = true
@ -49,26 +50,44 @@ android {
}
}
dependencies {
kotlin {
jvmToolchain(17)
}
implementation("androidx.core:core-ktx:1.9.0")
apply(plugin = "kotlin-kapt")
dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("androidx.activity:activity-compose:1.7.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")
//
implementation("androidx.compose.material:material:1.5.2")
implementation("androidx.navigation:navigation-compose:2.7.3")
implementation("androidx.compose.material:material")
implementation("androidx.navigation:navigation-runtime-ktx:2.7.5")
implementation("androidx.compose.material3:material3:1.1.2")
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.activity:activity-ktx:1.8.0")
implementation ("androidx.fragment:fragment-ktx:1.6.2")
implementation ("io.coil-kt:coil-compose:1.4.0")
implementation ("com.google.code.gson:gson:2.9.0")
implementation("androidx.navigation:navigation-compose:2.7.5")
implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0-beta01")
implementation ("androidx.paging:paging-compose:3.2.1")
implementation ("androidx.paging:paging-runtime:3.2.1")
//ROOM
val room_version = "2.5.2"
implementation("androidx.room:room-runtime:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version")
kapt("androidx.room:room-compiler:$room_version")
implementation("androidx.room:room-ktx:$room_version")
implementation("androidx.room:room-paging:$room_version")
}

View File

@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:name=".App"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
@ -14,7 +15,7 @@
android:theme="@style/Theme.MyApplication"
tools:targetApi="31">
<activity
android:name=".App"
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.MyApplication">

View File

@ -1,33 +1,12 @@
package com.example.myapplication
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.SnackbarDefaults.backgroundColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.darkColorScheme
import androidx.compose.ui.Modifier
import com.example.myapplication.Navbar.NavBar
import com.example.myapplication.ui.theme.AppTheme
import com.example.myapplication.ui.theme.BlueMain
import android.app.Application
class App : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme (darkTheme = false){
// A surface container using the 'background' color from the theme
class App : Application() {
lateinit var container: AppContainer
Surface(
modifier = Modifier.fillMaxSize()
.background(BlueMain),
) {
NavBar();
}
}
}
override fun onCreate() {
super.onCreate()
container = AppDataContainer(this)
}
}
}

View File

@ -0,0 +1,13 @@
package com.example.myapplication
import com.example.myapplication.repository.BasketRepository
import com.example.myapplication.repository.OrderRepository
import com.example.myapplication.repository.ServiceRepository
import com.example.myapplication.repository.UserRepository
interface AppContainer {
val serviceRepo: ServiceRepository
val userRepo: UserRepository
val orderRepo: OrderRepository
val basketRepo: BasketRepository
}

View File

@ -0,0 +1,15 @@
package com.example.myapplication
import android.content.Context
import com.example.myapplication.database.AppDatabase
import com.example.myapplication.repository.BasketRepository
import com.example.myapplication.repository.OrderRepository
import com.example.myapplication.repository.ServiceRepository
import com.example.myapplication.repository.UserRepository
class AppDataContainer(context: Context) : AppContainer {
override val serviceRepo = ServiceRepository(AppDatabase.getInstance(context).serviceDao())
override val userRepo = UserRepository(AppDatabase.getInstance(context).userDao())
override val orderRepo = OrderRepository(AppDatabase.getInstance(context).orderDao())
override val basketRepo = BasketRepository(AppDatabase.getInstance(context).basketDao())
}

View File

@ -1,43 +0,0 @@
package com.example.myapplication.List_of_Services
import SearchBar
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.example.myapplication.getServices
import com.example.myapplication.ui.theme.BlueMain
@Composable
fun ListOfServices(navController: NavHostController){
Column(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(bottom = 60.dp)
){
SearchBar(
modifier = Modifier)
{
searchText ->
//TODO search logic
}
LazyColumn(modifier = Modifier.padding(15.dp, 0.dp)){
var serviceList = getServices()
itemsIndexed(serviceList){_, item ->
Service(item)
}
}
}
}

View File

@ -1,131 +0,0 @@
package com.example.myapplication.List_of_Services
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.example.myapplication.TestServiceItem
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextPrimary
import com.example.myapplication.ui.theme.TextSecondary
import java.security.Provider.Service
@Composable
fun Service(item: TestServiceItem){
Box(
modifier = Modifier
.padding(0.dp, 0.dp, 0.dp, 10.dp)
.height(150.dp)
.shadow(
elevation = 4.dp,
shape = RoundedCornerShape(15.dp),
clip = false
),
){
Row(
modifier = Modifier
.fillMaxWidth()
.height(145.dp)
.background(color = Color.White, RoundedCornerShape(15.dp))
.padding(15.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
){
Image(
painter = painterResource(id = item.image),
contentDescription = null,
modifier = Modifier
.fillMaxHeight()
.heightIn(min = 100.dp)
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
contentScale = ContentScale.FillHeight,
)
Column(
modifier = Modifier
.fillMaxHeight()
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
verticalArrangement = Arrangement.Top,
){
var animalsString = ""
item.name?.let {
Text(
text = it,
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium)
}
item.animals?.forEach { animal -> animalsString += "$animal, " }
Text(
text = animalsString,
color = TextSecondary,
style = MaterialTheme.typography.bodyMedium
)
}
Column(
modifier = Modifier
.fillMaxHeight()
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally,
){
Text(
text = "$${item.price}",
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium,
)
Button(
onClick = { /*TODO*/ },
modifier = Modifier
.size(42.dp)
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
){
Text(text = "+")
}
}
}
}
}

View File

@ -0,0 +1,53 @@
package com.example.myapplication
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import com.example.myapplication.composeui.Navbar.NavBar
import com.example.myapplication.model.User
import com.example.myapplication.ui.theme.AppTheme
import com.example.myapplication.ui.theme.BlueMain
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme (darkTheme = false){
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier
.fillMaxSize()
.background(BlueMain),
) {
NavBar();
}
}
}
}
}
class GlobalUser private constructor() {
private var user: User? = null
fun setUser(user: User?) {
this.user = user
}
fun getUser(): User? {
return user
}
companion object {
private var instance: GlobalUser? = null
fun getInstance(): GlobalUser {
return instance ?: synchronized(this) {
instance ?: GlobalUser().also { instance = it }
}
}
}
}

View File

@ -1,86 +0,0 @@
package com.example.myapplication.Navbar
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.example.myapplication.Basket.Basket
import com.example.myapplication.List_of_Services.AddService
import com.example.myapplication.List_of_Services.ListOfServices
import com.example.myapplication.Orders.Orders
import com.example.myapplication.Profile.Login
import com.example.myapplication.Profile.Profile
import com.example.myapplication.Profile.ProfileChange
import com.example.myapplication.Profile.ProfileNotAuth
import com.example.myapplication.Profile.Registration
@Composable
fun NavController(navController : NavHostController){
NavHost(
navController = navController,
startDestination = NavItem.Profile.route
){
composable(
NavItem.ListOfServices.route
){
ListOfServices(navController)
}
composable(
// "${NavItem.Basket.route}/{userId}"
"${NavItem.Basket.route}"
){
Basket(navController)
// backStackEntry ->
// backStackEntry.arguments?.getString("userId")?.let {
// Basket(
// navController,
// it.toLong()
// )
// }
}
composable(
// "${NavItem.Profile.route}/{userId}"
"${NavItem.Profile.route}"
){
Profile(navController)
// backStackEntry ->
// backStackEntry.arguments?.getString("userId")?.let {
// Profile(
// navController,
// it.toLong()
// )
// }
}
composable(
"${NavItem.ProfileChange.route}"
){
ProfileChange(navController)
}
composable(
"${NavItem.Orders.route}"
){
Orders(navController)
}
composable(
"${NavItem.ProfileNotAuth.route}"
){
ProfileNotAuth(navController)
}
composable(
"${NavItem.Login.route}"
){
Login(navController)
}
composable(
"${NavItem.Registration.route}"
){
Registration(navController)
}
composable(
"${NavItem.AddService.route}"
){
AddService(navController)
}
}
}

View File

@ -1,69 +0,0 @@
package com.example.myapplication.Orders
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.modifier.modifierLocalConsumer
import androidx.compose.ui.unit.dp
import com.example.myapplication.ui.theme.TextPrimary
import com.example.myapplication.ui.theme.TextSecondary
import java.util.Date
@Composable
fun OrderItem (orderId: Int, date: String, price: Double, list: String){
Column(modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(15.dp))){
Column (
modifier = Modifier
.padding(0.dp, 0.dp, 0.dp, 0.dp)
.shadow(
elevation = 4.dp,
shape = RoundedCornerShape(15.dp),
clip = false
)
){
Row (
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.background(Color.White)
.fillMaxWidth()
.padding(horizontal = 15.dp, vertical = 10.dp),
){
Text(text = "Order:$orderId", modifier = Modifier.weight(1f), style = MaterialTheme.typography.bodyMedium)
Text(text = "$price$", style = MaterialTheme.typography.bodyMedium)
}
Row (
modifier = Modifier
.background(Color.White)
.fillMaxWidth()
.padding(horizontal = 15.dp),
){
Text(text = date, style = MaterialTheme.typography.bodyMedium)
}
}
Row (
modifier = Modifier
.background(Color.White)
.fillMaxWidth()
.padding(15.dp)
){
Text(text = list, style = MaterialTheme.typography.bodyMedium.copy(TextSecondary))
}
}
}

View File

@ -1,27 +0,0 @@
package com.example.myapplication.Orders
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.example.myapplication.ui.theme.BlueMain
import java.util.Date
@Composable
fun Orders (navController: NavController){
Column (
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
){
OrderItem(orderId = 1, date = "11.11.2023", price = 40.0, list = "Inoculation x2")
}
}

View File

@ -1,81 +0,0 @@
package com.example.myapplication.Profile
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.example.myapplication.Navbar.NavItem
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
@Composable
fun ProfileNotAuth (navController: NavController){
Column(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
){
Row (
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(bottom = 15.dp)
){
Button(
onClick = { navController.navigate(NavItem.Login.route) },
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(
text = "Login",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White)
)
}
}
Button(
onClick = { navController.navigate(NavItem.Registration.route) },
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(
text = "Registration",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White)
)
}
}
}

View File

@ -2,7 +2,6 @@ package com.example.myapplication
import android.os.Parcel
import android.os.Parcelable
import kotlin.collections.ArrayList
data class TestServiceItem(
val image: Int,
@ -43,17 +42,8 @@ data class TestServiceItem(
fun getServices(): List<TestServiceItem>{
return listOf(
TestServiceItem(R.drawable.image_service, "Inoculation", arrayListOf("Dogs", "Cats","Dogs", "Cats","Dogs", "Cats","Dogs", "Cats","Dogs", "Cats","Dogs", "Cats","Dogs", "Cats",), 20.0),
TestServiceItem(R.drawable.image_service, "Inoculation", arrayListOf("Dogs", "Cats"), 20.0),
TestServiceItem(R.drawable.image_service, "Inoculation", arrayListOf("Dogs", "Cats"), 20.0),
TestServiceItem(R.drawable.image_service, "Inoculation", arrayListOf("Dogs", "Cats"), 20.0),
TestServiceItem(R.drawable.image_service, "Inoculation", arrayListOf("Dogs", "Cats"), 20.0),
TestServiceItem(R.drawable.image_service, "Inoculation", arrayListOf("Dogs", "Cats"), 20.0),
TestServiceItem(R.drawable.image_service, "Inoculation", arrayListOf("Dogs", "Cats"), 20.0),
TestServiceItem(R.drawable.image_service, "Inoculation", arrayListOf("Dogs", "Cats"), 20.0),
TestServiceItem(R.drawable.image_service, "Inoculation", arrayListOf("Dogs", "Cats"), 20.0),
TestServiceItem(R.drawable.image_service, "Inoculation", arrayListOf("Dogs", "Cats"), 20.0),
TestServiceItem(R.drawable.image_service, "Inoculation", arrayListOf("Dogs", "Cats"), 20.0),
TestServiceItem(R.drawable.image_service, "Inoculation", arrayListOf("Dogs", "Cats"), 20.0),
)
}

View File

@ -1,8 +1,6 @@
package com.example.myapplication.Basket
package com.example.myapplication.composeui.Basket
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@ -12,11 +10,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
@ -24,21 +17,34 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.example.myapplication.R
import com.example.myapplication.TestServiceItem
import com.example.myapplication.getServices
import com.example.myapplication.GlobalUser
import com.example.myapplication.composeui.Profile.Login
import com.example.myapplication.model.Service
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.viewmodel.AppViewModelProvider
import com.example.myapplication.viewmodel.BasketViewModel
import com.example.myapplication.viewmodel.OrderViewModel
@Composable
fun Basket(navController : NavHostController){
fun Basket(navController : NavHostController, basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory), orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val user = GlobalUser.getInstance().getUser()
if (user == null){
Login(navController = navController)
}else{
basketViewModel.updateSubTotal(user.userId!!)
val total = basketViewModel.total.value
val basketList by basketViewModel.getBasketServices(user.userId).collectAsState(initial = null)
Column(
modifier = Modifier
.fillMaxSize()
@ -47,17 +53,11 @@ fun Basket(navController : NavHostController){
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = R.drawable.icon_calendar),
contentDescription = null,
modifier = Modifier
.padding(5.dp)
)
LazyColumn(){
val items = listOf<TestServiceItem>(getServices()[1])
itemsIndexed(items){
_, item ->
BasketItem(item = item)
val serviceList: List<Service>? = basketList?.services
if (serviceList != null){
orderViewModel.updateSelectedItems(serviceList)
for (item in serviceList){
BasketItemUI(item = item)
}
}
Box(modifier = Modifier
@ -75,31 +75,20 @@ fun Basket(navController : NavHostController){
horizontalArrangement = Arrangement.SpaceBetween,
){
Text(
text = "Total:",
text = "Total: ",
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.weight(1f)
)
Text(
text = "40$",
style = MaterialTheme.typography.bodyMedium
)
}
Row (
horizontalArrangement = Arrangement.SpaceBetween,
){
Text(
text = "Date:",
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.weight(1f)
)
Text(
text = "11.11.2023:",
text = "$$total",
style = MaterialTheme.typography.bodyMedium
)
}
}
Button(
onClick = { /*TODO*/ },
onClick = {
orderViewModel.createOrder()
},
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
@ -115,4 +104,5 @@ fun Basket(navController : NavHostController){
}
}
}
}
}

View File

@ -1,4 +1,4 @@
package com.example.myapplication.Basket
package com.example.myapplication.composeui.Basket
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
@ -13,21 +13,19 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
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.draw.clip
@ -36,15 +34,29 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.myapplication.TestServiceItem
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.myapplication.GlobalUser
import com.example.myapplication.model.Service
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextPrimary
import com.example.myapplication.ui.theme.TextSecondary
import com.example.myapplication.viewmodel.AppViewModelProvider
import com.example.myapplication.viewmodel.BasketViewModel
import com.example.myapplication.viewmodel.OrderViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun BasketItem(item: TestServiceItem){
fun BasketItemUI(item: Service, basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory), orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
val userId = GlobalUser.getInstance().getUser()?.userId!!
val basketStateId = remember { mutableIntStateOf(0) }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
basketStateId.value = basketViewModel.getUsersBasket(userId).basketId!!
}
}
val quantityState by basketViewModel.getQuantityState(basketStateId.value, item.serviceId!!).collectAsState()
Box(
modifier = Modifier
.padding(0.dp, 0.dp, 0.dp, 10.dp)
@ -54,7 +66,7 @@ fun BasketItem(item: TestServiceItem){
shape = RoundedCornerShape(15.dp),
clip = false
),
){
) {
Row(
modifier = Modifier
.fillMaxWidth()
@ -63,34 +75,28 @@ fun BasketItem(item: TestServiceItem){
.padding(15.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
){
Image(
painter = painterResource(id = item.image),
contentDescription = null,
modifier = Modifier
.fillMaxHeight()
.heightIn(min = 100.dp)
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
contentScale = ContentScale.FillHeight,
)
) {
item.photo?.let { painterResource(id = it) }?.let {
Image(
painter = it,
contentDescription = null,
modifier = Modifier
.fillMaxHeight()
.heightIn(min = 100.dp)
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
contentScale = ContentScale.FillHeight,
)
}
Column(
modifier = Modifier
.fillMaxHeight()
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
verticalArrangement = Arrangement.Top,
){
var animalsString = ""
item.name?.let {
Text(
text = it,
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium)
}
item.animals?.forEach { animal -> animalsString += "$animal, " }
) {
Text(
text = animalsString,
color = TextSecondary,
text = item.name,
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium
)
}
@ -101,21 +107,22 @@ fun BasketItem(item: TestServiceItem){
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally,
){
) {
Text(
text = "$${item.price}",
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium
)
Row(verticalAlignment = Alignment.CenterVertically){
var num by remember { mutableIntStateOf(5) }
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = num.toString(),
text = "$quantityState",
color = TextPrimary
)
Column(verticalArrangement = Arrangement.SpaceAround){
Column(verticalArrangement = Arrangement.SpaceAround) {
Button(
onClick = { num+=1 },
onClick = {
basketViewModel.incrementQuantity(basketStateId.intValue, item.serviceId!!)
},
modifier = Modifier
.size(42.dp)
.fillMaxWidth()
@ -129,7 +136,12 @@ fun BasketItem(item: TestServiceItem){
Text(text = "+")
}
Button(
onClick = { num-=1 },
onClick = {
if (quantityState == 1) basketViewModel.deleteServiceFromBasket(basketStateId.intValue,
item.serviceId!!
)
basketViewModel.decrementQuantity(basketStateId.intValue, item.serviceId!!)
},
modifier = Modifier
.size(42.dp)
.fillMaxWidth()

View File

@ -1,12 +1,5 @@
package com.example.myapplication.List_of_Services
package com.example.myapplication.composeui.List_of_Services
import android.content.ContentResolver
import android.content.Intent
import android.graphics.BitmapFactory
import android.provider.MediaStore
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
@ -20,55 +13,45 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ExposedDropdownMenuBox
import androidx.compose.material.ExposedDropdownMenuDefaults
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.UIComponents.MyTextField
import com.example.myapplication.composeui.Navbar.NavItem
import com.example.myapplication.composeui.UIComponents.MyTextField
import com.example.myapplication.model.Service
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextPrimary
import com.example.myapplication.ui.theme.TextSecondary
import com.example.myapplication.viewmodel.AppViewModelProvider
import com.example.myapplication.viewmodel.ServiceViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
@Composable
fun AddService (navController: NavController){
var serviceName = ""
var price = ""
var animals = ""
val availableAnimals = listOf("Dogs", "Cats")
var expanded by remember { mutableStateOf(false) }
fun AddService (navController: NavController, service: Service, serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val create = service.serviceId == null
LaunchedEffect(Dispatchers.Default){
if(!create){
serviceViewModel.service.value.serviceId = service.serviceId
serviceViewModel.service.value.photo = service.photo
}
}
Column (
modifier = Modifier
.fillMaxSize()
@ -96,15 +79,29 @@ fun AddService (navController: NavController){
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
){
/* Image(
bitmap = selectedImage!!,
contentDescription = null,
modifier = Modifier
.fillMaxHeight()
.heightIn(min = 100.dp)
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
contentScale = ContentScale.FillHeight,
)*/
if(create){
Image(
painter = painterResource(id = serviceViewModel.photo.intValue),
contentDescription = null,
modifier = Modifier
.fillMaxHeight()
.heightIn(min = 100.dp)
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
contentScale = ContentScale.FillHeight,
)
}else{
service.photo?.let { painterResource(id = it) }?.let {
Image(
painter = it,
contentDescription = null,
modifier = Modifier
.fillMaxHeight()
.heightIn(min = 100.dp)
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
contentScale = ContentScale.FillHeight,
)
}
}
Column(
modifier = Modifier
@ -112,19 +109,17 @@ fun AddService (navController: NavController){
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
verticalArrangement = Arrangement.Top,
){
var animalsString = ""
serviceName?.let {
if(create){
Text(
text = it,
text = serviceViewModel.name.value,
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium)
}else{
Text(
text = service.name,
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium)
}
animals?.forEach { animal -> animalsString += "$animal, " }
Text(
text = animalsString,
color = TextSecondary,
style = MaterialTheme.typography.bodyMedium
)
}
Column(
modifier = Modifier
@ -133,37 +128,42 @@ fun AddService (navController: NavController){
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally,
){
Text(
text = "${price}$",
style = MaterialTheme.typography.bodyMedium,
)
if(create){
Text(
text = serviceViewModel.price.doubleValue.toString(),
style = MaterialTheme.typography.bodyMedium,
)
}else{
Text(
text = service.price.toString(),
style = MaterialTheme.typography.bodyMedium,
)
}
}
}
}
Column (
){
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Service name"){
newValue ->
serviceName = newValue
}
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
availableAnimals.forEach {
DropdownMenuItem(onClick = { animals += "$it, " }) {
Text(it)
}
if(create){
MyTextField(label = "Service name"){
serviceViewModel.name.value = it
}
}else{
MyTextField(label = service.name){
serviceViewModel.service.value.name = it
}
}
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Price"){
newValue ->
price = newValue
if(create){
MyTextField(label = "Price"){
serviceViewModel.price.doubleValue = it.toDouble()
}
}else{
MyTextField(label = service.price.toString()){
serviceViewModel.service.value.price = it.toDouble()
}
}
}
}
@ -187,7 +187,11 @@ fun AddService (navController: NavController){
}
Button(
onClick = {
if (create)
serviceViewModel.createService()
else
serviceViewModel.service.let { serviceViewModel.updateService() }
navController.navigate(NavItem.ListOfServices.route)
},
modifier = Modifier
.height(60.dp)
@ -200,15 +204,11 @@ fun AddService (navController: NavController){
),
contentPadding = PaddingValues(0.dp),
) {
Text(text = "Add service", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
if(create) {
Text(text = "Add service", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
}else{
Text(text = "Update service", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
}
}
}
}
private suspend fun loadSelectedImage(uri: android.net.Uri, contentResolver: ContentResolver): ImageBitmap {
return withContext(Dispatchers.IO) {
val inputStream = contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(inputStream)
bitmap.asImageBitmap()
}
}

View File

@ -0,0 +1,49 @@
package com.example.myapplication.composeui.List_of_Services
import SearchBar
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.myapplication.model.Service
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.viewmodel.AppViewModelProvider
import com.example.myapplication.viewmodel.ServiceViewModel
@Composable
fun ListOfServices(navController: NavHostController, serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)){
Column(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(bottom = 60.dp)
){
SearchBar(
modifier = Modifier)
{
searchText ->
//TODO search logic
}
val services = serviceViewModel.serviceList.collectAsLazyPagingItems()
LazyColumn(modifier = Modifier.padding(15.dp, 0.dp)){
items(
count = services.itemCount,
key = services.itemKey { service -> service.serviceId!! }
){
index: Int ->
val service: Service? = services[index]
if (service != null){
Service(navController, item = service)
}
}
}
}
}

View File

@ -0,0 +1,171 @@
package com.example.myapplication.composeui.List_of_Services
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Create
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.example.myapplication.GlobalUser
import com.example.myapplication.model.BasketService
import com.example.myapplication.model.RoleEnum
import com.example.myapplication.model.Service
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextPrimary
import com.example.myapplication.viewmodel.AppViewModelProvider
import com.example.myapplication.viewmodel.BasketViewModel
import com.example.myapplication.viewmodel.ServiceViewModel
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@Composable
fun Service(navController: NavHostController, item: Service, basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory), serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val user = GlobalUser.getInstance().getUser()
Box(
modifier = Modifier
.padding(0.dp, 0.dp, 0.dp, 10.dp)
.height(150.dp)
.shadow(
elevation = 4.dp,
shape = RoundedCornerShape(15.dp),
clip = false
),
){
Row(
modifier = Modifier
.fillMaxWidth()
.height(145.dp)
.background(color = Color.White, RoundedCornerShape(15.dp))
.padding(15.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
){
item.photo?.let { painterResource(id = it) }?.let {
Image(
painter = it,
contentDescription = null,
modifier = Modifier
.fillMaxHeight()
.heightIn(min = 100.dp)
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
contentScale = ContentScale.FillHeight,
)
}
Column(
modifier = Modifier
.fillMaxHeight()
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
verticalArrangement = Arrangement.Top,
){
Text(
text = item.name,
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium)
if(user?.role == RoleEnum.Admin){
Button(
onClick = {
runBlocking {
launch(Dispatchers.Default){
serviceViewModel.deleteService(item)
}
}
},
modifier = Modifier
.size(42.dp)
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
){
Icon(imageVector = Icons.Default.Delete, contentDescription = null)
}
}
}
Column(
modifier = Modifier
.fillMaxHeight()
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally,
){
Text(
text = "$${item.price}",
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium,
)
Button(
onClick = {
runBlocking{
launch(Dispatchers.Default){
basketViewModel.addToBasket(BasketService(basketViewModel.getUsersBasket(user?.userId!!).basketId!!, item.serviceId!!, 1))
}
}
},
modifier = Modifier
.size(42.dp)
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
){
Text(text = "+")
}
if(user?.role == RoleEnum.Admin){
Button(
onClick = {
serviceViewModel.service.value = item
navController.navigate("add_service/${Gson().toJson(item)}")
},
modifier = Modifier
.size(42.dp)
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
){
Icon(imageVector = Icons.Default.Create, contentDescription = null)
}
}
}
}
}
}

View File

@ -1,4 +1,4 @@
package com.example.myapplication.Navbar
package com.example.myapplication.composeui.Navbar
import android.annotation.SuppressLint
import android.content.Context
@ -67,26 +67,32 @@ fun NavBar(){
.clip(RoundedCornerShape(15.dp, 15.dp, 0.dp, 0.dp)),
){
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
val currentDestination = navBackStackEntry
items.forEach { screen ->
val isSelected = currentDestination?.destination?.route == screen.route
BottomNavigationItem(
selected = isSelected,
icon = {
Icon(painterResource(screen.icon),
null,
modifier = Modifier,
GreenBtn)
},
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
modifier = Modifier
.padding(15.dp),
onClick = {
navController.navigate(screen.route){
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
if (!isSelected) {
navController.graph.startDestinationRoute?.let {
navController.popBackStack(it, inclusive = true)
}
navController.navigate(screen.route) {
launchSingleTop
}
}
launchSingleTop = true
restoreState = true
navController.navigate(screen.route)
}
}
)

View File

@ -0,0 +1,70 @@
package com.example.myapplication.composeui.Navbar
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.example.myapplication.composeui.Basket.Basket
import com.example.myapplication.composeui.List_of_Services.AddService
import com.example.myapplication.composeui.List_of_Services.ListOfServices
import com.example.myapplication.composeui.Orders.Orders
import com.example.myapplication.composeui.Profile.Login
import com.example.myapplication.composeui.Profile.Profile
import com.example.myapplication.composeui.Profile.ProfileChange
import com.example.myapplication.composeui.Profile.Registration
import com.example.myapplication.model.Service
import com.google.gson.Gson
@Composable
fun NavController(navController : NavHostController){
NavHost(
navController = navController,
startDestination = NavItem.ListOfServices.route,
){
composable(
NavItem.ListOfServices.route
){
ListOfServices(navController)
}
composable(
NavItem.Basket.route
){
Basket(navController)
}
composable(
NavItem.Profile.route
){
Profile(navController)
}
composable(
NavItem.ProfileChange.route
){
ProfileChange(navController)
}
composable(
NavItem.Orders.route
){
Orders(navController)
}
composable(
NavItem.Login.route
){
Login(navController)
}
composable(
NavItem.Registration.route
){
Registration(navController)
}
composable(
NavItem.AddService.route
){
backStackEntry ->
val serviceItemString = backStackEntry.arguments?.getString("serviceItem")
val serviceItem = Gson().fromJson(serviceItemString, Service::class.java)
serviceItem?.let { AddService(navController, it)
}
}
}
}

View File

@ -1,10 +1,6 @@
package com.example.myapplication.Navbar
package com.example.myapplication.composeui.Navbar
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.vectorResource
import com.example.myapplication.R
sealed class NavItem(
@ -31,10 +27,6 @@ sealed class NavItem(
"orders",
R.drawable.icon_profile
)
object ProfileNotAuth : NavItem(
"profile_not_auth",
R.drawable.icon_profile
)
object Login : NavItem(
"login",
R.drawable.icon_profile
@ -44,7 +36,7 @@ sealed class NavItem(
R.drawable.icon_profile
)
object AddService : NavItem(
"add_service",
"add_service/{serviceItem}",
R.drawable.icon_list_of_services
)
}

View File

@ -0,0 +1,49 @@
package com.example.myapplication.composeui.Orders
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.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.example.myapplication.model.Order
import java.text.SimpleDateFormat
import java.util.Date
@Composable
fun OrderItem (order: Order){
val dateFormat = SimpleDateFormat("dd-MM-yyyy")
Column (
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
.clip(RoundedCornerShape(15.dp))
){
Row (
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.background(Color.White)
.fillMaxWidth()
.padding(horizontal = 15.dp, vertical = 10.dp),
){
Text(text = "Order:${order.orderId}", modifier = Modifier.weight(1f), style = MaterialTheme.typography.bodyMedium)
Text(text = "${order.total}", style = MaterialTheme.typography.bodyMedium)
}
Row (
modifier = Modifier
.background(Color.White)
.fillMaxWidth()
.padding(horizontal = 15.dp),
){
Text(text = dateFormat.format(Date(order.date)), style = MaterialTheme.typography.bodyMedium)
}
}
}

View File

@ -0,0 +1,49 @@
package com.example.myapplication.composeui.Orders
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.GlobalUser
import com.example.myapplication.model.Order
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.viewmodel.AppViewModelProvider
import com.example.myapplication.viewmodel.OrderViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun Orders (navController: NavController, orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val ordersList = remember { mutableStateListOf<Order>() }
val user = GlobalUser.getInstance().getUser()
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
orderViewModel.getUserOrders(user?.userId!!).collect { data ->
ordersList.clear()
ordersList.addAll(data.orders)
}
}
}
LazyColumn(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
){
itemsIndexed(ordersList){_, item ->
OrderItem(item)
}
}
}

View File

@ -1,4 +1,4 @@
package com.example.myapplication.Profile
package com.example.myapplication.composeui.Profile
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@ -16,6 +16,10 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
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.draw.clip
@ -23,15 +27,18 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.Navbar.NavItem
import com.example.myapplication.UIComponents.MyTextField
import com.example.myapplication.composeui.Navbar.NavItem
import com.example.myapplication.composeui.UIComponents.MyTextField
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextSecondary
import com.example.myapplication.viewmodel.AppViewModelProvider
import com.example.myapplication.viewmodel.UserViewModel
@Composable
fun Login (navController: NavController){
fun Login (navController: NavController, userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
Column(
modifier = Modifier
.fillMaxSize()
@ -55,19 +62,45 @@ fun Login (navController: NavController){
}
Column (
){
Row (modifier = Modifier
.padding(vertical = 5.dp)
){
MyTextField(label = "Email", onValueChanged = {})
var isEmailValid by remember { mutableStateOf(true)}
var isPasswordValid by remember { mutableStateOf(true)}
if (!isEmailValid) {
Text(
text = "Invalid email format",
style = MaterialTheme.typography.bodyMedium
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
)
}
Row (modifier = Modifier
.padding(vertical = 5.dp)
){
MyTextField(label = "Password", onValueChanged = {})
MyTextField(label = "Email", onValueChanged = {
userViewModel.email.value = it
isEmailValid = userViewModel.isValidEmail()
})
}
if (!isPasswordValid) {
Text(
text = "Password is required",
style = MaterialTheme.typography.bodyMedium
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
)
}
Row (modifier = Modifier
.padding(vertical = 5.dp)
){
MyTextField(label = "Password", onValueChanged = {
userViewModel.password.value = it
isPasswordValid = it.isNotEmpty()
})
}
}
Button(
onClick = { navController.navigate(NavItem.ListOfServices.route) },
onClick = {
userViewModel.authUser()
navController.navigate(NavItem.Profile.route)
},
modifier = Modifier
.height(60.dp)
.padding(top = 10.dp)

View File

@ -1,7 +1,6 @@
package com.example.myapplication.Profile
package com.example.myapplication.composeui.Profile
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@ -21,19 +20,24 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.example.myapplication.Navbar.NavItem
import com.example.myapplication.GlobalUser
import com.example.myapplication.composeui.Navbar.NavItem
import com.example.myapplication.model.RoleEnum
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.RedBtn
@Composable
fun Profile(navController: NavHostController){
val user = GlobalUser.getInstance().getUser()
if(user == null){
Login(navController = navController)
}else{
Column (
modifier = Modifier
.fillMaxSize()
@ -49,26 +53,27 @@ fun Profile(navController: NavHostController){
){
// TODO: upload profile image
}
Box(modifier = Modifier.padding(15.dp)){
Text(
text = "Name Surname",
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center),
textAlign = TextAlign.Center,
)
}
Box(modifier = Modifier.padding(15.dp)){
Text(
text = "example@mail.ex",
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center),
textAlign = TextAlign.Center,
)
}
Box(modifier = Modifier.padding(15.dp)){
Text(
text = user.name + " " + user.surname,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center),
textAlign = TextAlign.Center,
)
}
Box(modifier = Modifier.padding(15.dp)){
Text(
text = user.email,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center),
textAlign = TextAlign.Center,
)
}
Button(
onClick = { navController.navigate(NavItem.ProfileChange.route) },
modifier = Modifier
@ -107,27 +112,32 @@ fun Profile(navController: NavHostController){
color = Color.White
)
}
Button(
onClick = { navController.navigate(NavItem.AddService.route) },
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
.clip(CircleShape)
.padding(vertical = 5.dp),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(
text = "Add service",
style = MaterialTheme.typography.bodyMedium,
color = Color.White
)
if(user.role == RoleEnum.Admin){
Button(
onClick = { navController.navigate("add_service/{}") },
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
.clip(CircleShape)
.padding(vertical = 5.dp),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(
text = "Add service",
style = MaterialTheme.typography.bodyMedium,
color = Color.White
)
}
}
Button(
onClick = { navController.navigate(NavItem.ProfileNotAuth.route) },
onClick = {
GlobalUser.getInstance().setUser(null)
navController.navigate(NavItem.Login.route)
},
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
@ -151,4 +161,5 @@ fun Profile(navController: NavHostController){
)
}
}
}
}

View File

@ -1,8 +1,6 @@
package com.example.myapplication.Profile
package com.example.myapplication.composeui.Profile
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@ -13,11 +11,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material3.AlertDialogDefaults.shape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
@ -27,28 +20,30 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.modifier.modifierLocalConsumer
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.example.myapplication.GlobalUser
import com.example.myapplication.R
import com.example.myapplication.UIComponents.MyTextField
import com.example.myapplication.ui.theme.BlueBorder
import com.example.myapplication.composeui.Navbar.NavItem
import com.example.myapplication.composeui.UIComponents.MyTextField
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextSecondary
import com.example.myapplication.viewmodel.AppViewModelProvider
import com.example.myapplication.viewmodel.UserViewModel
@Composable
fun ProfileChange (navController: NavHostController){
fun ProfileChange (navController: NavHostController, userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val user = GlobalUser.getInstance().getUser()
Column (
modifier = Modifier
.fillMaxSize()
@ -72,7 +67,7 @@ fun ProfileChange (navController: NavHostController){
}
Box(modifier = Modifier.padding(15.dp)){
Text(
text = "Name Surname",
text = "${user?.name} ${user?.surname}",
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth()
@ -82,7 +77,7 @@ fun ProfileChange (navController: NavHostController){
}
Box(modifier = Modifier.padding(15.dp)){
Text(
text = "example@mail.ex",
text = "${user?.email}",
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth()
@ -93,23 +88,48 @@ fun ProfileChange (navController: NavHostController){
Column (
){
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Name", onValueChanged = {})
MyTextField(label = "Name", onValueChanged = {userViewModel.name.value = it})
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Surname", onValueChanged = {})
MyTextField(label = "Surname", onValueChanged = {userViewModel.surname.value = it})
}
var isEmailValid by remember { mutableStateOf(true) }
var isPasswordValid by remember { mutableStateOf(true) }
if (!isEmailValid) {
Text(
text = "Invalid email format",
style = MaterialTheme.typography.bodyMedium
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
)
}
Row (modifier = Modifier
.padding(vertical = 5.dp)
){
MyTextField(label = "Email", onValueChanged = {
userViewModel.email.value = it
isEmailValid = userViewModel.isValidEmail()
})
}
if (!isPasswordValid) {
Text(
text = "Password is required",
style = MaterialTheme.typography.bodyMedium
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
)
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Email", onValueChanged = {})
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Old password", onValueChanged = {})
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "New password", onValueChanged = {})
MyTextField(label = "Password", onValueChanged = {
userViewModel.password.value = it
isPasswordValid = it.isNotEmpty()
})
}
}
Button(
onClick = { /*TODO*/ },
onClick = {
userViewModel.updateUser()
navController.navigate(NavItem.Profile.route)
},
modifier = Modifier
.height(60.dp)
.padding(top = 10.dp)

View File

@ -1,4 +1,4 @@
package com.example.myapplication.Profile
package com.example.myapplication.composeui.Profile
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@ -16,6 +16,10 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
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.draw.clip
@ -23,15 +27,18 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.Navbar.NavItem
import com.example.myapplication.UIComponents.MyTextField
import com.example.myapplication.composeui.Navbar.NavItem
import com.example.myapplication.composeui.UIComponents.MyTextField
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextSecondary
import com.example.myapplication.viewmodel.AppViewModelProvider
import com.example.myapplication.viewmodel.UserViewModel
@Composable
fun Registration (navController: NavController){
fun Registration (navController: NavController, userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
Column(
modifier = Modifier
.fillMaxSize()
@ -56,20 +63,50 @@ fun Registration (navController: NavController){
Column (
){
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Name", onValueChanged = {})
MyTextField(label = "Name", onValueChanged = { userViewModel.name.value = it })
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Surname", onValueChanged = {})
MyTextField(label = "Surname", onValueChanged = { userViewModel.surname.value = it })
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Email", onValueChanged = {})
var isEmailValid by remember { mutableStateOf(true) }
var isPasswordValid by remember { mutableStateOf(true) }
if (!isEmailValid) {
Text(
text = "Invalid email format",
style = MaterialTheme.typography.bodyMedium
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
)
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Password", onValueChanged = {})
Row (modifier = Modifier
.padding(vertical = 5.dp)
){
MyTextField(label = "Email", onValueChanged = {
userViewModel.email.value = it
isEmailValid = userViewModel.isValidEmail()
})
}
if (!isPasswordValid) {
Text(
text = "Password is required",
style = MaterialTheme.typography.bodyMedium
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
)
}
Row (modifier = Modifier
.padding(vertical = 5.dp)
){
MyTextField(label = "Password", onValueChanged = {
userViewModel.password.value = it
isPasswordValid = it.isNotEmpty()
})
}
}
Button(
onClick = { navController.navigate(NavItem.ListOfServices.route) },
onClick = {
userViewModel.createUser()
navController.navigate(NavItem.Login.route)
},
modifier = Modifier
.height(60.dp)
.padding(top = 10.dp)

View File

@ -1,4 +1,4 @@
package com.example.myapplication.UIComponents
package com.example.myapplication.composeui.UIComponents
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@ -28,7 +28,7 @@ import com.example.myapplication.ui.theme.TextSecondary
@Composable
fun MyTextField (
label: String,
onValueChanged: (String) -> Unit
onValueChanged: (String) -> Unit,
){
val textState = remember { mutableStateOf(TextFieldValue()) }
val text by rememberUpdatedState(newValue = textState.value)
@ -46,13 +46,16 @@ fun MyTextField (
if(textState.value.text.isEmpty()){
Text(
text = label,
style = MaterialTheme.typography.bodyMedium.copy(color = TextSecondary)
style = MaterialTheme.typography.bodyMedium.copy(color = TextSecondary),
)
}else{
}
BasicTextField(
value = text,
onValueChange = { newValue ->
textState.value = newValue
onValueChange = {
textState.value = it
onValueChanged(it.text)
},
modifier = Modifier.fillMaxWidth(),
textStyle = MaterialTheme.typography.bodyMedium,

View File

@ -1,20 +1,16 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.res.stringResource
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TextFieldDefaults.indicatorLine
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -23,6 +19,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.myapplication.R
@ -61,9 +58,7 @@ fun SearchBar(
.clip(RoundedCornerShape(15.dp)),
singleLine = true,
colors = TextFieldDefaults.textFieldColors(
textColor = Color.Black,
containerColor = Color.White,
placeholderColor = TextSecondary,
focusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,

View File

@ -0,0 +1,42 @@
package com.example.myapplication.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import com.example.myapplication.model.Basket
import com.example.myapplication.model.BasketService
import com.example.myapplication.model.BasketWithServices
import kotlinx.coroutines.flow.Flow
@Dao
interface BasketDao {
@Insert
suspend fun insert(basket: Basket):Long
@Insert
suspend fun insertBasketService(basketService: BasketService)
@Query("SELECT * FROM tbl_basket WHERE creatorUserId = :id")
fun getBasketWithServices(id: Int): Flow<BasketWithServices>
@Query("SELECT * FROM tbl_basket WHERE creatorUserId = :id")
suspend fun getUsersBasket(id: Int): Basket
@Query("SELECT * FROM tbl_basket")
fun getAllBaskets(): Flow<List<Basket>>
@Delete
suspend fun delete(basket: Basket)
@Query("DELETE FROM tbl_basket_service WHERE basketId = :basketId AND serviceId = :serviceId")
suspend fun removeServiceFromBasket(basketId: Int, serviceId: Int)
@Query("UPDATE tbl_basket_service SET quantity = :quantity WHERE basketId = :basketId AND serviceId = :serviceId")
suspend fun updateServiceQuantity(basketId: Int, serviceId: Int, quantity: Int)
@Query("UPDATE tbl_basket_service SET quantity = quantity + 1 WHERE basketId = :basketId AND serviceId = :serviceId")
suspend fun incrementServiceQuantity(basketId: Int, serviceId: Int)
@Query("UPDATE tbl_basket_service SET quantity = quantity - 1 WHERE basketId = :basketId AND serviceId = :serviceId")
suspend fun decrementServiceQuantity(basketId: Int, serviceId: Int)
@Query("SELECT quantity FROM tbl_basket_service WHERE basketId = :basketId AND serviceId = :serviceId")
suspend fun getQuantity(basketId: Int, serviceId: Int): Int?
@Query("SELECT * FROM tbl_basket_service WHERE basketId = :basketId AND serviceId = :serviceId")
suspend fun getService(basketId: Int, serviceId: Int): BasketService?
@Query("SELECT SUM(price * quantity) FROM tbl_basket_service bs JOIN tbl_service s ON bs.serviceId = s.serviceId WHERE bs.basketId = :basketId")
suspend fun getTotalPriceForBasket(basketId: Int): Double?
@Query("DELETE FROM tbl_basket_service WHERE basketId = :basketId")
suspend fun clearBasket(basketId: Int)
}

View File

@ -0,0 +1,27 @@
package com.example.myapplication.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import com.example.myapplication.model.Order
import com.example.myapplication.model.OrderService
import com.example.myapplication.model.OrderWithServices
import com.example.myapplication.model.UserWithOrder
import kotlinx.coroutines.flow.Flow
@Dao
interface OrderDao {
@Insert
suspend fun insert(order: Order):Long
@Delete
suspend fun delete(order: Order)
@Insert
suspend fun insertOrderService(orderService: OrderService)
@Query("SELECT * FROM tbl_order WHERE orderId = :id")
fun getOrderWithServices(id: Int): Flow<OrderWithServices>
@Query("SELECT * FROM tbl_order")
fun getAllOrders(): Flow<List<Order>>
@Query("SELECT * FROM tbl_user WHERE userId =:id")
fun getUserOrders(id: Int) : Flow<UserWithOrder>
}

View File

@ -0,0 +1,29 @@
package com.example.myapplication.dao
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
@Dao
interface ServiceDao {
@Insert
suspend fun insert(service: Service) : Long
@Update
suspend fun update(service: Service)
@Delete
suspend fun delete(service: Service)
@Query("SELECT*FROM tbl_service")
fun getAllServices(): Flow<List<Service>>
@Query("SELECT*FROM tbl_service")
fun getAllServicesPaged(): PagingSource<Int, Service>
@Query("SELECT * FROM tbl_service WHERE serviceId = :id")
suspend fun getServiceById(id: Int): Service
}

View File

@ -0,0 +1,31 @@
package com.example.myapplication.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.model.User
import com.example.myapplication.model.UserWithOrder
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 tbl_user WHERE userId = :id")
suspend fun getUserById(id: Int): User
@Query("SELECT * FROM tbl_user WHERE email = :email")
suspend fun getUserByEmail(email: String): User
@Query("SELECT * FROM tbl_user WHERE userId =:id")
fun getUserOrders(id: Int) : Flow<UserWithOrder>
}

View File

@ -0,0 +1,69 @@
package com.example.myapplication.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import com.example.myapplication.dao.BasketDao
import com.example.myapplication.dao.OrderDao
import com.example.myapplication.dao.ServiceDao
import com.example.myapplication.dao.UserDao
import com.example.myapplication.model.Basket
import com.example.myapplication.model.BasketService
import com.example.myapplication.model.Order
import com.example.myapplication.model.OrderService
import com.example.myapplication.model.RoleEnum
import com.example.myapplication.model.Service
import com.example.myapplication.model.User
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Database(entities = [User::class, Service::class, Order::class, OrderService::class, Basket::class, BasketService::class], version = 7)
abstract class AppDatabase : RoomDatabase(){
abstract fun serviceDao(): ServiceDao
abstract fun userDao(): UserDao
abstract fun orderDao(): OrderDao
abstract fun basketDao(): BasketDao
companion object {
private const val DB_NAME: String = "my-db"
@Volatile
private var INSTANCE: AppDatabase? = null
suspend fun populateDatabase() {
INSTANCE?.let { database ->
// User
val userDao = database.userDao()
val user1 = User(null, "Danil", "Markov", "danil@mail.ru", "123", RoleEnum.Admin)
userDao.insert(user1)
val basketDao = database.basketDao()
val basket1 = Basket(null, user1.userId!!)
basketDao.insert(basket1)
}
}
fun getInstance(appContext: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
Room.databaseBuilder(
appContext,
AppDatabase::class.java,
DB_NAME
)
.addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
CoroutineScope(Dispatchers.IO).launch {
populateDatabase()
}
}
})
.fallbackToDestructiveMigration()
.build()
.also { INSTANCE = it }
}
}
}
}

View File

@ -0,0 +1,11 @@
package com.example.myapplication.model
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "tbl_basket")
data class Basket(
@PrimaryKey
val basketId: Int? = null,
val creatorUserId: Int
)

View File

@ -0,0 +1,11 @@
package com.example.myapplication.model
import androidx.room.Entity
@Entity(tableName = "tbl_basket_service",
primaryKeys = ["basketId", "serviceId"])
data class BasketService(
val basketId: Int,
val serviceId: Int,
val quantity: Int
)

View File

@ -0,0 +1,15 @@
package com.example.myapplication.model
import androidx.room.Embedded
import androidx.room.Junction
import androidx.room.Relation
data class BasketWithServices(
@Embedded val basket: Basket,
@Relation(
parentColumn = "basketId",
entityColumn = "serviceId",
associateBy = Junction(BasketService::class)
)
val services: List<Service>
)

View File

@ -0,0 +1,13 @@
package com.example.myapplication.model
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "tbl_order")
data class Order(
@PrimaryKey(autoGenerate = true)
val orderId: Int? = null,
val date: Long,
val total: Double,
val creatorUserId: Int
)

View File

@ -0,0 +1,10 @@
package com.example.myapplication.model
import androidx.room.Entity
@Entity(primaryKeys = ["orderId", "serviceId"], tableName = "tbl_order_service")
data class OrderService(
val orderId: Int,
val serviceId: Int,
val quantity: Int
)

View File

@ -0,0 +1,15 @@
package com.example.myapplication.model
import androidx.room.Embedded
import androidx.room.Junction
import androidx.room.Relation
data class OrderWithServices(
@Embedded val order: Order,
@Relation(
parentColumn = "orderId",
entityColumn = "serviceId",
associateBy = Junction(OrderService::class)
)
val services: List<Service>
)

View File

@ -0,0 +1,6 @@
package com.example.myapplication.model
enum class RoleEnum {
Admin,
User
}

View File

@ -0,0 +1,13 @@
package com.example.myapplication.model
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName="tbl_service")
data class Service (
@PrimaryKey(autoGenerate = true)
var serviceId: Int? = null,
var name: String,
var price: Double,
var photo: Int? = null
)

View File

@ -0,0 +1,16 @@
package com.example.myapplication.model
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "tbl_user")
data class User(
@PrimaryKey(autoGenerate = true)
val userId: Int? = null,
var name: String,
var surname: String,
var email: String,
var password: String,
val role: RoleEnum,
val photo: Int? = null
)

View File

@ -0,0 +1,13 @@
package com.example.myapplication.model
import androidx.room.Embedded
import androidx.room.Relation
data class UserWithOrder(
@Embedded val user: User,
@Relation(
parentColumn = "userId",
entityColumn = "creatorUserId"
)
val orders: List<Order>
)

View File

@ -0,0 +1,24 @@
package com.example.myapplication.repository
import com.example.myapplication.dao.BasketDao
import com.example.myapplication.model.Basket
import com.example.myapplication.model.BasketService
import com.example.myapplication.model.BasketWithServices
import kotlinx.coroutines.flow.Flow
class BasketRepository(private val basketDao: BasketDao) {
suspend fun createBasket(basket: Basket): Long = basketDao.insert(basket)
suspend fun getUsersBasket(userId: Int): Basket = basketDao.getUsersBasket(userId)
suspend fun removeServiceFromBasket(basketId: Int, serviceId: Int) = basketDao.removeServiceFromBasket(basketId, serviceId)
suspend fun updateServiceQuantity(basketId: Int, serviceId: Int, quantity: Int) = basketDao.updateServiceQuantity(basketId, serviceId, quantity)
suspend fun incrementServiceQuantity(basketId: Int, serviceId: Int) = basketDao.incrementServiceQuantity(basketId, serviceId)
suspend fun decrementServiceQuantity(basketId: Int, serviceId: Int) = basketDao.decrementServiceQuantity(basketId, serviceId)
suspend fun insertBasketService(basketService: BasketService) = basketDao.insertBasketService(basketService)
fun getBasketWithServices(id: Int): Flow<BasketWithServices> = basketDao.getBasketWithServices(id)
fun getAllBasket(): Flow<List<Basket>> = basketDao.getAllBaskets()
suspend fun delete(basket: Basket) = basketDao.delete(basket)
suspend fun getQuantity(basketId: Int, serviceId: Int): Int? = basketDao.getQuantity(basketId, serviceId)
suspend fun getService(basketId: Int, serviceId: Int): BasketService? = basketDao.getService(basketId, serviceId)
suspend fun getTotalPriceForBasket(basketId: Int): Double? = basketDao.getTotalPriceForBasket(basketId)
suspend fun clearBasket(basketId: Int) = basketDao.clearBasket(basketId)
}

View File

@ -0,0 +1,14 @@
package com.example.myapplication.repository
import com.example.myapplication.dao.OrderDao
import com.example.myapplication.model.Order
import com.example.myapplication.model.OrderService
class OrderRepository(private val orderDao: OrderDao) {
suspend fun insert(order: Order) = orderDao.insert(order)
suspend fun insertOrderService(orderService: OrderService) = orderDao.insertOrderService(orderService)
suspend fun delete(order: Order) = orderDao.delete(order)
fun getOrderWithServices(id: Int) = orderDao.getOrderWithServices(id)
fun getAllOrders() = orderDao.getAllOrders()
fun getUserOrders(id: Int) = orderDao.getUserOrders(id)
}

View File

@ -0,0 +1,23 @@
package com.example.myapplication.repository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.myapplication.dao.ServiceDao
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
class ServiceRepository(private val serviceDao: ServiceDao) {
suspend fun insert(service: Service) = serviceDao.insert(service)
suspend fun update(service: Service) = serviceDao.update(service)
suspend fun delete(service: Service) = serviceDao.delete(service)
suspend fun getServiceById(id: Int) = serviceDao.getServiceById(id)
fun getAllServices() = serviceDao.getAllServices()
fun call(): Flow<PagingData<Service>> {
return Pager(
PagingConfig(pageSize = 5)
){
serviceDao.getAllServicesPaged()
}.flow
}
}

View File

@ -0,0 +1,12 @@
package com.example.myapplication.repository
import com.example.myapplication.dao.UserDao
import com.example.myapplication.model.User
class UserRepository(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)
suspend fun getUserById(id: Int) = userDao.getUserById(id)
suspend fun getUserByEmail(email: String) = userDao.getUserByEmail(email)
}

View File

@ -0,0 +1,27 @@
package com.example.myapplication.viewmodel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.myapplication.App
object AppViewModelProvider {
val Factory = viewModelFactory {
initializer {
ServiceViewModel(app().container.serviceRepo)
}
initializer {
UserViewModel(app().container.userRepo, app().container.basketRepo)
}
initializer {
OrderViewModel(app().container.orderRepo, app().container.basketRepo)
}
initializer {
BasketViewModel(app().container.basketRepo, app().container.orderRepo,)
}
}
}
fun CreationExtras.app(): App =
(this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as App)

View File

@ -0,0 +1,92 @@
package com.example.myapplication.viewmodel
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableDoubleStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.GlobalUser
import com.example.myapplication.model.Basket
import com.example.myapplication.model.BasketService
import com.example.myapplication.model.BasketWithServices
import com.example.myapplication.repository.BasketRepository
import com.example.myapplication.repository.OrderRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class BasketViewModel(private val basketRepository: BasketRepository, private val orderRepository: OrderRepository) : ViewModel() {
private val _quantityStateMap = mutableMapOf<Int, MutableStateFlow<Int>>()
private val _total = mutableDoubleStateOf(0.00)
val total: State<Double> get() = _total
fun getQuantityState(basketId: Int, serviceId: Int): StateFlow<Int> {
val quantityStateFlow = _quantityStateMap.getOrPut(serviceId) {
MutableStateFlow(0)
}
viewModelScope.launch {
val quantityFromDb = basketRepository.getQuantity(basketId, serviceId)
quantityFromDb?.let { quantityStateFlow.value = it }
}
return quantityStateFlow
}
suspend fun isServiceInBasket(basketId: Int, serviceId: Int): Boolean {
return basketRepository.getService(basketId, serviceId) != null
}
fun addToBasket(basketServices: BasketService) = viewModelScope.launch {
val isServiceInBasket = isServiceInBasket(basketServices.basketId, basketServices.serviceId)
if (isServiceInBasket) {
incrementQuantity(basketServices.basketId, basketServices.serviceId)
} else {
basketRepository.insertBasketService(basketServices)
}
}
fun getBasketServices(id: Int): Flow<BasketWithServices> {
return basketRepository.getBasketWithServices(id)
}
suspend fun getUsersBasket(id: Int): Basket {
return basketRepository.getUsersBasket(id)
}
fun deleteServiceFromBasket(basketId: Int, serviceId: Int) = viewModelScope.launch {
basketRepository.removeServiceFromBasket(basketId, serviceId)
}
fun incrementQuantity(basketId: Int, serviceId: Int) {
val currentQuantity = _quantityStateMap[serviceId]?.value ?: 1
_quantityStateMap[serviceId]?.value = currentQuantity + 1
viewModelScope.launch {
basketRepository.incrementServiceQuantity(basketId, serviceId)
updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!)
}
}
fun decrementQuantity(basketId: Int, serviceId: Int) {
val currentQuantity = _quantityStateMap[serviceId]?.value ?: 1
if (currentQuantity > 1) {
_quantityStateMap[serviceId]?.value = currentQuantity - 1
viewModelScope.launch {
basketRepository.decrementServiceQuantity(basketId, serviceId)
updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!)
}
}
}
fun updateSubTotal(userId: Int) {
viewModelScope.launch {
_total.value = getTotal(userId)
}
}
suspend fun getTotal(userId: Int): Double {
return basketRepository.getTotalPriceForBasket(basketRepository.getUsersBasket(userId!!).basketId!!) ?: 0.00
}
}

View File

@ -0,0 +1,70 @@
package com.example.myapplication.viewmodel
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableDoubleStateOf
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.GlobalUser
import com.example.myapplication.model.Order
import com.example.myapplication.model.OrderService
import com.example.myapplication.model.OrderWithServices
import com.example.myapplication.model.Service
import com.example.myapplication.model.UserWithOrder
import com.example.myapplication.repository.BasketRepository
import com.example.myapplication.repository.OrderRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import java.util.Date
class OrderViewModel(private val orderRepository: OrderRepository, private val basketRepository: BasketRepository) : ViewModel() {
private var _selectedItems = MutableLiveData<List<Service>>()
val selectedItems get() = _selectedItems
private val _total = mutableDoubleStateOf(0.00)
val total: State<Double> get() = _total
fun createOrder() = viewModelScope.launch {
val userId = GlobalUser.getInstance().getUser()?.userId!!
val order = Order(
date = Date().time,
total = getTotal(basketRepository.getUsersBasket(userId).basketId!!),
creatorUserId = GlobalUser.getInstance().getUser()?.userId!!
)
var orderId = orderRepository.insert(order)
for(service in selectedItems.value!!){
val quantity = basketRepository.getQuantity(basketRepository.getUsersBasket(userId).basketId!!, service.serviceId!!)
val orderService = OrderService( orderId.toInt(), service.serviceId!!, quantity!!)
if (orderService != null) {
orderRepository.insertOrderService(orderService)
}
}
basketRepository.clearBasket(basketRepository.getUsersBasket(userId).basketId!!)
}
fun getOrderWithServices(id: Int) : Flow<OrderWithServices> {
return orderRepository.getOrderWithServices(id)
}
fun getUserOrders(id: Int): Flow<UserWithOrder> {
return orderRepository.getUserOrders(id)
}
fun updateSelectedItems(items: List<Service>) {
_selectedItems.value = items
val userId = GlobalUser.getInstance().getUser()?.userId!!
updateSubTotal(userId)
}
fun updateSubTotal(userId: Int) {
viewModelScope.launch {
_total.value = getTotal(userId)
}
}
suspend fun getTotal(basketId: Int): Double {
return basketRepository.getTotalPriceForBasket(basketId) ?: 0.00
}
}

View File

@ -0,0 +1,45 @@
package com.example.myapplication.viewmodel
import androidx.compose.runtime.mutableDoubleStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.cachedIn
import com.example.myapplication.R
import com.example.myapplication.model.Service
import com.example.myapplication.repository.ServiceRepository
import kotlinx.coroutines.launch
class ServiceViewModel(private val serviceRepository: ServiceRepository): ViewModel() {
var name = mutableStateOf("")
var price = mutableDoubleStateOf(0.00)
var photo = mutableIntStateOf(R.drawable.image_service)
var service = mutableStateOf<Service>(Service(null,"", 0.0, null))
val serviceList = serviceRepository.call().cachedIn(viewModelScope)
fun createService() = viewModelScope.launch {
val service = Service(
name = name.value,
price = price.doubleValue,
photo = photo.intValue
)
serviceRepository.insert(service)
}
fun updateService() = viewModelScope.launch {
serviceRepository.update(service.value)
}
fun deleteService(service: Service) = viewModelScope.launch {
serviceRepository.delete(service)
}
fun getServiceById(id: Int) = viewModelScope.launch {
serviceRepository.getServiceById(id)
}
fun getAllServices() = viewModelScope.launch {
serviceRepository.getAllServices()
}
}

View File

@ -0,0 +1,73 @@
package com.example.myapplication.viewmodel
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.GlobalUser
import com.example.myapplication.R
import com.example.myapplication.model.Basket
import com.example.myapplication.model.RoleEnum
import com.example.myapplication.model.User
import com.example.myapplication.repository.BasketRepository
import com.example.myapplication.repository.UserRepository
import kotlinx.coroutines.launch
class UserViewModel(private val userRepository: UserRepository, private val basketRepository: BasketRepository): ViewModel() {
var name = mutableStateOf("")
var surname = mutableStateOf("")
var email = mutableStateOf("")
var password = mutableStateOf("")
var photo = mutableIntStateOf(R.drawable.icon_profile)
fun createUser() = viewModelScope.launch {
var user = User(
name = name.value,
surname = surname.value,
email = email.value,
password = password.value,
role = RoleEnum.User
)
userRepository.insert(user)
}
fun authUser() = viewModelScope.launch {
val user = userRepository.getUserByEmail(email.value)
if(password.value.isNotEmpty() && user.password == password.value){
val globalUser = GlobalUser.getInstance()
globalUser.setUser(user)
val basket = basketRepository.getUsersBasket(user.userId!!)
if(basket == null){
basketRepository.createBasket(Basket(null, user.userId))
}
}
}
fun updateUser() = viewModelScope.launch {
var updateUser = GlobalUser.getInstance().getUser()
if(email.value != "")
updateUser?.email = email.value
else
updateUser?.email = updateUser?.email.toString()
if(name.value != "")
updateUser?.name = name.value
else
updateUser?.name = updateUser?.name.toString()
if(surname.value != "")
updateUser?.surname = surname.value
else
updateUser?.surname = updateUser?.surname.toString()
if(password.value != "")
updateUser?.password = password.value
else
updateUser?.password = updateUser?.password.toString()
//updateUser?.photo =
if (updateUser != null) {
userRepository.update(updateUser)
}
}
fun isValidEmail(): Boolean {
return android.util.Patterns.EMAIL_ADDRESS.matcher(email.value).matches()
}
}

BIN
reports/Отчет1.docx Normal file

Binary file not shown.

BIN
reports/Отчет2.docx Normal file

Binary file not shown.

BIN
reports/Отчет3.docx Normal file

Binary file not shown.

BIN
reports/Отчет4.docx Normal file

Binary file not shown.

BIN
reports/Отчет5.docx Normal file

Binary file not shown.