Compare commits

...

10 Commits

99 changed files with 4634 additions and 1344 deletions

221
.idea/workspace.xml generated
View File

@ -9,35 +9,61 @@
<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: add logo">
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/ApiStatus.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/model/RemoteConverters.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/model/ReportRemote.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/model/ServiceWithCount.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/repository/RestReportRepository.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/repository/ReportRepository.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/MyViewModel.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/ReportViewModel.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/List_of_Services/ChangeService.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/NetworkUI/Error.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/NetworkUI/Loading.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Orders/ServiceReportCard.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/Report.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/ReportDatePick.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Profile/ServiceReportCard.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/UIComponents/DatePicker.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/model/Converters.kt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app/src/main/res/drawable/profile.png" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" 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/MainActivity.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/MainActivity.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/ServerService.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/ServerService.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/model/ServiceRemote.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/model/ServiceRemote.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/model/UserRemote.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/model/UserRemote.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/repository/RestServiceRepository.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/repository/RestServiceRepository.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/repository/RestUserRepository.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/api/repository/RestUserRepository.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/repository/ServiceRepository.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/repository/ServiceRepository.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/AppViewModelProvider.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/AppViewModelProvider.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/BasketViewModel.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/BasketViewModel.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/OrderViewModel.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/OrderViewModel.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/ServiceViewModel.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/ServiceViewModel.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/UserViewModel.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/businessLogic/viewmodel/UserViewModel.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/NavBar.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Navbar/NavBar.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/OrderItem.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/composeui/Orders/OrderItem.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/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/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/database/dao/ServiceDao.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/database/dao/ServiceDao.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/database/repository/ServiceRepositoryImpl.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/database/repository/ServiceRepositoryImpl.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/di/AppContainer.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/di/AppContainer.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/di/AppDataContainer.kt" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/java/com/example/myapplication/di/AppDataContainer.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/res/values/strings.xml" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/main/res/values/strings.xml" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -53,8 +79,9 @@
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Class" />
<option value="resourceFile" />
<option value="Class" />
<option value="Kotlin Interface" />
<option value="Kotlin Class" />
</list>
</option>
@ -67,11 +94,14 @@
<component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY">
<map>
<entry key="$PROJECT_DIR$" value="main" />
<entry key="$PROJECT_DIR$" value="LabWork05" />
</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>
@ -84,37 +114,57 @@
&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;PROJECT_TRUSTED_KEY&quot;: &quot;true&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;ToolWindowLogcat.ShowToolbar&quot;: &quot;false&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;com.google.services.firebase.aqiPopupShown&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;C:/Users/Danil/Desktop/MDP/labs/app/src/main/java/com/example/myapplication/composeui/Orders&quot;,
&quot;project.structure.last.edited&quot;: &quot;Project&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;
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.lookFeel&quot;
},
&quot;keyToStringList&quot;: {
&quot;ExportApk.BuildVariants&quot;: [
&quot;release&quot;
],
&quot;com.android.tools.idea.sqlite.queryhistory&quot;: [
&quot;delete from tbl_service&quot;,
&quot;insert into tbl_user values (1, \&quot;Danil\&quot;, \&quot;Markov\&quot;, \&quot;danil@mail.ru\&quot;, \&quot;123\&quot;, \&quot;ADMIN\&quot;, null)&quot;,
&quot;delete from tbl_user where userId = 6&quot;,
&quot;delete from tbl_user where userId = 4&quot;,
&quot;delete from tbl_user where userId = 2&quot;
]
}
}</component>
<component name="PsdUISettings">
<option name="MODULE_TAB" value="Properties" />
<option name="LAST_EDITED_SIGNING_CONFIG" value="debug" />
<option name="LAST_EDITED_BUILD_TYPE" value="release" />
</component>
<component name="RecentsManager">
<key name="android.template.-1413981578">
<recent name="com.example.myapplication.Navbar" />
</key>
<key name="CopyFile.RECENT_KEYS">
<recent name="C:\Users\Danil\Desktop\MDP\labs\app\src\main\java\com\example\myapplication\composeui\Orders" />
<recent name="C:\Users\Danil\Desktop\MDP\labs\app\src\main\res\drawable" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="C:\Users\Danil\Desktop\MDP\labs\app\src\main\java\com\example\myapplication\business_logic" />
<recent name="C:\Users\Danil\Desktop\MDP\labs\app\src\main\res\drawable" />
<recent name="C:\Users\Danil\Desktop\MDP\labs\app\src\main\java\com\example\myapplication\UIComponents" />
</key>
<key name="MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY">
<recent name="com.example.myapplication.businessLogic.repository" />
<recent name="com.example.myapplication.di" />
</key>
</component>
<component name="RunManager" selected="Android App.App">
<configuration name="App" type="AndroidRunConfigurationType" factoryName="Android App" temporary="true">
<component name="RunManager" selected="Android App.app">
<configuration name="MainActivity" type="AndroidRunConfigurationType" factoryName="Android App" temporary="true">
<module name="My_Application.app.main" />
<option name="DEPLOY" value="true" />
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
@ -173,7 +223,7 @@
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>
<option name="DEEP_LINK" value="" />
<option name="ACTIVITY_CLASS" value="com.example.myapplication.App" />
<option name="ACTIVITY_CLASS" value="com.example.myapplication.MainActivity" />
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
<method v="2">
@ -246,9 +296,18 @@
<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="Android App.App" />
<item itemvalue="Android App.MainActivity" />
<item itemvalue="Kotlin script (Beta).build.gradle.kts" />
<item itemvalue="Kotlin script (Beta).build.gradle.kts" />
</list>
</recent_temporary>
</component>
@ -275,7 +334,70 @@
<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>
<task id="LOCAL-00008" summary="feature: lab4 maybe done">
<created>1702138495251</created>
<option name="number" value="00008" />
<option name="presentableId" value="LOCAL-00008" />
<option name="project" value="LOCAL" />
<updated>1702138495251</updated>
</task>
<task id="LOCAL-00009" summary="feature: lab5 almost done, save commit">
<created>1702979910728</created>
<option name="number" value="00009" />
<option name="presentableId" value="LOCAL-00009" />
<option name="project" value="LOCAL" />
<updated>1702979910728</updated>
</task>
<task id="LOCAL-00010" summary="feature: lab5 done">
<created>1703256177985</created>
<option name="number" value="00010" />
<option name="presentableId" value="LOCAL-00010" />
<option name="project" value="LOCAL" />
<updated>1703256177985</updated>
</task>
<task id="LOCAL-00011" summary="feature: add logo">
<created>1703337054935</created>
<option name="number" value="00011" />
<option name="presentableId" value="LOCAL-00011" />
<option name="project" value="LOCAL" />
<updated>1703337054935</updated>
</task>
<option name="localTasksCounter" value="12" />
<servers />
</component>
<component name="Vcs.Log.Tabs.Properties">
@ -291,6 +413,25 @@
</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" />
<MESSAGE value="feature: lab4 maybe done" />
<MESSAGE value="feature: lab5 almost done, save commit" />
<MESSAGE value="feature: lab5 done" />
<MESSAGE value="feature: add logo" />
<option name="LAST_COMMIT_MESSAGE" value="feature: add logo" />
</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/businessLogic/viewmodel/UserViewModel.kt</url>
<line>49</line>
<option name="timeStamp" value="105" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
</project>

View File

@ -1,6 +1,8 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id ("kotlin-kapt")
kotlin("plugin.serialization") version "1.4.21"
}
android {
@ -9,7 +11,7 @@ android {
defaultConfig {
applicationId = "com.example.myapplication"
minSdk = 29
minSdk = 26
targetSdk = 34
versionCode = 1
versionName = "1.0"
@ -30,11 +32,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 +51,53 @@ 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.4.0-alpha03")
//Paging
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")
// retrofit
val retrofitVersion = "2.9.0"
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
implementation("androidx.paging:paging-compose:3.2.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
}

View File

@ -2,8 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".App"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
@ -14,7 +16,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,14 @@
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
import com.example.myapplication.di.AppContainer
import com.example.myapplication.di.AppDataContainer
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

@ -1,118 +0,0 @@
package com.example.myapplication.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
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.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
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.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.example.myapplication.R
import com.example.myapplication.TestServiceItem
import com.example.myapplication.getServices
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
@Composable
fun Basket(navController : NavHostController){
Column(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.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)
}
}
Box(modifier = Modifier
.clip(RoundedCornerShape(15.dp, 15.dp, 0.dp, 0.dp))
.background(Color.Transparent)
.height(130.dp),
){
Column (modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.background(Color.White)
.padding(PaddingValues(15.dp)),
){
Row (
horizontalArrangement = Arrangement.SpaceBetween,
){
Text(
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:",
style = MaterialTheme.typography.bodyMedium
)
}
}
Button(
onClick = { /*TODO*/ },
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
.clip(CircleShape)
.align(Alignment.BottomCenter),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(text = "Confirm order", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
}
}
}
}

View File

@ -1,214 +0,0 @@
package com.example.myapplication.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
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.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.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.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.navigation.NavController
import com.example.myapplication.UIComponents.MyTextField
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 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) }
Column (
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
){
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(
bitmap = selectedImage!!,
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 = ""
serviceName?.let {
Text(
text = it,
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium)
}
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 = "${price}$",
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)
}
}
}
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Price"){
newValue ->
price = newValue
}
}
}
Button(
onClick = {
// val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
// launcher.launch(intent)
},
modifier = Modifier
.height(60.dp)
.padding(top = 10.dp)
.fillMaxWidth()
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(text = "Upload image", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
}
Button(
onClick = {
},
modifier = Modifier
.height(60.dp)
.padding(top = 10.dp)
.fillMaxWidth()
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(text = "Add 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

@ -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)
//this.deleteDatabase("my-db")
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,107 +0,0 @@
package com.example.myapplication.Profile
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.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.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.example.myapplication.Navbar.NavItem
import com.example.myapplication.UIComponents.MyTextField
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextSecondary
@Composable
fun Login (navController: NavController){
Column(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
){
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 30.dp),
horizontalArrangement = Arrangement.Center
){
Text(
text = "PetMed",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White, fontSize = TextUnit(16.0f, TextUnitType.Em))
)
}
Column (
){
Row (modifier = Modifier
.padding(vertical = 5.dp)
){
MyTextField(label = "Email", onValueChanged = {})
}
Row (modifier = Modifier
.padding(vertical = 5.dp)
){
MyTextField(label = "Password", onValueChanged = {})
}
}
Button(
onClick = { navController.navigate(NavItem.ListOfServices.route) },
modifier = Modifier
.height(60.dp)
.padding(top = 10.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)
)
}
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
){
Text(
text = "Don't have an account? ",
style = MaterialTheme.typography.bodyMedium
.copy(TextSecondary)
)
Text(
text = "Sign up",
style = MaterialTheme.typography.bodyMedium
.copy(GreenBtn),
modifier = Modifier
.clickable { navController.navigate(NavItem.Registration.route) }
)
}
}
}

View File

@ -1,127 +0,0 @@
package com.example.myapplication.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
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.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
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.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.dp
import androidx.navigation.NavHostController
import com.example.myapplication.R
import com.example.myapplication.UIComponents.MyTextField
import com.example.myapplication.ui.theme.BlueBorder
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextSecondary
@Composable
fun ProfileChange (navController: NavHostController){
Column (
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
){
Box(modifier = Modifier
.clip(CircleShape)
.size(200.dp)
.background(Color.White)
.padding(PaddingValues(0.dp))
){
Icon(
painterResource(id = R.drawable.upload),
contentDescription = null,
modifier = Modifier.align(Alignment.Center),
GreenBtn
)
}
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,
)
}
Column (
){
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Name", onValueChanged = {})
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Surname", onValueChanged = {})
}
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 = {})
}
}
Button(
onClick = { /*TODO*/ },
modifier = Modifier
.height(60.dp)
.padding(top = 10.dp)
.fillMaxWidth()
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(text = "Confirm changes", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
}
}
}

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

@ -1,107 +0,0 @@
package com.example.myapplication.Profile
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.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.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.example.myapplication.Navbar.NavItem
import com.example.myapplication.UIComponents.MyTextField
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextSecondary
@Composable
fun Registration (navController: NavController){
Column(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
){
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 30.dp),
horizontalArrangement = Arrangement.Center,
){
Text(
text = "PetMed",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White, fontSize = TextUnit(16.0f, TextUnitType.Em))
)
}
Column (
){
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Name", onValueChanged = {})
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Surname", onValueChanged = {})
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Email", onValueChanged = {})
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Password", onValueChanged = {})
}
}
Button(
onClick = { navController.navigate(NavItem.ListOfServices.route) },
modifier = Modifier
.height(60.dp)
.padding(top = 10.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)
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
){
Text(
text = "Already have a account? ",
style = MaterialTheme.typography.bodyMedium
.copy(TextSecondary)
)
Text(
text = "Login",
style = MaterialTheme.typography.bodyMedium
.copy(GreenBtn),
modifier = Modifier.clickable { navController.navigate(NavItem.Login.route) }
)
}
}
}

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

@ -0,0 +1,3 @@
package com.example.myapplication.api
enum class ApiStatus { LOADING, ERROR, DONE }

View File

@ -0,0 +1,182 @@
package com.example.myapplication.api
import com.example.myapplication.api.model.BasketServiceRemote
import com.example.myapplication.api.model.OrderRemote
import com.example.myapplication.api.model.OrderServiceRemote
import com.example.myapplication.api.model.ReportRemote
import com.example.myapplication.api.model.ServiceRemote
import com.example.myapplication.api.model.UserRemote
import com.example.myapplication.api.model.UserRemoteSignIn
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.Query
interface ServerService {
// Service
@GET("service/get/{id}")
suspend fun getService(
@Path("id") id: Int,
): ServiceRemote
@GET("service/getAll")
suspend fun getServices(
@Query("page") page: Int,
@Query("size") size: Int,
): List<ServiceRemote>
@POST("service/create")
suspend fun createService(
@Body service: ServiceRemote,
): ServiceRemote
@PUT("service/update/{id}")
suspend fun updateService(
@Path("id") id: Int,
@Body service: ServiceRemote
): ServiceRemote
@DELETE("service/delete/{id}")
suspend fun deleteService(
@Path("id") id: Int
): Response<String>
//USER
@POST("user/signup")
suspend fun SignUp(
@Body user: UserRemote,
): UserRemote
@POST("user/signin")
suspend fun SignIn(
@Body user: UserRemoteSignIn
): UserRemote
@PUT("user/update")
suspend fun updateUser(
@Body user: UserRemote
): UserRemote
//BASKET
@POST("basket/addServiceToBasket")
suspend fun addServiceToBasket(
@Body basketService: BasketServiceRemote
)
@GET("basket/getUserBasketServices/{id}")
suspend fun getUserBasketServices(
@Path("id") id: Int
): List<ServiceRemote>
@GET("basket/getUserBasket/{id}")
suspend fun getUserBasket(
@Path("id") id: Int
): Int
@GET("basket/getQuantity/{basketId}/{serviceId}")
suspend fun getQuantity(
@Path("basketId") basketId: Int,
@Path("serviceId") serviceId: Int,
): Int
@PUT("basket/incrementQuantity/{basketId}/{serviceId}")
suspend fun increment(
@Path("basketId") basketId: Int,
@Path("serviceId") serviceId: Int,
)
@PUT("basket/decrementQuantity/{basketId}/{serviceId}")
suspend fun decrement(
@Path("basketId") basketId: Int,
@Path("serviceId") serviceId: Int,
)
@GET("basket/getService/{basketId}/{serviceId}")
suspend fun getService(
@Path("basketId") basketId: Int,
@Path("serviceId") serviceId: Int,
): Boolean
@GET("basket/removeService/{basketId}/{serviceId}")
suspend fun deleteServiceFromBasket(
@Path("basketId") basketId: Int,
@Path("serviceId") serviceId: Int,
)
@GET("basket/getUserPrice/{userId}")
suspend fun getTotalPriceForUserBasket(
@Path("userId") userId: Int
): Double
@GET("basket/deleteAllServiceFromBasket/{basketId}")
suspend fun deleteAllServiceFromBasket(
@Path("basketId") basketId: Int
)
//ORDER
@POST("order/addServiceToOrder")
suspend fun addServiceToOrder(
@Body orderService: OrderServiceRemote
)
@POST("order/create")
suspend fun createOrder(
@Body order: OrderRemote
)
@GET("order/getUserOrders/{userId}")
suspend fun getUserOrders(
@Path("userId") userId: Int
) : List<OrderRemote>
@GET("order/getServiceFromOrder/{orderId}")
suspend fun getServiceFromOrder(
@Path("orderId") orderId: Int
) : List<ServiceRemote>
@GET("order/deleteOrder/{orderId}")
suspend fun deleteOrder(
@Path("orderId") orderId: Int
)
//REPORT
@GET("report/getReport/{dateFrom}/{dateTo}")
suspend fun getReport(
@Path("dateFrom") dateFrom: Long,
@Path("dateTo") dateTo: Long
): ReportRemote
companion object {
private const val BASE_URL = "https://ftkfjb1l-8080.euw.devtunnels.ms/api/"
@Volatile
private var INSTANCE: ServerService? = null
fun getInstance(): ServerService {
return INSTANCE ?: synchronized(this) {
val logger = HttpLoggingInterceptor()
logger.level = HttpLoggingInterceptor.Level.BASIC
val client = OkHttpClient.Builder()
.addInterceptor(logger)
.build()
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.build()
.create(ServerService::class.java)
.also { INSTANCE = it }
}
}
}
}

View File

@ -0,0 +1,106 @@
package com.example.myapplication.api
import android.net.http.HttpException
import android.os.Build
import androidx.annotation.RequiresExtension
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.myapplication.api.model.toService
import com.example.myapplication.database.AppDatabase
import com.example.myapplication.database.repository.RemoteKeysRepositoryImpl
import com.example.myapplication.database.repository.ServiceRepositoryImpl
import com.example.myapplication.model.RemoteKeyType
import com.example.myapplication.model.RemoteKeys
import com.example.myapplication.model.Service
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
class ServiceRemoteMediator(
private val service: ServerService,
private val serviceRepository: ServiceRepositoryImpl,
private val database: AppDatabase,
private val dbRemoteKeyRepository: RemoteKeysRepositoryImpl
) : RemoteMediator<Int, Service>() {
override suspend fun initialize(): InitializeAction {
return InitializeAction.LAUNCH_INITIAL_REFRESH
}
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 7)
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, Service>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: 1
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
remoteKeys?.prevKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
remoteKeys?.nextKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
}
try {
val services = service.getServices(page, state.config.pageSize).map { it.toService() }
val endOfPaginationReached = services.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.SERVICE)
serviceRepository.clearServices()
}
val prevKey = if (page == 1) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = services.map {
RemoteKeys(
entityId = it.serviceId!!,
type = RemoteKeyType.SERVICE,
prevKey = prevKey,
nextKey = nextKey
)
}
dbRemoteKeyRepository.createRemoteKeys(keys)
serviceRepository.insertServices(services)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(exception)
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Service>): RemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { service ->
service.serviceId?.let { dbRemoteKeyRepository.getAllRemoteKeys(it, RemoteKeyType.SERVICE) }
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Service>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { service ->
service.serviceId?.let { dbRemoteKeyRepository.getAllRemoteKeys(it, RemoteKeyType.SERVICE) }
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Service>
): RemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.serviceId?.let { serviceUid ->
dbRemoteKeyRepository.getAllRemoteKeys(serviceUid, RemoteKeyType.SERVICE)
}
}
}
}

View File

@ -0,0 +1,9 @@
package com.example.myapplication.api.model
import kotlinx.serialization.Serializable
@Serializable
data class BasketRemote (
val id: Int? = 0,
val userId: Int = 0,
)

View File

@ -0,0 +1,23 @@
package com.example.myapplication.api.model
import com.example.myapplication.model.BasketService
import kotlinx.serialization.Serializable
@Serializable
data class BasketServiceRemote (
val basketId: Int = 0,
val serviceId: Int = 0,
val quantity: Int = 0
)
fun BasketServiceRemote.toBasketService(): BasketService = BasketService(
basketId,
serviceId,
quantity
)
fun BasketService.toBasketServiceRemote():BasketServiceRemote = BasketServiceRemote(
basketId,
serviceId,
quantity
)

View File

@ -0,0 +1,26 @@
package com.example.myapplication.api.model
import com.example.myapplication.model.Order
import kotlinx.serialization.Serializable
@Serializable
data class OrderRemote(
val id: Int? = 0,
val date: Long = 0L,
val total: Double = 0.0,
val creatorUserId: Int = 0
)
fun OrderRemote.toOrder(): Order = Order(
id,
date,
total,
creatorUserId
)
fun Order.toOrderRemote():OrderRemote = OrderRemote(
orderId,
date,
total,
creatorUserId
)

View File

@ -0,0 +1,23 @@
package com.example.myapplication.api.model
import com.example.myapplication.model.OrderService
import kotlinx.serialization.Serializable
@Serializable
data class OrderServiceRemote (
val orderId: Int = 0,
val serviceId: Int = 0,
val quantity: Int = 0
)
fun OrderServiceRemote.toOrderService(): OrderService = OrderService(
orderId,
serviceId,
quantity
)
fun OrderService.toOrderServiceRemote():OrderServiceRemote = OrderServiceRemote(
orderId,
serviceId,
quantity
)

View File

@ -0,0 +1,24 @@
package com.example.myapplication.api.model
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Base64
import java.io.ByteArrayOutputStream
class RemoteConverters {
companion object {
private const val CHARSET_UTF8 = "UTF-8"
fun fromBitmap(bitmap: Bitmap): String {
val outputStream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 1, outputStream)
val byteArray = outputStream.toByteArray()
return Base64.encodeToString(byteArray, Base64.NO_WRAP)
}
fun toBitmap(base64String: String): Bitmap {
val byteArray = Base64.decode(base64String, Base64.NO_WRAP)
return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
}
}
}

View File

@ -0,0 +1,11 @@
package com.example.myapplication.api.model
import kotlinx.serialization.Serializable
@Serializable
data class ReportRemote(
val countOrder: Int,
val totalEarn: Double? = 0.0,
val avgCheck: Double? = 0.0,
val serviceList: List<ServiceWithCount>
)

View File

@ -0,0 +1,26 @@
package com.example.myapplication.api.model
import com.example.myapplication.model.Service
import kotlinx.serialization.Serializable
@Serializable
data class ServiceRemote (
val id: Int? = 0,
val name: String = "",
val price: Double = 0.0,
val photo: String = ""
)
fun ServiceRemote.toService(): Service = Service(
id,
name,
price,
RemoteConverters.toBitmap(photo)
)
fun Service.toServiceRemote():ServiceRemote = ServiceRemote(
serviceId,
name,
price,
RemoteConverters.fromBitmap(photo)
)

View File

@ -0,0 +1,9 @@
package com.example.myapplication.api.model
import kotlinx.serialization.Serializable
@Serializable
data class ServiceWithCount(
val service: ServiceRemote,
val quantity: Int
)

View File

@ -0,0 +1,40 @@
package com.example.myapplication.api.model
import com.example.myapplication.model.RoleEnum
import com.example.myapplication.model.User
import kotlinx.serialization.Serializable
@Serializable
data class UserRemote (
val id: Int? = 0,
val name: String = "",
val surname: String = "",
val email: String = "",
val password: String = "",
val role: RoleEnum = RoleEnum.User,
val photo: String = "",
val basketId: Int? = 0
)
fun UserRemote.toUser(): User = User(
id,
name,
surname,
email,
password,
role,
RemoteConverters.toBitmap(photo),
basketId
)
fun User.toUserRemote():UserRemote = UserRemote(
userId,
name,
surname,
email,
password,
role,
RemoteConverters.fromBitmap(photo),
basketId
)

View File

@ -0,0 +1,9 @@
package com.example.myapplication.api.model
import kotlinx.serialization.Serializable
@Serializable
data class UserRemoteSignIn(
val email: String = "",
val password: String = "",
)

View File

@ -0,0 +1,76 @@
package com.example.myapplication.api.repository
import com.example.myapplication.api.ServerService
import com.example.myapplication.api.model.toBasketServiceRemote
import com.example.myapplication.api.model.toService
import com.example.myapplication.businessLogic.repository.BasketRepository
import com.example.myapplication.model.BasketService
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
class RestBasketRepository(
private var service: ServerService
): BasketRepository {
override suspend fun insertBasketService(basketService: BasketService) {
service.addServiceToBasket(basketService.toBasketServiceRemote())
}
override suspend fun getBasketWithServices(id: Int): Flow<List<Service>> {
val servicesRemoteList = service.getUserBasketServices(id)
val servicesList = servicesRemoteList.map { it.toService() }
return flowOf(servicesList.toList())
}
override suspend fun getUserBasketId(id: Int): Flow<Int> {
return flowOf(service.getUserBasket(id))
}
override suspend fun getQuantity(basketId: Int, serviceId: Int): Int? {
return service.getQuantity(basketId, serviceId)
}
//
// override fun getAllBasket(): Flow<List<Basket>> {
// TODO("Not yet implemented")
// }
//
// override suspend fun delete(basket: Basket) {
// TODO("Not yet implemented")
// }
//
// override suspend fun createBasket(basket: Basket): Long {
// TODO("Not yet implemented")
// }
//
override suspend fun removeServiceFromBasket(basketId: Int, serviceId: Int) {
service.deleteServiceFromBasket(basketId, serviceId)
}
//
// override suspend fun updateServiceQuantity(basketId: Int, serviceId: Int, quantity: Int) {
// TODO("Not yet implemented")
// }
//
override suspend fun incrementServiceQuantity(basketId: Int, serviceId: Int) {
service.increment(basketId, serviceId)
}
override suspend fun decrementServiceQuantity(basketId: Int, serviceId: Int) {
service.decrement(basketId, serviceId)
}
//
// override suspend fun getQuantity(basketId: Int, serviceId: Int): Int? {
// TODO("Not yet implemented")
// }
//
override suspend fun existService(basketId: Int, serviceId: Int): Boolean {
return service.getService(basketId, serviceId)
}
override suspend fun getTotalPriceForUser(userId: Int): Double? {
return service.getTotalPriceForUserBasket(userId)
}
override suspend fun deleteAllServiceFromBasket(basketId: Int) {
service.deleteAllServiceFromBasket(basketId)
}
}

View File

@ -0,0 +1,39 @@
package com.example.myapplication.api.repository
import com.example.myapplication.api.ServerService
import com.example.myapplication.api.model.toOrder
import com.example.myapplication.api.model.toOrderRemote
import com.example.myapplication.api.model.toOrderServiceRemote
import com.example.myapplication.api.model.toService
import com.example.myapplication.businessLogic.repository.OrderRepository
import com.example.myapplication.model.Order
import com.example.myapplication.model.OrderService
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
class RestOrderRepository(private val server: ServerService): OrderRepository {
override suspend fun insert(order: Order) {
server.createOrder(order.toOrderRemote())
}
override suspend fun addServiceToOrder(orderService: OrderService) {
server.addServiceToOrder(orderService.toOrderServiceRemote())
}
override suspend fun delete(order: Order) {
order.orderId?.let { this.server.deleteOrder(it) }
}
override suspend fun getServiceFromOrder(id: Int): Flow<List<Service>> {
val servicesRemoteList = server.getServiceFromOrder(id)
val servicesList = servicesRemoteList.map { it.toService() }
return flowOf(servicesList.toList())
}
override suspend fun getUserOrders(id: Int): Flow<List<Order>> {
val ordersRemoteList = server.getUserOrders(id)
val ordersList = ordersRemoteList.map { it.toOrder() }
return flowOf(ordersList.toList())
}
}

View File

@ -0,0 +1,11 @@
package com.example.myapplication.api.repository
import com.example.myapplication.api.ServerService
import com.example.myapplication.api.model.ReportRemote
import com.example.myapplication.businessLogic.repository.ReportRepository
class RestReportRepository(private var service: ServerService) : ReportRepository {
override suspend fun getReportData(dateFrom: Long, dateTo: Long): ReportRemote {
return service.getReport(dateFrom, dateTo)
}
}

View File

@ -0,0 +1,68 @@
package com.example.myapplication.api.repository
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.myapplication.api.ServerService
import com.example.myapplication.api.ServiceRemoteMediator
import com.example.myapplication.api.model.toService
import com.example.myapplication.api.model.toServiceRemote
import com.example.myapplication.businessLogic.repository.ServiceRepository
import com.example.myapplication.database.AppDatabase
import com.example.myapplication.database.repository.RemoteKeysRepositoryImpl
import com.example.myapplication.database.repository.ServiceRepositoryImpl
import com.example.myapplication.di.AppContainer
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
class RestServiceRepository(
private val service: ServerService,
private val dbServiceRepository: ServiceRepositoryImpl,
private val database: AppDatabase,
private val dbRemoteKeyRepository: RemoteKeysRepositoryImpl
) : ServiceRepository {
override suspend fun getAllServices(): Flow<PagingData<Service>> {
val pagingSourceFactory = {
dbServiceRepository.getAllServicesPagingSource()
}
@OptIn(ExperimentalPagingApi::class)
return Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
remoteMediator = ServiceRemoteMediator(
service,
dbServiceRepository,
database,
dbRemoteKeyRepository,
),
pagingSourceFactory = pagingSourceFactory
).flow
}
override suspend fun getServiceById(id: Int): Service = service.getService(id).toService()
override suspend fun insert(service: Service) {
this.service.createService(service.toServiceRemote())
}
override suspend fun update(service: Service) {
service.serviceId?.let { this.service.updateService(it, service.toServiceRemote()) }
}
override suspend fun delete(service: Service) {
try {
service.serviceId?.let { this.service.deleteService(it) }
dbServiceRepository.invalidateService(service.serviceId!!)
}catch (ex: Exception){}
}
override fun call(str: String): Flow<PagingData<Service>> {
return dbServiceRepository.call(str)
}
}

View File

@ -0,0 +1,27 @@
package com.example.myapplication.api.repository
import com.example.myapplication.api.ServerService
import com.example.myapplication.api.model.UserRemoteSignIn
import com.example.myapplication.api.model.toUser
import com.example.myapplication.api.model.toUserRemote
import com.example.myapplication.businessLogic.repository.UserRepository
import com.example.myapplication.model.User
class RestUserRepository(
private var service: ServerService
): UserRepository {
override suspend fun insert(user: User) {
service.SignUp(user.toUserRemote())
}
override suspend fun update(user: User) {
service.updateUser(user.toUserRemote())
}
override suspend fun delete(user: User) {
println()
}
override suspend fun authUser(user: UserRemoteSignIn): User {
return service.SignIn(user).toUser()
}
}

View File

@ -0,0 +1,18 @@
package com.example.myapplication.businessLogic.repository
import com.example.myapplication.model.BasketService
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
interface BasketRepository {
suspend fun insertBasketService(basketService: BasketService)
suspend fun getBasketWithServices(id: Int): Flow<List<Service>>
suspend fun getUserBasketId(id: Int): Flow<Int>
suspend fun removeServiceFromBasket(basketId: Int, serviceId: Int)
suspend fun incrementServiceQuantity(basketId: Int, serviceId: Int)
suspend fun decrementServiceQuantity(basketId: Int, serviceId: Int)
suspend fun getQuantity(basketId: Int, serviceId: Int): Int?
suspend fun existService(basketId: Int, serviceId: Int): Boolean
suspend fun getTotalPriceForUser(userId: Int): Double?
suspend fun deleteAllServiceFromBasket(basketId: Int)
}

View File

@ -0,0 +1,14 @@
package com.example.myapplication.businessLogic.repository
import com.example.myapplication.model.Order
import com.example.myapplication.model.OrderService
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
interface OrderRepository {
suspend fun insert(order: Order)
suspend fun addServiceToOrder(orderService: OrderService)
suspend fun delete(order: Order)
suspend fun getServiceFromOrder(id: Int): Flow<List<Service>>
suspend fun getUserOrders(id: Int) : Flow<List<Order>>
}

View File

@ -0,0 +1,10 @@
package com.example.myapplication.businessLogic.repository
import com.example.myapplication.model.RemoteKeyType
import com.example.myapplication.model.RemoteKeys
interface RemoteKeyRepository {
suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType): RemoteKeys?
suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>)
suspend fun deleteRemoteKey(type: RemoteKeyType)
}

View File

@ -0,0 +1,7 @@
package com.example.myapplication.businessLogic.repository
import com.example.myapplication.api.model.ReportRemote
interface ReportRepository {
suspend fun getReportData(dateFrom: Long, dateTo: Long): ReportRemote
}

View File

@ -0,0 +1,14 @@
package com.example.myapplication.businessLogic.repository
import androidx.paging.PagingData
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
interface ServiceRepository {
suspend fun insert(service: Service)
suspend fun update(service: Service)
suspend fun delete(service: Service)
suspend fun getServiceById(id: Int): Service
suspend fun getAllServices(): Flow<PagingData<Service>>
fun call(str: String): Flow<PagingData<Service>>
}

View File

@ -0,0 +1,11 @@
package com.example.myapplication.businessLogic.repository
import com.example.myapplication.api.model.UserRemoteSignIn
import com.example.myapplication.model.User
interface UserRepository {
suspend fun insert(user: User)
suspend fun update(user: User)
suspend fun delete(user: User)
suspend fun authUser(user: UserRemoteSignIn): User
}

View File

@ -0,0 +1,39 @@
package com.example.myapplication.businessLogic.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,
)
}
initializer {
ReportViewModel(app().container.reportRepo)
}
}
}
fun CreationExtras.app(): App =
(this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as App)

View File

@ -0,0 +1,142 @@
package com.example.myapplication.businessLogic.viewmodel
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableDoubleStateOf
import androidx.lifecycle.viewModelScope
import com.example.myapplication.GlobalUser
import com.example.myapplication.businessLogic.repository.BasketRepository
import com.example.myapplication.businessLogic.repository.OrderRepository
import com.example.myapplication.model.BasketService
import com.example.myapplication.model.Service
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class BasketViewModel(
private val basketRepository: BasketRepository,
private val orderRepository: OrderRepository
) : MyViewModel() {
private val _quantityMap = MutableStateFlow<Map<Int, MutableStateFlow<Int>>>(emptyMap())
val quantityMap: StateFlow<Map<Int, MutableStateFlow<Int>>> get() = _quantityMap
private val _total = mutableDoubleStateOf(0.00)
val total: State<Double> get() = _total
private val _myList = MutableStateFlow<List<Service>>(emptyList())
val myList: StateFlow<List<Service>> get() = _myList
private var _basketId = MutableStateFlow(0)
val basketId: StateFlow<Int> get() = _basketId
fun getQuantityState(basketId: Int, serviceId: Int): Flow<Int> {
val quantityMap = _quantityMap.value.toMutableMap()
val quantityStateFlow = quantityMap.getOrPut(serviceId) {
MutableStateFlow(0)
}
viewModelScope.launch {
val quantityFromDb = basketRepository.getQuantity(basketId, serviceId)
quantityFromDb?.let { quantityStateFlow.value = it }
}
_quantityMap.value = quantityMap.toMap()
return quantityStateFlow
}
suspend fun isServiceInBasket(basketId: Int, serviceId: Int): Boolean {
return basketRepository.existService(basketId, serviceId)
}
suspend fun addToBasket(serviceId: Int, quantity: Int) = viewModelScope.launch {
val basketId = GlobalUser.getInstance().getUser()?.basketId
val isServiceInBasket = isServiceInBasket(basketId!!, serviceId)
if (isServiceInBasket) {
incrementServiceQuantity(basketId, serviceId)
} else {
basketRepository.insertBasketService(BasketService(basketId, serviceId, quantity))
}
}
fun getBasketServices() = viewModelScope.launch {
val userId = GlobalUser.getInstance().getUser()?.userId!!
basketRepository.getBasketWithServices(userId)
.collect{services ->
_myList.value = services
}
}
fun getUsersBasket(id: Int) {
viewModelScope.launch{
val userBasketId = basketRepository.getUserBasketId(id).first()
withContext(Dispatchers.Main) {
_basketId.value = userBasketId
}
}
}
fun deleteServiceFromBasket(basketId: Int, serviceId: Int) {
viewModelScope.launch {
basketRepository.removeServiceFromBasket(basketId, serviceId)
_quantityMap.value.toMutableMap().apply {
remove(serviceId)
}
withContext(Dispatchers.Main){
updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!)
}
}
}
fun incrementServiceQuantity(basketId: Int, serviceId: Int) {
runInScope(
actionSuccess = {
basketRepository.incrementServiceQuantity(basketId, serviceId)
}
)
var currentQuantity: Int = 0
viewModelScope.launch {
currentQuantity = getQuantityState(basketId, serviceId).first()
}
_quantityMap.value.toMutableMap().apply {
put(serviceId, MutableStateFlow(currentQuantity + 1))
}
updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!)
}
fun decrementOrRemoveServiceQuantity(basketId: Int, serviceId: Int) = viewModelScope.launch{
val currentQuantity = getQuantityState(basketId, serviceId).first()
if (currentQuantity > 1) {
runInScope(
actionSuccess = { basketRepository.decrementServiceQuantity(basketId, serviceId) }
)
_quantityMap.value.toMutableMap().apply {
put(serviceId, MutableStateFlow(currentQuantity - 1))
}
}
else{
runInScope(
actionSuccess = { basketRepository.removeServiceFromBasket(basketId, serviceId) }
)
_quantityMap.value.toMutableMap().apply {
remove(serviceId, MutableStateFlow(currentQuantity - 1))
}
getBasketServices()
}
updateSubTotal(GlobalUser.getInstance().getUser()?.userId!!)
}
fun updateSubTotal(userId: Int) {
viewModelScope.launch {
_total.doubleValue = getTotal(userId)
}
}
suspend fun getTotal(userId: Int): Double {
val price = basketRepository.getTotalPriceForUser(userId) ?: 0.00
return price
}
}

View File

@ -0,0 +1,50 @@
package com.example.myapplication.businessLogic.viewmodel
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.api.ApiStatus
import kotlinx.coroutines.launch
import retrofit2.HttpException
import java.io.IOException
open class MyViewModel : ViewModel() {
var apiStatus by mutableStateOf(ApiStatus.DONE)
var apiError by mutableStateOf("")
private set
fun runInScope(
actionSuccess: suspend () -> Unit,
actionError: suspend () -> Unit
) {
viewModelScope.launch {
apiStatus = ApiStatus.LOADING
println(apiStatus)
runCatching {
actionSuccess()
apiStatus = ApiStatus.DONE
println(apiStatus)
apiError = ""
}.onFailure { e: Throwable ->
when (e) {
is IOException ,
is HttpException -> {
apiStatus = ApiStatus.ERROR
println(apiStatus)
actionError()
apiError = e.localizedMessage ?: e.toString()
}
else -> throw e
}
}
}
}
fun runInScope(actionSuccess: suspend () -> Unit) {
runInScope(actionSuccess, actionError = {})
}
}

View File

@ -0,0 +1,80 @@
package com.example.myapplication.businessLogic.viewmodel
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableDoubleStateOf
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.example.myapplication.GlobalUser
import com.example.myapplication.businessLogic.repository.BasketRepository
import com.example.myapplication.businessLogic.repository.OrderRepository
import com.example.myapplication.model.Order
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.launch
import java.util.Date
class OrderViewModel(
private val orderRepository: OrderRepository,
private val basketRepository: BasketRepository
) : MyViewModel() {
private var _selectedItems = MutableLiveData<List<Service>>()
val selectedItems get() = _selectedItems
private val _total = mutableDoubleStateOf(0.00)
val total: State<Double> get() = _total
private var _orders = MutableStateFlow<List<Order>>(emptyList())
val orders: StateFlow<List<Order>> get() = _orders
fun createOrder(): Boolean {
viewModelScope.launch {
val userId = GlobalUser.getInstance().getUser()?.userId!!
val order = Order(
date = Date().time,
total = getTotal(userId),
creatorUserId = userId
)
runInScope(
actionSuccess = { orderRepository.insert(order) }
)
}
return true
}
suspend fun getOrderWithServices(id: Int) : Flow<List<Service>> {
return try{
orderRepository.getServiceFromOrder(id)
}catch (e: Exception){
emptyFlow()
}
}
suspend fun getUserOrders(id: Int) {
runInScope(
actionSuccess = {
viewModelScope.launch {
orderRepository.getUserOrders(id)
.collect{items ->
_orders.value = items
}
}
}
)
}
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.getTotalPriceForUser(basketId) ?: 0.00
}
}

View File

@ -0,0 +1,29 @@
package com.example.myapplication.businessLogic.viewmodel
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.example.myapplication.api.model.ServiceWithCount
import com.example.myapplication.businessLogic.repository.ReportRepository
import kotlinx.coroutines.launch
import java.util.Date
class ReportViewModel(private val reportRepository: ReportRepository): MyViewModel() {
val dateFrom = mutableStateOf(0L)
val dateTo = mutableStateOf(Date().time)
val avgSum = mutableStateOf(0.0)
val totalEarn = mutableStateOf(0.0)
val countOrder = mutableStateOf(0)
var serviceList = MutableLiveData<List<ServiceWithCount>>()
fun updateReportData(dateFrom: Long, dateTo: Long) {
viewModelScope.launch {
val report = reportRepository.getReportData(dateFrom, dateTo)
countOrder.value = report.countOrder
avgSum.value = report.avgCheck!!
totalEarn.value = report.totalEarn!!
serviceList.value = report.serviceList
}
}
}

View File

@ -0,0 +1,75 @@
package com.example.myapplication.businessLogic.viewmodel
import android.graphics.Bitmap
import androidx.compose.runtime.mutableDoubleStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.example.myapplication.businessLogic.repository.ServiceRepository
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.launch
class ServiceViewModel(
private val serviceRepository: ServiceRepository
): MyViewModel() {
var name = mutableStateOf("")
var price = mutableDoubleStateOf(0.00)
private val _serviceList = MutableStateFlow<Flow<PagingData<Service>>>(emptyFlow())
val serviceList: StateFlow<Flow<PagingData<Service>>> get() = _serviceList
init {
runInScope(
actionSuccess = { getServiceList() }
)
}
fun insertService(photo: Bitmap) {
val service = Service(
name = name.value,
price = price.doubleValue,
photo = photo
)
runInScope(
actionSuccess = { serviceRepository.insert(service) }
)
}
fun deleteService(service : Service) {
runInScope(
actionSuccess = {
serviceRepository.delete(service)
getServiceList()
}
)
}
fun updateService(service: Service) {
runInScope(
actionSuccess = {
serviceRepository.update(service)
getServiceList()
}
)
}
fun getServiceList(){
try{
viewModelScope.launch{
_serviceList.value = serviceRepository.getAllServices()
}
}catch(e: Exception){}
}
fun searchServicesByFilter(searchText: String){
viewModelScope.launch {
val filteredServices = serviceRepository.call(searchText).cachedIn(viewModelScope)
_serviceList.value = filteredServices
}
}
}

View File

@ -0,0 +1,59 @@
package com.example.myapplication.businessLogic.viewmodel
import android.graphics.Bitmap
import androidx.compose.runtime.mutableStateOf
import com.example.myapplication.GlobalUser
import com.example.myapplication.api.model.UserRemoteSignIn
import com.example.myapplication.businessLogic.repository.BasketRepository
import com.example.myapplication.businessLogic.repository.UserRepository
import com.example.myapplication.model.RoleEnum
import com.example.myapplication.model.User
class UserViewModel(
private val userRepository: UserRepository,
private val basketRepository: BasketRepository
): MyViewModel() {
var name = mutableStateOf("")
var surname = mutableStateOf("")
var email = mutableStateOf("")
var password = mutableStateOf("")
fun createUser(photo: Bitmap){
val user =
User(
name = name.value,
surname = surname.value,
email = email.value,
password = password.value,
role = RoleEnum.User,
photo = photo
)
runInScope(
actionSuccess = {
userRepository.insert(user)
}
)
}
fun authUser() {
runInScope(
actionSuccess = {
val user = userRepository.authUser(UserRemoteSignIn(email.value, password.value))
GlobalUser.getInstance().setUser(user)
},
)
}
fun updateUser(user: User){
runInScope(
actionSuccess = {
userRepository.update(user)
}
)
}
fun isValidEmail(): Boolean {
return android.util.Patterns.EMAIL_ADDRESS.matcher(email.value).matches()
}
}

View File

@ -0,0 +1,144 @@
package com.example.myapplication.composeui.Basket
import android.widget.Toast
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.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.foundation.shape.RoundedCornerShape
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.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
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.api.ApiStatus
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.BasketViewModel
import com.example.myapplication.businessLogic.viewmodel.OrderViewModel
import com.example.myapplication.composeui.Navbar.NavItem
import com.example.myapplication.composeui.NetworkUI.Loading
import com.example.myapplication.composeui.Profile.Login
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
@Composable
fun Basket(navController : NavHostController,
basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory),
orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
val user = GlobalUser.getInstance().getUser()
val context = LocalContext.current
if (basketViewModel.apiStatus == ApiStatus.ERROR) {
Toast.makeText(context, "Error: " + basketViewModel.apiError, Toast.LENGTH_SHORT).show()
return
}
when (basketViewModel.apiStatus) {
ApiStatus.LOADING -> Loading()
ApiStatus.DONE ->
if (user == null) {
Login(navController = navController)
} else {
basketViewModel.updateSubTotal(user.userId!!)
val total = basketViewModel.total.value
LaunchedEffect(basketViewModel) {
basketViewModel.getBasketServices()
}
val serviceList by basketViewModel.myList.collectAsState()
orderViewModel.updateSelectedItems(serviceList)
Column(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Text(
text = "PetMed",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White, fontSize = TextUnit(8.0f, TextUnitType.Em))
)
}
for (item in serviceList) {
BasketItemUI(item = item)
}
Box(
modifier = Modifier
.padding(15.dp, 0.dp)
.clip(RoundedCornerShape(15.dp, 15.dp, 0.dp, 0.dp))
.background(Color.Transparent)
.height(130.dp),
) {
Column(
modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.background(Color.White)
.padding(PaddingValues(15.dp)),
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = "Total: ",
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.weight(1f)
)
Text(
text = "$$total",
style = MaterialTheme.typography.bodyMedium
)
}
}
Button(
onClick = {
orderViewModel.createOrder()
navController.navigate(NavItem.ListOfServices.route)
},
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
.clip(CircleShape)
.align(Alignment.BottomCenter),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(
text = "Confirm order",
style = MaterialTheme.typography.bodyMedium.copy(Color.White)
)
}
}
}
}
else -> {}
}
}

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,38 +13,43 @@ 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.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.runtime.rememberCoroutineScope
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.asImageBitmap
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.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.myapplication.GlobalUser
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.BasketViewModel
import com.example.myapplication.businessLogic.viewmodel.OrderViewModel
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 kotlinx.coroutines.launch
@Composable
fun BasketItem(item: TestServiceItem){
fun BasketItemUI(item: Service, basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory), orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
val user = GlobalUser.getInstance().getUser()
val basketId = user?.basketId!!
val quantityState: Int by basketViewModel.getQuantityState(basketId, item.serviceId!!).collectAsState(0)
val coroutineScope = rememberCoroutineScope()
Box(
modifier = Modifier
.padding(0.dp, 0.dp, 0.dp, 10.dp)
@ -54,7 +59,7 @@ fun BasketItem(item: TestServiceItem){
shape = RoundedCornerShape(15.dp),
clip = false
),
){
) {
Row(
modifier = Modifier
.fillMaxWidth()
@ -63,34 +68,26 @@ 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,
)
) {
Image(
bitmap = item.photo.asImageBitmap(),
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 +98,24 @@ 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 = {
coroutineScope.launch {
basketViewModel.incrementServiceQuantity(basketId, item.serviceId!!)
}
},
modifier = Modifier
.size(42.dp)
.fillMaxWidth()
@ -129,7 +129,11 @@ fun BasketItem(item: TestServiceItem){
Text(text = "+")
}
Button(
onClick = { num-=1 },
onClick = {
basketViewModel.viewModelScope.launch{
basketViewModel.decrementOrRemoveServiceQuantity(basketId, item.serviceId!!)
}
},
modifier = Modifier
.size(42.dp)
.fillMaxWidth()

View File

@ -0,0 +1,225 @@
package com.example.myapplication.composeui.List_of_Services
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.ImageDecoder
import android.net.Uri
import android.os.Build
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
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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
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.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically
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.asImageBitmap
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
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.R
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.ServiceViewModel
import com.example.myapplication.composeui.Navbar.NavItem
import com.example.myapplication.composeui.NetworkUI.Loading
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.TextPrimary
@Composable
fun AddService (
navController: NavController,
serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val context = LocalContext.current
val serviceImage = remember { mutableStateOf<Bitmap>(BitmapFactory.decodeResource(context.resources, R.drawable.image_service)) }
val imageData = remember { mutableStateOf<Uri?>(null) }
val launcher =
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
imageData.value = uri
}
imageData.value?.let {
if (Build.VERSION.SDK_INT < 28) {
serviceImage.value = MediaStore.Images
.Media.getBitmap(context.contentResolver, imageData.value)
} else {
val source = ImageDecoder
.createSource(context.contentResolver, imageData.value!!)
serviceImage.value = ImageDecoder.decodeBitmap(source)
}
}
if (serviceViewModel.apiStatus == ApiStatus.ERROR) {
Toast.makeText(context, "Error: " + serviceViewModel.apiError, Toast.LENGTH_SHORT).show()
return
}
when (serviceViewModel.apiStatus) {
ApiStatus.LOADING -> Loading()
ApiStatus.DONE ->
Column(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp, 0.dp, 15.dp, 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Text(
text = "PetMed",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White, fontSize = TextUnit(8.0f, TextUnitType.Em))
)
}
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 = CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Image(
bitmap = serviceImage.value.asImageBitmap(),
contentDescription = null,
modifier = Modifier.align(CenterVertically),
)
Column(
modifier = Modifier
.fillMaxHeight()
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
verticalArrangement = Arrangement.Top,
) {
Text(
text = serviceViewModel.name.value,
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium
)
}
Column(
modifier = Modifier
.fillMaxHeight()
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = serviceViewModel.price.doubleValue.toString(),
style = MaterialTheme.typography.bodyMedium,
)
}
}
}
Column(
) {
Row(modifier = Modifier.padding(vertical = 5.dp)) {
MyTextField(label = "Service name") {
serviceViewModel.name.value = it
}
}
Row(modifier = Modifier.padding(vertical = 5.dp)) {
MyTextField(label = "Price") {
try {
serviceViewModel.price.doubleValue = it.toDouble()
}catch (e: Exception){
serviceViewModel.price.doubleValue = 0.0
Toast.makeText(context, "Input correct price!", Toast.LENGTH_SHORT).show()
}
}
}
}
Button(
onClick = {
launcher.launch("image/*")
},
modifier = Modifier
.height(60.dp)
.padding(top = 10.dp)
.fillMaxWidth()
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(
text = "Upload image",
style = MaterialTheme.typography.bodyMedium.copy(Color.White)
)
}
Button(
onClick = {
if (serviceViewModel.name.value == ""
|| serviceViewModel.price.doubleValue == 0.0)
Toast.makeText(context, "Input correct value!", Toast.LENGTH_SHORT).show()
else {
serviceViewModel.insertService(serviceImage.value)
navController.navigate(NavItem.AddService.route)
}
},
modifier = Modifier
.height(60.dp)
.padding(top = 10.dp)
.fillMaxWidth()
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(
text = "Add service",
style = MaterialTheme.typography.bodyMedium.copy(Color.White)
)
}
}
else -> {}
}
}

View File

@ -0,0 +1,266 @@
package com.example.myapplication.composeui.List_of_Services
import android.graphics.Bitmap
import android.graphics.ImageDecoder
import android.net.Uri
import android.os.Build
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.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
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.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.foundation.text.BasicTextField
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.mutableStateOf
import androidx.compose.runtime.remember
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.asImageBitmap
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
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.api.ApiStatus
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.ServiceViewModel
import com.example.myapplication.composeui.NetworkUI.Loading
import com.example.myapplication.model.Service
import com.example.myapplication.ui.theme.BlueBorder
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextPrimary
@Composable
fun ChangeService(
service: Service,
navController: NavHostController,
serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)
){
val context = LocalContext.current
val name = remember { mutableStateOf(service.name) }
val price = remember { mutableStateOf(service.price) }
val photo = remember { mutableStateOf<Bitmap>(service.photo) }
val imageData = remember { mutableStateOf<Uri?>(null) }
val launcher =
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
imageData.value = uri
}
imageData.value?.let {
if (Build.VERSION.SDK_INT < 28) {
photo.value = MediaStore.Images
.Media.getBitmap(context.contentResolver, imageData.value)
} else {
val source = ImageDecoder
.createSource(context.contentResolver, imageData.value!!)
photo.value = ImageDecoder.decodeBitmap(source)
}
}
when (serviceViewModel.apiStatus) {
ApiStatus.ERROR -> Toast.makeText(context, "Error: " + serviceViewModel.apiError, Toast.LENGTH_SHORT).show()
ApiStatus.LOADING -> Loading()
ApiStatus.DONE ->
Column(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp, 0.dp, 15.dp, 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Text(
text = "PetMed",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White, fontSize = TextUnit(8.0f, TextUnitType.Em))
)
}
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(
bitmap = photo.value.asImageBitmap(),
contentDescription = null,
modifier = Modifier.align(Alignment.CenterVertically),
)
Column(
modifier = Modifier
.fillMaxHeight()
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
verticalArrangement = Arrangement.Top,
) {
Text(
text = service.name,
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium
)
}
Column(
modifier = Modifier
.fillMaxHeight()
.widthIn(max = (LocalConfiguration.current.screenWidthDp / 3).dp),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = service.price.toString(),
style = MaterialTheme.typography.bodyMedium,
)
}
}
}
Column(
) {
Row (
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.clip(RoundedCornerShape(15.dp))
.fillMaxWidth()
.background(Color.White)
.border(2.dp, color = BlueBorder, RoundedCornerShape(15.dp))
.height(45.dp)
.padding(15.dp, 5.dp),
){
BasicTextField(
value = name.value,
onValueChange = {
name.value = it
},
modifier = Modifier.fillMaxWidth(),
textStyle = MaterialTheme.typography.bodyMedium,
singleLine = true,
)
}
Spacer(modifier = Modifier.size(10.dp))
Row (
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.clip(RoundedCornerShape(15.dp))
.fillMaxWidth()
.background(Color.White)
.border(2.dp, color = BlueBorder, RoundedCornerShape(15.dp))
.height(45.dp)
.padding(15.dp, 5.dp),
){
BasicTextField(
value = price.value.toString(),
onValueChange = {
try {
price.value = it.toDouble()
}catch (e: Exception){
price.value = 0.0
serviceViewModel.price.doubleValue = 0.0
Toast.makeText(context, "Input correct price!", Toast.LENGTH_SHORT).show()
}
},
modifier = Modifier.fillMaxWidth(),
textStyle = MaterialTheme.typography.bodyMedium,
singleLine = true,
)
}
}
Button(
onClick = {
launcher.launch("image/*")
},
modifier = Modifier
.height(60.dp)
.padding(top = 10.dp)
.fillMaxWidth()
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(
text = "Upload image",
style = MaterialTheme.typography.bodyMedium.copy(Color.White)
)
}
Button(
onClick = {
if (service.name == ""
|| service.price == 0.0
|| price.value == 0.0)
Toast.makeText(context, "Input correct value!", Toast.LENGTH_SHORT).show()
else{
serviceViewModel.updateService(
Service(
serviceId = service.serviceId,
name = name.value,
price = price.value,
photo = photo.value
)
)
}
},
modifier = Modifier
.height(60.dp)
.padding(top = 10.dp)
.fillMaxWidth()
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(
text = "Save changes",
style = MaterialTheme.typography.bodyMedium.copy(Color.White)
)
}
}
else -> {}
}
}

View File

@ -0,0 +1,89 @@
package com.example.myapplication.composeui.List_of_Services
import SearchBar
import android.widget.Toast
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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
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.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
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 androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.ServiceViewModel
import com.example.myapplication.composeui.NetworkUI.Loading
import com.example.myapplication.model.Service
import com.example.myapplication.ui.theme.BlueMain
@Composable
fun ListOfServices(navController: NavHostController, serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)){
LaunchedEffect(serviceViewModel){
serviceViewModel.getServiceList()
}
val services = serviceViewModel.serviceList.collectAsState().value.collectAsLazyPagingItems()
val context = LocalContext.current
if (serviceViewModel.apiStatus == ApiStatus.ERROR){
Toast.makeText(context, "Error: " + serviceViewModel.apiError, Toast.LENGTH_SHORT).show()
return
}
when(serviceViewModel.apiStatus) {
ApiStatus.LOADING -> Loading()
ApiStatus.DONE -> Column(
modifier = Modifier
.background(BlueMain)
.fillMaxSize()
.padding(bottom = 60.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Text(
text = "PetMed",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White, fontSize = TextUnit(8.0f, TextUnitType.Em))
)
}
LazyColumn(
) {
item {
SearchBar(
modifier = Modifier
)
{ searchText ->
serviceViewModel.searchServicesByFilter(searchText)
}
}
items(
count = services.itemCount,
key = services.itemKey { service -> service.serviceId!! }
) { index: Int ->
val service: Service? = services[index]
if (service != null) {
Service(navController, item = service)
}
}
}
}
else -> {}
}
}

View File

@ -0,0 +1,178 @@
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.runtime.LaunchedEffect
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.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.example.myapplication.GlobalUser
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.BasketViewModel
import com.example.myapplication.businessLogic.viewmodel.ServiceViewModel
import com.example.myapplication.composeui.Navbar.NavItem
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.google.gson.Gson
import kotlinx.coroutines.launch
@Composable
fun Service(
navController: NavHostController,
item: Service,
basketViewModel: BasketViewModel = viewModel(factory = AppViewModelProvider.Factory),
serviceViewModel: ServiceViewModel = viewModel(factory = AppViewModelProvider.Factory)
){
val user = GlobalUser.getInstance().getUser()
val basketId by basketViewModel.basketId.collectAsState()
LaunchedEffect(basketViewModel){
if(user!=null && basketId == 0) basketViewModel.getUsersBasket(user.userId!!)
}
Box(
modifier = Modifier
.padding(10.dp, 0.dp, 10.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(
bitmap = item.photo.asImageBitmap(),
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 = {
basketViewModel.viewModelScope.launch {
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 = {
if(user != null)
basketViewModel.viewModelScope.launch {
basketViewModel.addToBasket(item.serviceId!!, 1)
}
else
navController.navigate(NavItem.Login.route)
},
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 = {
navController.navigate("change_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,49 +1,31 @@
package com.example.myapplication.Navbar
package com.example.myapplication.composeui.Navbar
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
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.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountBox
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ControlledComposition
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.max
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat.getColor
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.example.myapplication.R
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.BlueNavbar
import com.example.myapplication.ui.theme.GreenBtn
@ -67,26 +49,34 @@ 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)
null,
modifier = if (isSelected){
Modifier.background(BlueMain, CircleShape).size(50.dp).padding(8.dp)
} else {
Modifier.size(50.dp).padding(8.dp)
},
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,82 @@
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.ChangeService
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.composeui.Profile.Report
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
){
AddService(navController)
}
composable(
NavItem.ChangeService.route
){
backStackEntry ->
val serviceItemString = backStackEntry.arguments?.getString("serviceItem")
val serviceItem = Gson().fromJson(serviceItemString, Service::class.java)
serviceItem?.let { ChangeService(it, navController)
}
}
composable(
NavItem.Report.route
){
Report()
}
}
}

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
@ -47,4 +39,12 @@ sealed class NavItem(
"add_service",
R.drawable.icon_list_of_services
)
object ChangeService : NavItem(
"change_service/{serviceItem}",
R.drawable.icon_list_of_services
)
object Report : NavItem(
"report",
R.drawable.profile
)
}

View File

@ -0,0 +1,46 @@
package com.example.myapplication.composeui.NetworkUI
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
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 com.example.myapplication.R
@Composable
fun ErrorPlaceholder(message: String, onBack: () -> Unit) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(10.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
fontSize = TextUnit(value = 20F, type = TextUnitType.Sp),
text = message,
color = Color(0xFFFF1744)
)
Spacer(modifier = Modifier.padding(bottom = 10.dp))
Button(
modifier = Modifier.fillMaxWidth(),
onClick = { onBack() }
) {
Text(stringResource(id = R.string.back))
}
}
}

View File

@ -0,0 +1,41 @@
package com.example.myapplication.composeui.NetworkUI
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.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.graphics.Color
import androidx.compose.ui.res.stringResource
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 com.example.myapplication.R
import com.example.myapplication.ui.theme.BlueMain
@Composable
fun Loading() {
Column(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(10.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyMedium
.copy(Color.White, fontSize = TextUnit(16.0f, TextUnitType.Em)),
text = stringResource(id = R.string.loading)
)
}
}

View File

@ -0,0 +1,80 @@
package com.example.myapplication.composeui.Orders
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.OrderViewModel
import com.example.myapplication.model.Order
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.first
import java.text.SimpleDateFormat
import java.util.Date
@Composable
fun OrderItem (
order: Order,
orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)
){
val dateFormat = SimpleDateFormat("dd-MM-yyyy")
var services by remember { mutableStateOf<List<Service>>(emptyList()) }
var expanded = remember { mutableStateOf(false) }
LaunchedEffect(order.orderId) {
services = orderViewModel.getOrderWithServices(order.orderId!!).first()
}
Column (
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
.clip(RoundedCornerShape(15.dp))
.clickable {
expanded.value = !expanded.value
}
){
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)
}
if(expanded.value){
LazyRow(){
itemsIndexed(services ?: emptyList()) { index, service ->
OrderScrollService(service)
}
}
}
}
}

View File

@ -0,0 +1,48 @@
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.collectAsState
import androidx.compose.runtime.getValue
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.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.OrderViewModel
import com.example.myapplication.ui.theme.BlueMain
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun Orders (
navController: NavController,
orderViewModel: OrderViewModel = viewModel(factory = AppViewModelProvider.Factory)
){
val user = GlobalUser.getInstance().getUser()
val ordersList by orderViewModel.orders.collectAsState()
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
orderViewModel.getUserOrders(user?.userId!!)
}
}
LazyColumn(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
){
itemsIndexed(ordersList){_, item ->
OrderItem(item)
}
}
}

View File

@ -0,0 +1,77 @@
package com.example.myapplication.composeui.Orders
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.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.widthIn
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.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.dp
import com.example.myapplication.model.Service
import com.example.myapplication.ui.theme.TextPrimary
@Composable
fun OrderScrollService(item: Service){
val maxWidth = (LocalConfiguration.current.screenWidthDp/2)
Box(
modifier = Modifier
.padding(4.dp)
.widthIn(max = maxWidth.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(145.dp)
.background(color = Color.White, RoundedCornerShape(15.dp))
.padding(15.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
){
Image(
bitmap = item.photo.asImageBitmap(),
contentDescription = null,
modifier = Modifier
.fillMaxHeight()
.heightIn(min = 100.dp)
.padding(5.dp)
.widthIn(max = (maxWidth / 2).dp),
contentScale = ContentScale.FillHeight,
)
Column(
modifier = Modifier
.fillMaxHeight()
.widthIn(max = (maxWidth / 2).dp),
verticalArrangement = Arrangement.Top,
){
Text(text = item.name,
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium)
Text(text = item.price.toString(),
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium)
Text(
text = "$${item.price}",
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}

View File

@ -0,0 +1,171 @@
package com.example.myapplication.composeui.Profile
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.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.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
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.PasswordVisualTransformation
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.GlobalUser
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.UserViewModel
import com.example.myapplication.composeui.Navbar.NavItem
import com.example.myapplication.composeui.NetworkUI.Loading
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
@Composable
fun Login (
navController: NavController,
userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)
){
val context = LocalContext.current
var isEmailValid by remember { mutableStateOf(true) }
var isPasswordValid by remember { mutableStateOf(true) }
if(GlobalUser.getInstance().getUser() != null){
navController.navigate(NavItem.Profile.route)
}
when(userViewModel.apiStatus){
ApiStatus.LOADING -> Loading()
ApiStatus.ERROR -> {
Toast.makeText(context, "Error: " + userViewModel.apiError, Toast.LENGTH_SHORT).show()
userViewModel.apiStatus = ApiStatus.DONE
}
ApiStatus.DONE ->
Column(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
){
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 30.dp),
horizontalArrangement = Arrangement.Center
){
Text(
text = "PetMed",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White, fontSize = TextUnit(16.0f, TextUnitType.Em))
)
}
Column (
){
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 = "Password", visualTransformation = PasswordVisualTransformation(), onValueChanged = {
userViewModel.password.value = it
isPasswordValid = it.isNotEmpty()
})
}
}
Button(
onClick = {
if (userViewModel.email.value == ""
|| !isEmailValid
|| !isPasswordValid){
Toast.makeText(context, "Input correct value!", Toast.LENGTH_SHORT).show()
}
else{
userViewModel.authUser()
}
},
modifier = Modifier
.height(60.dp)
.padding(top = 10.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)
)
}
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
){
Text(
text = "Don't have an account? ",
style = MaterialTheme.typography.bodyMedium
.copy(TextSecondary)
)
Text(
text = "Sign up",
style = MaterialTheme.typography.bodyMedium
.copy(GreenBtn),
modifier = Modifier
.clickable { navController.navigate(NavItem.Registration.route) }
)
}
}
}
}

View File

@ -1,10 +1,12 @@
package com.example.myapplication.Profile
package com.example.myapplication.composeui.Profile
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.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
@ -21,19 +23,27 @@ 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.graphics.asImageBitmap
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.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()
@ -42,33 +52,49 @@ fun Profile(navController: NavHostController){
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
){
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
){
Text(
text = "PetMed",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White, fontSize = TextUnit(8.0f, TextUnitType.Em))
)
}
Box(modifier = Modifier
.clip(CircleShape)
.size(200.dp)
.background(Color.White)
){
// 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,
Image(
bitmap = user.photo.asImageBitmap(),
contentDescription = null,
modifier = Modifier.align(Alignment.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 +133,51 @@ 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(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
)
}
Button(
onClick = { navController.navigate(NavItem.Report.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 = "Reports",
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 +201,5 @@ fun Profile(navController: NavHostController){
)
}
}
}
}

View File

@ -0,0 +1,309 @@
package com.example.myapplication.composeui.Profile
import android.graphics.Bitmap
import android.graphics.ImageDecoder
import android.net.Uri
import android.os.Build
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.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
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
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.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
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
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.api.ApiStatus
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.UserViewModel
import com.example.myapplication.composeui.Navbar.NavItem
import com.example.myapplication.composeui.NetworkUI.Loading
import com.example.myapplication.model.User
import com.example.myapplication.ui.theme.BlueBorder
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.GreenBtn
@Composable
fun ProfileChange (navController: NavHostController, userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val user = GlobalUser.getInstance().getUser()
val name = remember { mutableStateOf(user?.name) }
val surname = remember { mutableStateOf(user?.surname) }
val email = remember { mutableStateOf(user?.email) }
val password = remember { mutableStateOf(user?.password) }
val context = LocalContext.current
val imageData = remember { mutableStateOf<Uri?>(null) }
val profileImage = remember { mutableStateOf<Bitmap>(user?.photo!!) }
val launcher =
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {uri: Uri? ->
imageData.value = uri
}
imageData.value?.let {
if (Build.VERSION.SDK_INT < 28) {
profileImage.value = MediaStore.Images
.Media.getBitmap(context.contentResolver, imageData.value)
} else {
val source = ImageDecoder
.createSource(context.contentResolver, imageData.value!!)
profileImage.value = ImageDecoder.decodeBitmap(source)
}
}
var isEmailValid by remember { mutableStateOf(true) }
var isPasswordValid by remember { mutableStateOf(true) }
when(userViewModel.apiStatus){
ApiStatus.LOADING -> Loading()
ApiStatus.DONE -> Column (
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
){
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
){
Text(
text = "PetMed",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White, fontSize = TextUnit(8.0f, TextUnitType.Em))
)
}
Box(modifier = Modifier
.clip(CircleShape)
.size(200.dp)
.background(Color.White)
.padding(PaddingValues(0.dp))
){
Image(
bitmap = profileImage.value.asImageBitmap(),
contentDescription = null,
modifier = Modifier.align(Alignment.Center),
)
}
Box(modifier = Modifier.padding(15.dp)){
Text(
text = "${name.value} ${surname.value}",
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center),
textAlign = TextAlign.Center,
)
}
Box(modifier = Modifier.padding(15.dp)){
Text(
text = "${email.value}",
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center),
textAlign = TextAlign.Center,
)
}
Column (
){
Spacer(modifier = Modifier.size(5.dp))
Row (
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.clip(RoundedCornerShape(15.dp))
.fillMaxWidth()
.background(Color.White)
.border(2.dp, color = BlueBorder, RoundedCornerShape(15.dp))
.height(45.dp)
.padding(15.dp, 5.dp),
){
BasicTextField(
value = name.value!!,
onValueChange = {
name.value = it
},
modifier = Modifier.fillMaxWidth(),
textStyle = MaterialTheme.typography.bodyMedium,
singleLine = true,
)
}
Spacer(modifier = Modifier.size(5.dp))
Row (
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.clip(RoundedCornerShape(15.dp))
.fillMaxWidth()
.background(Color.White)
.border(2.dp, color = BlueBorder, RoundedCornerShape(15.dp))
.height(45.dp)
.padding(15.dp, 5.dp),
){
BasicTextField(
value = surname.value!!,
onValueChange = {
surname.value = it
},
modifier = Modifier.fillMaxWidth(),
textStyle = MaterialTheme.typography.bodyMedium,
singleLine = true,
)
}
Spacer(modifier = Modifier.size(5.dp))
if (!isEmailValid) {
Text(
text = "Invalid email format",
style = MaterialTheme.typography.bodyMedium
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
)
}
Row (
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.clip(RoundedCornerShape(15.dp))
.fillMaxWidth()
.background(Color.White)
.border(2.dp, color = BlueBorder, RoundedCornerShape(15.dp))
.height(45.dp)
.padding(15.dp, 5.dp),
){
BasicTextField(
value = email.value!!,
onValueChange = {
email.value = it
isEmailValid = isValidEmail(email.value!!)
},
modifier = Modifier.fillMaxWidth(),
textStyle = MaterialTheme.typography.bodyMedium,
singleLine = true,
)
}
Spacer(modifier = Modifier.size(5.dp))
if (!isPasswordValid) {
Text(
text = "Password is required",
style = MaterialTheme.typography.bodyMedium
.copy(Color.Red, fontSize = TextUnit(4.0f, TextUnitType.Em))
)
}
Row (
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.clip(RoundedCornerShape(15.dp))
.fillMaxWidth()
.background(Color.White)
.border(2.dp, color = BlueBorder, RoundedCornerShape(15.dp))
.height(45.dp)
.padding(15.dp, 5.dp),
){
BasicTextField(
value = password.value.toString(),
onValueChange = {
password.value = it
isPasswordValid = it.isNotEmpty()
},
modifier = Modifier.fillMaxWidth(),
textStyle = MaterialTheme.typography.bodyMedium,
singleLine = true,
)
}
}
Spacer(modifier = Modifier.size(5.dp))
Button(
onClick = {
launcher.launch("image/*")
},
modifier = Modifier
.height(50.dp)
.fillMaxWidth()
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(
text = "Upload image",
style = MaterialTheme.typography.bodyMedium.copy(Color.White)
)
}
Spacer(modifier = Modifier.size(5.dp))
Button(
onClick = {
if (!isEmailValid
|| !isPasswordValid
|| name.value == ""
|| surname.value == "")
Toast.makeText(context, "Input correct value!", Toast.LENGTH_SHORT).show()
else {
val updatedUser = User(
user?.userId!!,
name = name.value!!,
surname = surname.value!!,
email = email.value!!,
password = password.value!!,
role = user.role,
photo = profileImage.value,
basketId = user.basketId)
userViewModel.updateUser(updatedUser)
GlobalUser.getInstance().setUser(updatedUser)
navController.navigate(NavItem.Profile.route)
}
},
modifier = Modifier
.height(50.dp)
.fillMaxWidth()
.clip(CircleShape),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
contentPadding = PaddingValues(0.dp),
) {
Text(text = "Confirm changes", style = MaterialTheme.typography.bodyMedium.copy(Color.White))
}
}
else -> {}
}
}
fun isValidEmail(str: String): Boolean {
return android.util.Patterns.EMAIL_ADDRESS.matcher(str).matches()
}

View File

@ -0,0 +1,172 @@
package com.example.myapplication.composeui.Profile
import android.graphics.BitmapFactory
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.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.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
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.PasswordVisualTransformation
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.R
import com.example.myapplication.api.ApiStatus
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.UserViewModel
import com.example.myapplication.composeui.Navbar.NavItem
import com.example.myapplication.composeui.NetworkUI.Loading
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
@Composable
fun Registration (navController: NavController, userViewModel: UserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val context = LocalContext.current
if (userViewModel.apiStatus == ApiStatus.ERROR){
Toast.makeText(context, "Error: " + userViewModel.apiError, Toast.LENGTH_SHORT).show()
return
}
var isEmailValid by remember { mutableStateOf(true) }
var isPasswordValid by remember { mutableStateOf(true) }
val photo = BitmapFactory.decodeResource(context.resources, R.drawable.profile)
when(userViewModel.apiStatus) {
ApiStatus.LOADING -> Loading()
ApiStatus.DONE ->
Column(
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
){
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 30.dp),
horizontalArrangement = Arrangement.Center,
){
Text(
text = "PetMed",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White, fontSize = TextUnit(16.0f, TextUnitType.Em))
)
}
Column (
){
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Name", onValueChanged = { userViewModel.name.value = it })
}
Row (modifier = Modifier.padding(vertical = 5.dp)){
MyTextField(label = "Surname", onValueChanged = { userViewModel.surname.value = it })
}
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 = "Password", visualTransformation = PasswordVisualTransformation(), onValueChanged = {
userViewModel.password.value = it
isPasswordValid = it.isNotEmpty()
})
}
}
Button(
onClick = {
if (userViewModel.email.value == ""
|| userViewModel.name.value == ""
|| userViewModel.surname.value == ""
|| !isEmailValid
|| !isPasswordValid)
Toast.makeText(context, "Input correct value!", Toast.LENGTH_SHORT).show()
else {
userViewModel.createUser(photo)
navController.navigate(NavItem.Login.route)
}
},
modifier = Modifier
.height(60.dp)
.padding(top = 10.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)
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
){
Text(
text = "Already have a account? ",
style = MaterialTheme.typography.bodyMedium
.copy(TextSecondary)
)
Text(
text = "Login",
style = MaterialTheme.typography.bodyMedium
.copy(GreenBtn),
modifier = Modifier.clickable { navController.navigate(NavItem.Login.route) }
)
}
}
else -> {}
}
}

View File

@ -0,0 +1,98 @@
package com.example.myapplication.composeui.Profile
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed
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.graphics.Color
import androidx.compose.ui.platform.LocalContext
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 com.example.myapplication.api.ApiStatus
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.ReportViewModel
import com.example.myapplication.composeui.NetworkUI.Loading
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.TextPrimary
@Composable
fun Report(reportViewModel: ReportViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val context = LocalContext.current
if (reportViewModel.apiStatus == ApiStatus.ERROR){
Toast.makeText(context, "Error: " + reportViewModel.apiError, Toast.LENGTH_SHORT).show()
return
}
if(reportViewModel.countOrder.value == 0){
ReportDatePick()
}
else{
when(reportViewModel.apiStatus){
ApiStatus.LOADING -> Loading()
ApiStatus.DONE ->
Column (
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
){
Column(modifier = Modifier
.background(Color.White, RoundedCornerShape(15.dp)).padding(10.dp)
){
Text(
text = "Number of orders: ${reportViewModel.countOrder.value}",
style = MaterialTheme.typography.bodyMedium.copy(
TextPrimary, fontSize = TextUnit(4.0f, TextUnitType.Em)),
color = TextPrimary
)
Text(
text = "Total earn: ${reportViewModel.totalEarn.value}",
style = MaterialTheme.typography.bodyMedium.copy(
TextPrimary, fontSize = TextUnit(4.0f, TextUnitType.Em)),
color = TextPrimary
)
Text(
text = "Average check cost: ${reportViewModel.avgSum.value}",
style = MaterialTheme.typography.bodyMedium.copy(
TextPrimary, fontSize = TextUnit(4.0f, TextUnitType.Em)),
color = TextPrimary
)
}
Spacer(modifier = Modifier.size(15.dp))
Box(modifier = Modifier
.background(Color.White, RoundedCornerShape(15.dp))
.padding(10.dp)
){
Text(
text = "Top 10 popular services:",
style = MaterialTheme.typography.bodyMedium.copy(
TextPrimary, fontSize = TextUnit(4.0f, TextUnitType.Em)),
color = TextPrimary
)
}
LazyRow {
itemsIndexed(reportViewModel.serviceList.value ?: emptyList()) { index, service ->
ServiceReportCard(service)
}
}
}
else -> {}
}
}
}

View File

@ -0,0 +1,102 @@
package com.example.myapplication.composeui.Profile
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.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.platform.LocalContext
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 com.example.myapplication.api.ApiStatus
import com.example.myapplication.businessLogic.viewmodel.AppViewModelProvider
import com.example.myapplication.businessLogic.viewmodel.ReportViewModel
import com.example.myapplication.composeui.NetworkUI.Loading
import com.example.myapplication.composeui.UIComponents.DatePicker
import com.example.myapplication.ui.theme.BlueMain
import com.example.myapplication.ui.theme.RedBtn
@Composable
fun ReportDatePick(reportViewModel: ReportViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
val context = LocalContext.current
if (reportViewModel.apiStatus == ApiStatus.ERROR){
Toast.makeText(context, "Error: " + reportViewModel.apiError, Toast.LENGTH_SHORT).show()
return
}
when(reportViewModel.apiStatus){
ApiStatus.LOADING -> Loading()
ApiStatus.DONE ->
Column (
modifier = Modifier
.fillMaxSize()
.background(BlueMain)
.padding(15.dp)
.padding(bottom = 60.dp),
horizontalAlignment = Alignment.CenterHorizontally,
){
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
){
Text(
text = "PetMed",
style = MaterialTheme.typography.bodyMedium
.copy(Color.White, fontSize = TextUnit(8.0f, TextUnitType.Em))
)
}
DatePicker(
selectedDate = reportViewModel.dateFrom,
onDateSelected = { date ->
reportViewModel.dateFrom.value = date
}
)
Spacer(modifier = Modifier.height(16.dp))
DatePicker(
selectedDate = reportViewModel.dateTo,
onDateSelected = { date ->
reportViewModel.dateTo.value = date
},
)
Button(
onClick = {
if(reportViewModel.dateFrom.value <= reportViewModel.dateTo.value){
reportViewModel.updateReportData(reportViewModel.dateFrom.value, reportViewModel.dateTo.value)
}else{
Toast.makeText(context, "Incorrect date!", Toast.LENGTH_SHORT).show()
}
},
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
.clip(CircleShape)
.padding(vertical = 5.dp),
colors = ButtonDefaults.buttonColors(
containerColor = RedBtn,
contentColor = Color.White
),
) {
Text("Get reports")
}
}
else -> {}
}
}

View File

@ -0,0 +1,78 @@
package com.example.myapplication.composeui.Profile
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.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.widthIn
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.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.dp
import com.example.myapplication.api.model.RemoteConverters
import com.example.myapplication.api.model.ServiceWithCount
import com.example.myapplication.ui.theme.TextPrimary
@Composable
fun ServiceReportCard(item: ServiceWithCount){
val maxWidth = (LocalConfiguration.current.screenWidthDp/2)
Box(
modifier = Modifier
.padding(4.dp)
.widthIn(max = maxWidth.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(145.dp)
.background(color = Color.White, RoundedCornerShape(15.dp))
.padding(15.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
){
Image(
bitmap = RemoteConverters.toBitmap(item.service.photo).asImageBitmap(),
contentDescription = null,
modifier = Modifier
.fillMaxHeight()
.heightIn(min = 100.dp)
.padding(5.dp)
.widthIn(max = (maxWidth / 2).dp),
contentScale = ContentScale.FillHeight,
)
Column(
modifier = Modifier
.fillMaxHeight()
.widthIn(max = (maxWidth / 2).dp),
verticalArrangement = Arrangement.Top,
){
Text(text = item.service.name,
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium)
Text(text = item.service.price.toString(),
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium)
Text(
text = "x${item.quantity}",
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}

View File

@ -0,0 +1,87 @@
package com.example.myapplication.composeui.UIComponents
import android.app.DatePickerDialog
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.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.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.example.myapplication.ui.theme.GreenBtn
import com.example.myapplication.ui.theme.TextPrimary
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
@Composable
fun DatePicker(
selectedDate: MutableState<Long>,
onDateSelected: (Long) -> Unit,
value: Long? = null
) {
val context = LocalContext.current
val calendar = Calendar.getInstance()
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH)
val day = calendar.get(Calendar.DAY_OF_MONTH)
val date = Date()
val datePickerDialog = remember { mutableStateOf(DatePickerDialog(context)) }
val dateFormatter = remember { SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()) }
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Selected Date: ${dateFormatter.format(selectedDate.value)}",
color = TextPrimary,
style = MaterialTheme.typography.bodyMedium)
Spacer(modifier = Modifier.size(16.dp))
Button(
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
.clip(CircleShape)
.padding(vertical = 5.dp),
colors = ButtonDefaults.buttonColors(
containerColor = GreenBtn,
contentColor = Color.White
),
onClick = {
datePickerDialog.value = DatePickerDialog(
context,
{ _, year: Int, month: Int, dayOfMonth: Int ->
val selectedDateInMillis = Calendar.getInstance().apply {
set(year, month, dayOfMonth)
}.timeInMillis
selectedDate.value = selectedDateInMillis
onDateSelected(selectedDateInMillis)
},
year,
month,
day
)
datePickerDialog.value.show()
}
) {
Text(text = "Open Date Picker")
}
}
}

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
@ -21,6 +21,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import com.example.myapplication.ui.theme.BlueBorder
import com.example.myapplication.ui.theme.TextSecondary
@ -28,7 +29,8 @@ import com.example.myapplication.ui.theme.TextSecondary
@Composable
fun MyTextField (
label: String,
onValueChanged: (String) -> Unit
visualTransformation: VisualTransformation = VisualTransformation.None,
onValueChanged: (String) -> Unit,
){
val textState = remember { mutableStateOf(TextFieldValue()) }
val text by rememberUpdatedState(newValue = textState.value)
@ -46,14 +48,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),
)
}
BasicTextField(
value = text,
onValueChange = { newValue ->
textState.value = newValue
onValueChange = {
textState.value = it
onValueChanged(it.text)
},
visualTransformation = visualTransformation,
modifier = Modifier.fillMaxWidth(),
textStyle = MaterialTheme.typography.bodyMedium,
singleLine = true,

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,72 @@
package com.example.myapplication.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import androidx.sqlite.db.SupportSQLiteDatabase
import com.example.myapplication.database.dao.BasketDao
import com.example.myapplication.database.dao.OrderDao
import com.example.myapplication.database.dao.RemoteKeysDao
import com.example.myapplication.database.dao.ServiceDao
import com.example.myapplication.database.dao.UserDao
import com.example.myapplication.model.Basket
import com.example.myapplication.model.BasketService
import com.example.myapplication.model.Converters
import com.example.myapplication.model.Order
import com.example.myapplication.model.OrderService
import com.example.myapplication.model.RemoteKeys
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,
RemoteKeys::class], version = 14)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase(){
abstract fun serviceDao(): ServiceDao
abstract fun userDao(): UserDao
abstract fun orderDao(): OrderDao
abstract fun basketDao(): BasketDao
abstract fun remoteKeysDao(): RemoteKeysDao
companion object {
private const val DB_NAME: String = "my-db"
@Volatile
private var INSTANCE: AppDatabase? = null
suspend fun populateDatabase() {
}
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,42 @@
package com.example.myapplication.database.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.database.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,18 @@
package com.example.myapplication.database.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.example.myapplication.model.RemoteKeyType
import com.example.myapplication.model.RemoteKeys
@Dao
interface RemoteKeysDao {
@Query("SELECT * FROM remote_keys WHERE entityId = :entityId AND type = :type")
suspend fun getRemoteKeys(entityId: Int, type: RemoteKeyType): RemoteKeys?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(remoteKey: List<RemoteKeys>)
@Query("DELETE FROM remote_keys WHERE type = :type")
suspend fun clearRemoteKeys(type: RemoteKeyType)
}

View File

@ -0,0 +1,31 @@
package com.example.myapplication.database.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
@Dao
interface ServiceDao {
@Insert
suspend fun insert(vararg service: Service)
@Update
suspend fun update(service: Service)
@Delete
suspend fun delete(service: Service)
@Query("SELECT*FROM tbl_service")
fun getAllServicesPaged(): PagingSource<Int, Service>
@Query("select * from tbl_service")
fun getAll(): PagingSource<Int, Service>
@Query("SELECT * FROM tbl_service WHERE serviceId = :id")
suspend fun getServiceById(id: Int): Service
@Query("DELETE FROM tbl_service")
suspend fun deleteAll()
@Query("DELETE FROM tbl_service WHERE serviceId = :id")
suspend fun invalidateService(id: Int)
@Query("SELECT * FROM tbl_service WHERE LOWER(name) LIKE '%' || LOWER(:searchString) || '%' ")
fun findServices(searchString: String): PagingSource<Int, Service>
}

View File

@ -0,0 +1,26 @@
package com.example.myapplication.database.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
@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
}

View File

@ -0,0 +1,41 @@
package com.example.myapplication.database.repository
import com.example.myapplication.businessLogic.repository.BasketRepository
import com.example.myapplication.database.dao.BasketDao
import com.example.myapplication.model.Basket
import com.example.myapplication.model.BasketService
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
class BasketRepositoryImpl(private val basketDao: BasketDao): BasketRepository {
suspend fun createBasket(basket: Basket): Long = basketDao.insert(basket)
suspend fun getUsersBasket(userId: Int): Basket = basketDao.getUsersBasket(userId)
override 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)
override suspend fun incrementServiceQuantity(basketId: Int, serviceId: Int) = basketDao.incrementServiceQuantity(basketId, serviceId)
override suspend fun decrementServiceQuantity(basketId: Int, serviceId: Int) = basketDao.decrementServiceQuantity(basketId, serviceId)
override suspend fun insertBasketService(basketService: BasketService) = basketDao.insertBasketService(basketService)
override suspend fun getBasketWithServices(id: Int): Flow<List<Service>> = flow {basketDao.getBasketWithServices(id).first().services}
override suspend fun getUserBasketId(id: Int): Flow<Int> {
TODO("Not yet implemented")
}
suspend fun delete(basket: Basket) = basketDao.delete(basket)
override suspend fun getQuantity(basketId: Int, serviceId: Int): Int? = basketDao.getQuantity(basketId, serviceId)
override suspend fun existService(basketId: Int, serviceId: Int): Boolean {
TODO("Not yet implemented")
}
override suspend fun getTotalPriceForUser(userId: Int): Double? {
TODO("Not yet implemented")
}
override suspend fun deleteAllServiceFromBasket(basketId: Int) {
TODO("Not yet implemented")
}
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,39 @@
package com.example.myapplication.database.repository
import com.example.myapplication.businessLogic.repository.OrderRepository
import com.example.myapplication.database.dao.OrderDao
import com.example.myapplication.model.Order
import com.example.myapplication.model.OrderService
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
class OrderRepositoryImpl(private val orderDao: OrderDao): OrderRepository {
// override suspend fun insert(order: Order) = orderDao.insert(order)
// override suspend fun addServiceToOrder(orderService: OrderService) = orderDao.insertOrderService(orderService)
// override suspend fun delete(order: Order) = orderDao.delete(order)
// override suspend fun getServiceFromOrder(id: Int): Flow<List<Service>> {
// TODO("Not yet implemented")
// }
// suspend fun getOrderWithServices(id: Int) = orderDao.getOrderWithServices(id)
// suspend fun getAllOrders() = orderDao.getAllOrders()
// override suspend fun getUserOrders(id: Int): Flow<List<Order>> = flow { orderDao.getUserOrders(id).first().orders }
override suspend fun insert(order: Order) {
TODO("Not yet implemented")
}
override suspend fun addServiceToOrder(orderService: OrderService) {
TODO("Not yet implemented")
}
override suspend fun delete(order: Order) {
TODO("Not yet implemented")
}
override suspend fun getServiceFromOrder(id: Int): Flow<List<Service>> {
TODO("Not yet implemented")
}
override suspend fun getUserOrders(id: Int): Flow<List<Order>> {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,16 @@
package com.example.myapplication.database.repository
import com.example.myapplication.database.dao.RemoteKeysDao
import com.example.myapplication.model.RemoteKeyType
import com.example.myapplication.model.RemoteKeys
class RemoteKeysRepositoryImpl(private val remoteKeysDao: RemoteKeysDao) {
suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType) =
remoteKeysDao.getRemoteKeys(id, type)
suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>) =
remoteKeysDao.insertAll(remoteKeys)
suspend fun deleteRemoteKey(type: RemoteKeyType) =
remoteKeysDao.clearRemoteKeys(type)
}

View File

@ -0,0 +1,41 @@
package com.example.myapplication.database.repository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import com.example.myapplication.businessLogic.repository.ServiceRepository
import com.example.myapplication.database.dao.ServiceDao
import com.example.myapplication.di.AppContainer
import com.example.myapplication.model.Service
import kotlinx.coroutines.flow.Flow
class ServiceRepositoryImpl(private val serviceDao: ServiceDao): ServiceRepository {
override suspend fun insert(service: Service) = serviceDao.insert(service)
override suspend fun update(service: Service) = serviceDao.update(service)
override suspend fun delete(service: Service) = serviceDao.delete(service)
override suspend fun getServiceById(id: Int): Service = serviceDao.getServiceById(id)
override suspend fun getAllServices(): Flow<PagingData<Service>> = Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
pagingSourceFactory = serviceDao::getAll
).flow
suspend fun clearServices() = serviceDao.deleteAll()
suspend fun insertServices(services: List<Service>) =
serviceDao.insert(*services.toTypedArray())
fun getAllServicesPagingSource(): PagingSource<Int, Service> = serviceDao.getAll()
suspend fun invalidateService(id: Int) = serviceDao.invalidateService(id)
override fun call(str: String): Flow<PagingData<Service>> {
return Pager(
PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
) {
serviceDao.findServices(str)
}.flow
}
}

View File

@ -0,0 +1,17 @@
package com.example.myapplication.database.repository
import com.example.myapplication.api.model.UserRemoteSignIn
import com.example.myapplication.businessLogic.repository.UserRepository
import com.example.myapplication.database.dao.UserDao
import com.example.myapplication.model.User
class UserRepositoryImpl(private val userDao: UserDao): UserRepository {
override suspend fun insert(user: User) = userDao.insert(user)
override suspend fun update(user: User) = userDao.update(user)
override suspend fun delete(user: User) = userDao.delete(user)
override suspend fun authUser(user: UserRemoteSignIn): User {
TODO("Not yet implemented")
}
suspend fun getUserById(id: Int) = userDao.getUserById(id)
suspend fun getUserByEmail(email: String) = userDao.getUserByEmail(email)
}

View File

@ -0,0 +1,20 @@
package com.example.myapplication.di
import com.example.myapplication.businessLogic.repository.BasketRepository
import com.example.myapplication.businessLogic.repository.OrderRepository
import com.example.myapplication.businessLogic.repository.ReportRepository
import com.example.myapplication.businessLogic.repository.ServiceRepository
import com.example.myapplication.businessLogic.repository.UserRepository
interface AppContainer {
val serviceRepo: ServiceRepository
val userRepo: UserRepository
val orderRepo: OrderRepository
val basketRepo: BasketRepository
val reportRepo: ReportRepository
companion object {
const val TIMEOUT = 5000L
const val LIMIT = 10
}
}

View File

@ -0,0 +1,47 @@
package com.example.myapplication.di
import android.content.Context
import com.example.myapplication.api.ServerService
import com.example.myapplication.api.repository.RestBasketRepository
import com.example.myapplication.api.repository.RestOrderRepository
import com.example.myapplication.api.repository.RestReportRepository
import com.example.myapplication.api.repository.RestServiceRepository
import com.example.myapplication.api.repository.RestUserRepository
import com.example.myapplication.businessLogic.repository.BasketRepository
import com.example.myapplication.businessLogic.repository.OrderRepository
import com.example.myapplication.businessLogic.repository.ReportRepository
import com.example.myapplication.businessLogic.repository.ServiceRepository
import com.example.myapplication.businessLogic.repository.UserRepository
import com.example.myapplication.database.AppDatabase
import com.example.myapplication.database.repository.RemoteKeysRepositoryImpl
import com.example.myapplication.database.repository.ServiceRepositoryImpl
class AppDataContainer(context: Context) : AppContainer {
override val serviceRepo: ServiceRepository by lazy {
RestServiceRepository(
ServerService.getInstance(),
serviceRepository,
AppDatabase.getInstance(context),
remoteKeyRepository
)
}
override val userRepo: UserRepository by lazy{
RestUserRepository(ServerService.getInstance())
}
override val orderRepo: OrderRepository by lazy {
RestOrderRepository(ServerService.getInstance())
}
override val basketRepo: BasketRepository by lazy{
RestBasketRepository(ServerService.getInstance())
}
override val reportRepo: ReportRepository by lazy{
RestReportRepository(ServerService.getInstance())
}
private val serviceRepository: ServiceRepositoryImpl by lazy {
ServiceRepositoryImpl(AppDatabase.getInstance(context).serviceDao())
}
private val remoteKeyRepository: RemoteKeysRepositoryImpl by lazy{
RemoteKeysRepositoryImpl(AppDatabase.getInstance(context).remoteKeysDao())
}
}

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,20 @@
package com.example.myapplication.model
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.room.TypeConverter
import java.io.ByteArrayOutputStream
class Converters {
@TypeConverter
fun fromBitmap(bitmap: Bitmap) : ByteArray {
val outputStream = ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 1, outputStream)
return outputStream.toByteArray()
}
@TypeConverter
fun toBitmap(byteArray: ByteArray): Bitmap {
return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
}
}

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,22 @@
package com.example.myapplication.model
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverter
import androidx.room.TypeConverters
enum class RemoteKeyType(private val type: String) {
SERVICE(Service::class.simpleName ?: "Service");
@TypeConverter
fun toRemoteKeyType(value: String) = RemoteKeyType.values().first { it.type == value }
@TypeConverter
fun fromRemoteKeyType(value: RemoteKeyType) = value.type
}
@Entity(tableName = "remote_keys")
data class RemoteKeys(
@PrimaryKey val entityId: Int,
@TypeConverters(RemoteKeyType::class)
val type: RemoteKeyType,
val prevKey: Int?,
val nextKey: Int?
)

View File

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

View File

@ -0,0 +1,14 @@
package com.example.myapplication.model
import android.graphics.Bitmap
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: Bitmap
)

View File

@ -0,0 +1,18 @@
package com.example.myapplication.model
import android.graphics.Bitmap
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,
var photo: Bitmap,
val basketId: 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>
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -2,4 +2,6 @@
<string name="app_name">PetMed</string>
<string name="not_auth_error">Please login</string>
<string name="placeholder_search">Search…</string>
<string name="loading">Loading...</string>
<string name="back">Return</string>
</resources>